-
-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add dry-operation generators #171
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
d3e5e78
Add dry-operation to default Gemfile
cllns 888c388
Add base Operation class, based on dry-operation
cllns 24d3d6b
Fix view spec
cllns bc2670a
Add Operation generators
cllns 1c37df3
Add empty `call` method definition
cllns a3f6a03
Remove ostruct
cllns d93dfc8
Merge branch 'main' into add-dry-operation
cllns 649bdcb
Allow slash separator for generator
cllns f74519f
Allow slash separator for generator
cllns 0f9f814
Rename module to admin
cllns 663abc6
Remove newlines in generated files
cllns 3b72feb
Remove input as default args
cllns 0f81a5c
Remove Operations namespace, generate in app/ or slices/SLICE_NAME/
cllns a5bd2f3
Prevent generating operation without namespace
cllns eb391ca
Revert "Prevent generating operation without namespace"
cllns 1023225
Add recommendation to add namespace to operations
cllns 6a3c32a
Change examples
cllns 8dc3de4
Switch to outputting directly, remove Files#recommend
cllns 8f90b33
x.x.x => 2.2.0
cllns 8e62aa3
Include Dry::Monads[:result] in base Action
cllns c2c54c9
Add explanatory comment, and include monads result on Slice action
cllns 9933c8a
Register command so it's available
cllns 9fc5e31
Require dry-monads
cllns ad0431d
Add require for slice action
cllns 0095497
Change note to past tense
cllns 29b02b9
Merge remote-tracking branch 'origin/main' into add-dry-operation
cllns e60248d
Remove inlude Dry::Monads in slice Action
cllns File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# frozen_string_literal: true | ||
|
||
require "dry/inflector" | ||
require "dry/files" | ||
require "shellwords" | ||
require_relative "../../../naming" | ||
require_relative "../../../errors" | ||
|
||
module Hanami | ||
module CLI | ||
module Commands | ||
module App | ||
module Generate | ||
# @since 2.2.0 | ||
# @api private | ||
class Operation < App::Command | ||
argument :name, required: true, desc: "Operation name" | ||
option :slice, required: false, desc: "Slice name" | ||
|
||
example [ | ||
%(books.add (MyApp::Books::Add)), | ||
%(books.add --slice=admin (Admin::Books::Add)), | ||
] | ||
attr_reader :generator | ||
private :generator | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def initialize( | ||
fs:, inflector:, | ||
generator: Generators::App::Operation.new(fs: fs, inflector: inflector), | ||
**opts | ||
) | ||
super(fs: fs, inflector: inflector, **opts) | ||
@generator = generator | ||
end | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def call(name:, slice: nil, **) | ||
slice = inflector.underscore(Shellwords.shellescape(slice)) if slice | ||
|
||
generator.call(app.namespace, name, slice) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# frozen_string_literal: true | ||
|
||
require "erb" | ||
require "dry/files" | ||
require_relative "../../errors" | ||
|
||
module Hanami | ||
module CLI | ||
module Generators | ||
module App | ||
# @since 2.2.0 | ||
# @api private | ||
class Operation | ||
# @since 2.2.0 | ||
# @api private | ||
def initialize(fs:, inflector:, out: $stdout) | ||
@fs = fs | ||
@inflector = inflector | ||
@out = out | ||
end | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def call(app, key, slice) | ||
context = OperationContext.new(inflector, app, slice, key) | ||
|
||
if slice | ||
generate_for_slice(context, slice) | ||
else | ||
generate_for_app(context) | ||
end | ||
end | ||
|
||
private | ||
|
||
attr_reader :fs, :inflector, :out | ||
|
||
def generate_for_slice(context, slice) | ||
slice_directory = fs.join("slices", slice) | ||
raise MissingSliceError.new(slice) unless fs.directory?(slice_directory) | ||
|
||
if context.namespaces.any? | ||
fs.mkdir(directory = fs.join(slice_directory, context.namespaces)) | ||
fs.write(fs.join(directory, "#{context.name}.rb"), t("nested_slice_operation.erb", context)) | ||
else | ||
fs.mkdir(directory = fs.join(slice_directory)) | ||
fs.write(fs.join(directory, "#{context.name}.rb"), t("top_level_slice_operation.erb", context)) | ||
out.puts(" Note: We generated a top-level operation. To generate into a directory, add a namespace: `my_namespace.#{context.name}`") | ||
end | ||
end | ||
|
||
def generate_for_app(context) | ||
if context.namespaces.any? | ||
fs.mkdir(directory = fs.join("app", context.namespaces)) | ||
fs.write(fs.join(directory, "#{context.name}.rb"), t("nested_app_operation.erb", context)) | ||
else | ||
fs.mkdir(directory = fs.join("app")) | ||
out.puts(" Note: We generated a top-level operation. To generate into a directory, add a namespace: `my_namespace.#{context.name}`") | ||
fs.write(fs.join(directory, "#{context.name}.rb"), t("top_level_app_operation.erb", context)) | ||
end | ||
end | ||
|
||
def template(path, context) | ||
require "erb" | ||
|
||
ERB.new( | ||
File.read(__dir__ + "/operation/#{path}") | ||
).result(context.ctx) | ||
end | ||
|
||
alias_method :t, :template | ||
end | ||
end | ||
end | ||
end | ||
end |
10 changes: 10 additions & 0 deletions
10
lib/hanami/cli/generators/app/operation/nested_app_operation.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# frozen_string_literal: true | ||
|
||
module <%= camelized_app_name %> | ||
<%= module_namespace_declaration %> | ||
<%= module_namespace_offset %>class <%= camelized_name %> < <%= camelized_app_name %>::Operation | ||
<%= module_namespace_offset %> def call | ||
<%= module_namespace_offset %> end | ||
<%= module_namespace_offset %>end | ||
<%= module_namespace_end %> | ||
end |
10 changes: 10 additions & 0 deletions
10
lib/hanami/cli/generators/app/operation/nested_slice_operation.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# frozen_string_literal: true | ||
|
||
module <%= camelized_slice_name %> | ||
<%= module_namespace_declaration %> | ||
<%= module_namespace_offset %>class <%= camelized_name %> < <%= camelized_slice_name %>::Operation | ||
<%= module_namespace_offset %> def call | ||
<%= module_namespace_offset %> end | ||
<%= module_namespace_offset %>end | ||
<%= module_namespace_end %> | ||
end |
8 changes: 8 additions & 0 deletions
8
lib/hanami/cli/generators/app/operation/top_level_app_operation.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# frozen_string_literal: true | ||
|
||
module <%= camelized_app_name %> | ||
<%= module_namespace_offset %>class <%= camelized_name %> < <%= camelized_app_name %>::Operation | ||
<%= module_namespace_offset %> def call | ||
<%= module_namespace_offset %> end | ||
<%= module_namespace_offset %>end | ||
end |
8 changes: 8 additions & 0 deletions
8
lib/hanami/cli/generators/app/operation/top_level_slice_operation.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# frozen_string_literal: true | ||
|
||
module <%= camelized_slice_name %> | ||
<%= module_namespace_offset %>class <%= camelized_name %> < <%= camelized_slice_name %>::Operation | ||
<%= module_namespace_offset %> def call | ||
<%= module_namespace_offset %> end | ||
<%= module_namespace_offset %>end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative "slice_context" | ||
require "dry/files/path" | ||
|
||
module Hanami | ||
module CLI | ||
module Generators | ||
# @since 2.2.0 | ||
# @api private | ||
module App | ||
# @since 2.2.0 | ||
# @api private | ||
class OperationContext < SliceContext | ||
# TODO: move these constants somewhere that will let us reuse them | ||
KEY_SEPARATOR = %r{\.|/} | ||
private_constant :KEY_SEPARATOR | ||
|
||
NAMESPACE_SEPARATOR = "::" | ||
private_constant :NAMESPACE_SEPARATOR | ||
|
||
INDENTATION = " " | ||
private_constant :INDENTATION | ||
|
||
OFFSET = INDENTATION | ||
private_constant :OFFSET | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
attr_reader :key | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def initialize(inflector, app, slice, key) | ||
@key = key | ||
super(inflector, app, slice, nil) | ||
end | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def namespaces | ||
@namespaces ||= key.split(KEY_SEPARATOR)[..-2] | ||
end | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def name | ||
@name ||= key.split(KEY_SEPARATOR)[-1] | ||
end | ||
|
||
# @api private | ||
# @since 2.2.0 | ||
# @api private | ||
def camelized_name | ||
inflector.camelize(name) | ||
end | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def module_namespace_declaration | ||
namespaces.each_with_index.map { |token, i| | ||
"#{OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}" | ||
}.join($/) | ||
end | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def module_namespace_end | ||
namespaces.each_with_index.map { |_, i| | ||
"#{OFFSET}#{INDENTATION * i}end" | ||
}.reverse.join($/) | ||
end | ||
|
||
# @since 2.2.0 | ||
# @api private | ||
def module_namespace_offset | ||
"#{OFFSET}#{INDENTATION * namespaces.count}" | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# auto_register: false | ||
# frozen_string_literal: true | ||
|
||
module <%= camelized_slice_name %> | ||
class Operation < <%= camelized_app_name %>::Operation | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# auto_register: false | ||
# frozen_string_literal: true | ||
|
||
require "dry/operation" | ||
|
||
module <%= camelized_app_name %> | ||
class Operation < Dry::Operation | ||
end | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that you've included this!
I suggest we include a little explainer to make it clear why this line is here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency, can you please update the slice generator to include this in slice actions too?
The difference should be that the slice generator should check to see if dry-monads is bundled before it includes this line. If the user chooses to remove dry-operation from their
Gemfile
, then dry-monads will disappear and theinclude
would then result in an error.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did this for the slice, but the slice actions inherit from app/action.rb so it doesn't do anything. I kept it in since I might be missing something, but I think we can remove it?
I also had to add a line to require dry/monads
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cllns Ah yeah, sorry, that's my mistake. With slice actions inheriting from an app action, we don't need to put anything additional into the slice actions.