-
-
Notifications
You must be signed in to change notification settings - Fork 28
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 Hanami::CLI::RubyFileGenerator, convert Operation to use it instead of ERB #186
Conversation
By adding new templates for un-nested operations
This reverts commit a5bd2f3.
Add dry-monads include for slice base action
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.
Brilliant, @cllns! This is going to be a huge win for our generators. 😍😍😍
I've left a couple of thoughts for you. The only one worth doing before merging would be rearranging a couple methods, with the others being ideas for future refactors.
def initialize( | ||
fs:, | ||
inflector:, | ||
generator_class: nil, |
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 feel like generator_class
is better suited to being a class attribute at this point.
Now that we're asking for a class for the generator, and taking care of initializing it with our standard options (fs, inflector, out), the generator itself no longer needs to be injectable for the purposes of testing, and beyond that there's really no need to be able to switch the generator at all.
This way, the subclasses of CLI::Commands::App::Generate
can just specify their generator class inter class body, and not even have to worry about overriding #initialize
.
Note: I think this would be fine to experiment with in a follow-up PR rather than blocking this one any further :)
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.
Already addressed in #199, I forgot to backport it here :)
argument :name, required: true, desc: "Operation name" | ||
option :slice, required: false, desc: "Slice name" | ||
|
||
class Operation < Generate::Command |
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 love how tidy this class has become!
base = if slice | ||
fs.join("slices", slice) | ||
else | ||
fs.join("app") | ||
end |
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.
If we can pass an actual slice instance into this, we could use our new Slice#source_path
to make this simpler:
base = slice.source_path
This returns the appropriate value for both the app as well as slices, so it means we no longer need if app else slice
conditionals everywhere, which has been a bother for the longest time :)
(Again, this would be another great refactor for post-merge, I don't want to hold anything up!)
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.
Extracted to #202
def self.class(class_name, **args) | ||
new(class_name: class_name, **args).to_s | ||
end | ||
|
||
def self.module(*names, **args) | ||
module_names = if names.first.is_a?(Array) | ||
names.first | ||
else | ||
names | ||
end | ||
|
||
new(modules: module_names, class_name: nil, parent_class: nil, **args).to_s | ||
end |
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.
Minor nit, could these class methods go above #initialize
? Having class methods up top is roughly how we've been arranging our code in general.
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.
Done
def lines(remaining_modules) | ||
this_module, *rest_modules = remaining_modules | ||
if this_module | ||
with_module_lines(this_module, lines(rest_modules)) |
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.
Recursion! Drink!
(Very clever solution, nice work 😄)
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.
Hahah, I think that I knew this could be done recursively to arbitrary depth was one of the reasons I was excited to write this file.
|
||
module Hanami | ||
module CLI | ||
class RubyFileGenerator |
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.
Love it. This class is easy to follow and is so powerful in how much it will simplify the rest of the generators.
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.
Yea, I'm surprised I wasn't able to find any prior work for creating a Ruby file in Ruby. It was pretty fun to write though, so I don't mind :D
This reverts commit 303f502. Would need to namespace it and we may want to this to standalone so keeping it here. It's just two little spaces anyway
* Add struct generator * Extract App::Generate::Command * Specify full name, to use App::Command * Use App::Generate::Command for Struct * Use KEY_SEPARATOR from constants file * Provide generator class to command * Extract Helper, use for Struct generator * Fix specs with Helper * kwargs * Rename helper to RubyFileWriter * Fix examples * Remove optional kwarg * Reorder args * Rename to relative_parent_class * Remove duplicate implementation * Add api doc comments * Reorder methods * Refactor initialize * Refactor to use method instead of arg * Refactor to move logic into generator * Reorder assignments * Add Hanami::CLI::RubyFileGenerator, convert Operation to use it instead of ERB (#186) * Add dry-operation to default Gemfile * Add base Operation class, based on dry-operation * Fix view spec * Add Operation generators * Add empty `call` method definition * Remove ostruct * Allow slash separator for generator * Allow slash separator for generator * Rename module to admin * Remove newlines in generated files By adding new templates for un-nested operations * Remove input as default args * Remove Operations namespace, generate in app/ or slices/SLICE_NAME/ * Prevent generating operation without namespace * Revert "Prevent generating operation without namespace" This reverts commit a5bd2f3. * Add recommendation to add namespace to operations * Change examples * Switch to outputting directly, remove Files#recommend * Add Hanami::CLI::RubyFileGenerator * x.x.x => 2.2.0 * x.x.x => 2.2.0 * Include Dry::Monads[:result] in base Action * Add .module tests * Convert top-level app operation to use RubyFileGenerator * Convert nested app operation to use RubyFileGenerator * Support slash separators * Convert top-level slice operation to use RubyFileGenerator * Remove OperationContext * Remove namespaces instance variable * Refactor to variables * Remove last temporary instance variable * Refactor * More refactoring, for clarity * Rename variable for clarity * Rename helper method * Simplify RubyFileGenerator, support older versions * Convert Operation generator to use simplified RubyFileGenerator * Remove un-used errors * Refactor * Older kwargs forwarding style * Refactor * Rename variable * Add explanatory comment Add dry-monads include for slice base action * Fix base slice action * Remove un-used ERB templates * Remove OperationContext * Ternary over and/or * Fix missing 'end' from bad merge * Fix namespace recommendation * Extract App::Generate::Command * Specify full name, to use App::Command * Use constants file * Move class methods above initialize * Use constants file * Add yard comments * Revert "Use constants file" This reverts commit 303f502. Would need to namespace it and we may want to this to standalone so keeping it here. It's just two little spaces anyway * Fix indent to be two spaces * Remove extraneous requires * Use out.string.chomp * Fix name of expectation
* Add dry-operation to default Gemfile * Add base Operation class, based on dry-operation * Fix view spec * Add Operation generators * Add empty `call` method definition * Remove ostruct * Allow slash separator for generator * Allow slash separator for generator * Rename module to admin * Remove newlines in generated files By adding new templates for un-nested operations * Remove input as default args * Remove Operations namespace, generate in app/ or slices/SLICE_NAME/ * Prevent generating operation without namespace * Revert "Prevent generating operation without namespace" This reverts commit a5bd2f3. * Add recommendation to add namespace to operations * Change examples * Switch to outputting directly, remove Files#recommend * Add Hanami::CLI::RubyFileGenerator * x.x.x => 2.2.0 * x.x.x => 2.2.0 * Include Dry::Monads[:result] in base Action * Add .module tests * Convert top-level app operation to use RubyFileGenerator * Convert nested app operation to use RubyFileGenerator * Support slash separators * Convert top-level slice operation to use RubyFileGenerator * Remove OperationContext * Remove namespaces instance variable * Refactor to variables * Remove last temporary instance variable * Refactor * More refactoring, for clarity * Rename variable for clarity * Rename helper method * Simplify RubyFileGenerator, support older versions * Convert Operation generator to use simplified RubyFileGenerator * Remove un-used errors * Refactor * Older kwargs forwarding style * Refactor * Rename variable * Add explanatory comment Add dry-monads include for slice base action * Fix base slice action * Remove un-used ERB templates * Remove OperationContext * Ternary over and/or * Fix missing 'end' from bad merge * Fix namespace recommendation * Extract App::Generate::Command * Specify full name, to use App::Command * Use constants file * Move class methods above initialize * Use constants file * Add yard comments * Revert "Use constants file" This reverts commit 303f502. Would need to namespace it and we may want to this to standalone so keeping it here. It's just two little spaces anyway * Fix indent to be two spaces * Generate struct (with RubyFileGenerator, add RubyFileWriter) (#199) * Add struct generator * Extract App::Generate::Command * Specify full name, to use App::Command * Use App::Generate::Command for Struct * Use KEY_SEPARATOR from constants file * Provide generator class to command * Extract Helper, use for Struct generator * Fix specs with Helper * kwargs * Rename helper to RubyFileWriter * Fix examples * Remove optional kwarg * Reorder args * Rename to relative_parent_class * Remove duplicate implementation * Add api doc comments * Reorder methods * Refactor initialize * Refactor to use method instead of arg * Refactor to move logic into generator * Reorder assignments * Add Hanami::CLI::RubyFileGenerator, convert Operation to use it instead of ERB (#186) * Add dry-operation to default Gemfile * Add base Operation class, based on dry-operation * Fix view spec * Add Operation generators * Add empty `call` method definition * Remove ostruct * Allow slash separator for generator * Allow slash separator for generator * Rename module to admin * Remove newlines in generated files By adding new templates for un-nested operations * Remove input as default args * Remove Operations namespace, generate in app/ or slices/SLICE_NAME/ * Prevent generating operation without namespace * Revert "Prevent generating operation without namespace" This reverts commit a5bd2f3. * Add recommendation to add namespace to operations * Change examples * Switch to outputting directly, remove Files#recommend * Add Hanami::CLI::RubyFileGenerator * x.x.x => 2.2.0 * x.x.x => 2.2.0 * Include Dry::Monads[:result] in base Action * Add .module tests * Convert top-level app operation to use RubyFileGenerator * Convert nested app operation to use RubyFileGenerator * Support slash separators * Convert top-level slice operation to use RubyFileGenerator * Remove OperationContext * Remove namespaces instance variable * Refactor to variables * Remove last temporary instance variable * Refactor * More refactoring, for clarity * Rename variable for clarity * Rename helper method * Simplify RubyFileGenerator, support older versions * Convert Operation generator to use simplified RubyFileGenerator * Remove un-used errors * Refactor * Older kwargs forwarding style * Refactor * Rename variable * Add explanatory comment Add dry-monads include for slice base action * Fix base slice action * Remove un-used ERB templates * Remove OperationContext * Ternary over and/or * Fix missing 'end' from bad merge * Fix namespace recommendation * Extract App::Generate::Command * Specify full name, to use App::Command * Use constants file * Move class methods above initialize * Use constants file * Add yard comments * Revert "Use constants file" This reverts commit 303f502. Would need to namespace it and we may want to this to standalone so keeping it here. It's just two little spaces anyway * Fix indent to be two spaces * Remove extraneous requires * Use out.string.chomp * Fix name of expectation
This is an experiment I worked on a couple weeks ago: to generate simple class/module definitions in Ruby, that nest properly in modules and without needing ERB templates. This is a stripped down version of that experiment, where we just pass-in
body
andheaders
instead of more specific args, based on excellent feedback from @timriley.In this PR, I just convert the Operation generator (very recently merged in #171) to use the RubyFileGenerator. I wanted to give an bounded example that's easy to grok, rather than a huge messy diff. Plus I want to get feedback on this before putting in the work to change all the others.
Advantages:
hanami generate operation --deps repos.book_repo
to automatically addinclude Deps["repos.book_repo"]
etc.A disadvantage is that this adds more code, for now. I'm confident we'll be able to make up for that if we convert all the other files to use this, though and have this be a net-negative lines of code change, since we can get rid of the ERB templates and the 'Context' classes
I know we're pushing to get 2.2 out the door soon and this is refactoring that doesn't move the needle forward on that, so I'm happy to wait on this until after 2.2 is released and we start work on 2.3 :)