padrino / padrino-framework

Padrino is a full-stack ruby framework built upon Sinatra.
http://www.padrinorb.com
MIT License
3.37k stars 508 forks source link

Flexible generators #1961

Open dnesteryuk opened 9 years ago

dnesteryuk commented 9 years ago

Hello,

I saw #1921, but after learning code of the generators, I understood that it would be difficult to implement with the current code base. I don't say it isn't possible, but it will require new portion of if/else in the code. Therefore, I worked on prototyping the new generators.

I don't create a pull request for this change, because it is only prototype. You can find it here.

It is 100% working example, you can checkout and play with it.

The idea is simple. Each generator receives options (example: {lean: false, tiny: true, gem: false}), each option means that we need add or remove some parts (example, the structure for applications). Instead of having monolithic code I suggest to divide it on parts:

part :standard, {tiny: false} do |app, *|
  app_dir app

  empty_directory destination_root(app, 'controllers')
  empty_directory destination_root(app, 'helpers')
  empty_directory destination_root(app, 'views')
  empty_directory destination_root(app, 'views', 'layouts')
end

with their conditions, if a part matches those conditions, it will be taken for generation. I suggest to have a skeleton class for each generator of Padrino.

module Padrino
  module Generators
    class ProjectSkeleton < BaseSkeleton
     part :standard, {tiny: false} do |app, *|
       # generate the structure
     end

     # other parts of the project
   end
  end
end

That skeleton will keep parts with their conditions.

Some parts may be without conditions:

part :project do |*|
  directory('project/', destination_root)
end

it will be taken regardless of the given options.

Also, I suppose dependencies between skeletons:

part :no_lean, {lean: false}, ['app'] do |app, project_name, app_name|

For instance, to generate no_lean part of the project, the application skeleton will be taken as well.

As you can see after integrating such approach, the project generator looks simpler.

Benefit

We receive flexibility allowing to extend functionality of generators:

part :public_dir, {api: false} do |*|
  empty_directory destination_root('public/images')
  empty_directory destination_root('public/javascripts')
  empty_directory destination_root('public/stylesheets')
end

part :gemfile, {api: false} do |*|
  template 'templates/Gemfile.tt', destination_root('Gemfile')
end

part :gemfile_api, {api: true} do |*|
  template 'templates/api_Gemfile.tt', destination_root('Gemfile')
end

Drawbacks

For now I am interested in your thoughts. I see 3 options:

ujifgc commented 9 years ago

Looks promising!

Though, I personally prefer modules over SimpleDelegator, and rocket syntax for hashes.