effective_developer 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -9
  3. data/app/models/effective/code_writer.rb +218 -0
  4. data/lib/effective_developer/version.rb +1 -1
  5. data/lib/generators/effective/ability_generator.rb +78 -0
  6. data/lib/generators/effective/controller_generator.rb +27 -9
  7. data/lib/generators/effective/datatable_generator.rb +20 -2
  8. data/lib/generators/effective/form_generator.rb +46 -0
  9. data/lib/generators/effective/helpers.rb +94 -11
  10. data/lib/generators/effective/menu_generator.rb +52 -0
  11. data/lib/generators/effective/migration_generator.rb +16 -2
  12. data/lib/generators/effective/model_generator.rb +23 -1
  13. data/lib/generators/effective/route_generator.rb +58 -2
  14. data/lib/generators/effective/scaffold_controller_generator.rb +51 -0
  15. data/lib/generators/effective/scaffold_generator.rb +25 -52
  16. data/lib/generators/effective/views_generator.rb +47 -0
  17. data/lib/scaffolds/controllers/controller.rb +142 -0
  18. data/lib/scaffolds/datatables/datatable.rb +18 -0
  19. data/lib/scaffolds/forms/_form.html.haml +9 -0
  20. data/lib/{generators/effective_developer/csv_importer.rb.erb → scaffolds/importers/csv_importer.rb} +0 -0
  21. data/lib/scaffolds/models/model.rb +48 -0
  22. data/lib/scaffolds/views/_resource.html.haml +7 -0
  23. data/lib/scaffolds/views/edit.html.haml +3 -0
  24. data/lib/scaffolds/views/index.html.haml +10 -0
  25. data/lib/scaffolds/views/new.html.haml +3 -0
  26. data/lib/scaffolds/views/show.html.haml +3 -0
  27. data/lib/tasks/effective_csv_importer.rake +1 -1
  28. metadata +18 -12
  29. data/app/scaffolds/controllers/controller.rb +0 -13
  30. data/app/scaffolds/datatables/datatable.rb +0 -14
  31. data/app/scaffolds/models/model.rb +0 -15
  32. data/app/scaffolds/views/_form.html.haml +0 -15
  33. data/app/scaffolds/views/edit.html.haml +0 -7
  34. data/app/scaffolds/views/index.html.haml +0 -25
  35. data/app/scaffolds/views/new.html.haml +0 -5
  36. data/app/scaffolds/views/show.html.haml +0 -11
  37. data/lib/generators/effective/view_generator.rb +0 -40
@@ -0,0 +1,46 @@
1
+ # rails generate effective:form NAME [field[:type] field[:type]] [options]
2
+
3
+ # TODO
4
+
5
+ # Generates a form
6
+ # rails generate effective:form Thing
7
+ # rails generate effective:form Thing name:string description:text
8
+
9
+ module Effective
10
+ module Generators
11
+ class FormGenerator < Rails::Generators::NamedBase
12
+ include Helpers
13
+
14
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
15
+
16
+ desc 'Creates a _form.html.haml in your app/views/model/ folder.'
17
+
18
+ argument :attributes, type: :array, default: [], banner: 'field[:type] field[:type]'
19
+
20
+ def assign_attributes
21
+ @attributes = (invoked_attributes.presence || klass_attributes).map do |attribute|
22
+ Rails::Generators::GeneratedAttribute.parse(attribute)
23
+ end
24
+ end
25
+
26
+ def invoke_form
27
+ say_status :invoke, :form, :white
28
+ end
29
+
30
+ def create_form
31
+ template 'forms/_form.html.haml', File.join('app/views', namespace_path, (namespace_path.present? ? '' : class_path), plural_name, '_form.html.haml')
32
+ end
33
+
34
+ protected
35
+
36
+ def form_for
37
+ if namespaces.blank?
38
+ singular_name
39
+ else
40
+ '[' + namespaces.map { |ns| ':' + ns }.join(', ') + ', ' + singular_name + ']'
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -4,26 +4,109 @@ module Effective
4
4
 
5
5
  protected
6
6
 
7
- def invoked_attributes
8
- _attributes = (respond_to?(:attributes) ? attributes : Array(options.attributes).compact)
9
- _attributes.map { |att| "#{att.name}:#{att.type}" }
7
+ def crud_actions
8
+ %w(index new create show edit update destroy)
10
9
  end
11
10
 
11
+ # --actions crud another
12
+ # --actions crud-show another
12
13
  def invoked_actions
13
- _actions = (respond_to?(:actions) ? actions : options.actions)
14
- _actions = Array(_actions).flat_map { |arg| arg.gsub('[', '').gsub(']', '').split(',') }
14
+ actions = (respond_to?(:actions) ? self.actions : options.actions)
15
+ actions = Array(actions).flat_map { |arg| arg.gsub('[', '').gsub(']', '').split(',') }
16
+
17
+ crudish = actions.find { |action| action.start_with?('crud') }
15
18
 
16
- case _actions
17
- when ['crud']
18
- %w(index new create show edit update destroy)
19
+ if crudish
20
+ actions = crud_actions + (actions - [crudish])
21
+ crudish.split('-').each { |except| actions.delete(except) }
22
+ end
23
+
24
+ actions
25
+ end
26
+
27
+ def invoked_attributes
28
+ if respond_to?(:attributes)
29
+ attributes.map { |att| "#{att.name}:#{att.type}" }
19
30
  else
20
- _actions
31
+ Array(options.attributes).compact
32
+ end
33
+ end
34
+
35
+ def invoked_attributes_args
36
+ invoked_attributes.present? ? (['--attributes'] + invoked_attributes) : []
37
+ end
38
+
39
+ def klass_attributes
40
+ klass = class_name.safe_constantize
41
+ return [] unless klass
42
+
43
+ begin
44
+ attributes = klass.new().attributes
45
+ rescue ActiveRecord::StatementInvalid => e
46
+ pending = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations(ActiveRecord::Migrator.migrations_paths)).pending_migrations.present?
47
+
48
+ if e.message.include?('PG::UndefinedTable') && pending
49
+ migrate = ask("Unable to read the attributes of #{class_name}. There are pending migrations. Run db:migrate now? [y/n]")
50
+ system('bundle exec rake db:migrate') if migrate.to_s.include?('y')
51
+ end
52
+ end
53
+
54
+ begin
55
+ attributes = klass.new().attributes
56
+ rescue => e
57
+ puts "Unable to call #{class_name}.new().attributes. Continuing with empty attributes."
58
+ return []
59
+ end
60
+
61
+ (attributes.keys - [klass.primary_key, 'created_at', 'updated_at']).map do |attr|
62
+ "#{attr}:#{klass.column_for_attribute(attr).type || 'string'}"
21
63
  end
22
64
  end
23
65
 
24
- # Used by model and datatable
25
66
  def parent_class_name
26
- options[:parent] || 'ApplicationRecord'
67
+ options[:parent] || (Rails::VERSION::MAJOR > 4 ? 'ApplicationRecord' : 'ActiveRecord::Base')
68
+ end
69
+
70
+ # We handle this a bit different than the regular scaffolds
71
+ def assign_names!(name)
72
+ @class_path = (name.include?('/') ? name[(name.rindex('/')+1)..-1] : name).split('::')
73
+ @class_path.map!(&:underscore)
74
+ @class_path[@class_path.length-1] = @class_path.last.singularize # Always singularize
75
+ @file_name = @class_path.pop
76
+ end
77
+
78
+ def namespaces
79
+ @namespaces ||= namespace_path.split('/')
80
+ end
81
+
82
+ # admin/effective::things => 'admin'
83
+ # effective::things => ''
84
+ def namespace_path
85
+ name.include?('/') ? name[0...name.rindex('/')] : ''
86
+ end
87
+
88
+ def namespaced_class_name
89
+ if name.include?('/')
90
+ name[0...name.rindex('/')].classify + '::' + singular_name.classify.pluralize
91
+ else
92
+ singular_name.classify.pluralize
93
+ end
94
+ end
95
+
96
+ def index_path
97
+ [namespace_path.underscore.presence, plural_name].compact.join('_') + '_path'
98
+ end
99
+
100
+ def new_path
101
+ ['new', namespace_path.underscore.presence, singular_name].compact.join('_') + '_path'
102
+ end
103
+
104
+ def edit_path
105
+ "edit_#{show_path}"
106
+ end
107
+
108
+ def show_path
109
+ [namespace_path.underscore.presence, singular_name].compact.join('_') + "_path(@#{singular_name})"
27
110
  end
28
111
 
29
112
  end
@@ -0,0 +1,52 @@
1
+ # rails generate effective:menu NAME
2
+
3
+ # Adds a menu to namespace/_navbar if present
4
+ # rails generate effective:menu Thing
5
+
6
+ module Effective
7
+ module Generators
8
+ class MenuGenerator < Rails::Generators::NamedBase
9
+ include Helpers
10
+
11
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
12
+
13
+ desc 'Adds a menu link to an existing _navbar.html.haml'
14
+
15
+ def invoke_ability
16
+ say_status :invoke, :menu, :white
17
+ end
18
+
19
+ def create_menu
20
+ begin
21
+ Effective::CodeWriter.new((['app/views'] + namespaces + ['_navbar.html.haml']).join('/')) do |w|
22
+ if w.find { |line, _| line == content.last.strip }
23
+ say_status :identical, index_path, :blue
24
+ else
25
+ index = w.last { |line, _| line.start_with?('- if can?') }
26
+
27
+ if index.blank?
28
+ say_status(:skipped, :menu, :yellow) and return
29
+ end
30
+
31
+ w.insert_raw(content, index+1, w.depth_at(index))
32
+ say_status :menu, index_path, :green
33
+ end
34
+ end
35
+ rescue Errno::ENOENT
36
+ # This will raise an error if the navbar file isn't present
37
+ say_status :skipped, :menu, :yellow
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def content
44
+ [
45
+ "\n",
46
+ "- if can? :manage, #{class_name}",
47
+ " %li= link_to '#{plural_name.titleize}', #{index_path}"
48
+ ]
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,18 +1,32 @@
1
1
  # rails generate effective:migration NAME [field[:type] field[:type]] [options]
2
2
 
3
+ # TODO - read from model dsl
4
+
5
+ # Generates a create_* migration
6
+ # rails generate effective:migration Thing
7
+ # rails generate effective:migration Thing name:string description:text
8
+
3
9
  module Effective
4
10
  module Generators
5
11
  class MigrationGenerator < Rails::Generators::NamedBase
6
12
  include Helpers
7
13
 
8
- source_root File.expand_path(('../' * 4) + 'app/scaffolds', __FILE__)
14
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
9
15
 
10
16
  desc 'Creates a migration.'
11
17
 
12
18
  argument :attributes, type: :array, default: [], banner: 'field[:type] field[:type]'
13
19
 
20
+ def invoke_migration
21
+ say_status :invoke, :migration, :white
22
+ end
23
+
14
24
  def create_migration
15
- Rails::Generators.invoke('migration', ["create_#{file_name.pluralize}"] + invoked_attributes)
25
+ Rails::Generators.invoke('migration', ["create_#{plural_name}"] + invoked_attributes)
26
+ end
27
+
28
+ def invoked_attributes
29
+ super | ['created_at:datetime', 'updated_at:datetime']
16
30
  end
17
31
 
18
32
  end
@@ -1,20 +1,42 @@
1
1
  # rails generate effective:model NAME [field[:type] field[:type]] [options]
2
2
 
3
+ # Generates a model
4
+ # rails generate effective:model Thing
5
+ # rails generate effective:model Thing name:string description:text
6
+
3
7
  module Effective
4
8
  module Generators
5
9
  class ModelGenerator < Rails::Generators::NamedBase
6
10
  include Helpers
7
11
 
8
- source_root File.expand_path(('../' * 4) + 'app/scaffolds', __FILE__)
12
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
9
13
 
10
14
  desc 'Creates a model in your app/models folder.'
11
15
 
12
16
  argument :attributes, type: :array, default: [], banner: 'field[:type] field[:type]'
13
17
 
18
+ def invoke_model
19
+ say_status :invoke, :model, :white
20
+ end
21
+
14
22
  def create_model
15
23
  template 'models/model.rb', File.join('app/models', class_path, "#{file_name}.rb")
16
24
  end
17
25
 
26
+ protected
27
+
28
+ def to_s_attribute
29
+ attributes.find { |att| ['display_name', 'name', 'title', 'subject'].include?(att.name) }
30
+ end
31
+
32
+ def archived_attribute
33
+ attributes.find { |att| att.name == 'archived' && att.type == :boolean }
34
+ end
35
+
36
+ def max_attribute_name_length
37
+ @max_attribute_name_length ||= (attributes.map { |att| att.name.length }.max || 0)
38
+ end
39
+
18
40
  end
19
41
  end
20
42
  end
@@ -1,18 +1,74 @@
1
1
  # rails generate effective:route NAME [action action] [options]
2
2
 
3
+ # Adds a route to config/routes.rb
4
+ # rails generate effective:route Thing
5
+ # rails generate effective:model Thing index edit create
6
+
3
7
  module Effective
4
8
  module Generators
5
9
  class RouteGenerator < Rails::Generators::NamedBase
6
10
  include Helpers
7
11
 
8
- source_root File.expand_path(('../' * 4) + 'app/scaffolds', __FILE__)
12
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
9
13
 
10
14
  desc 'Creates a route.'
11
15
 
12
16
  argument :actions, type: :array, default: ['crud'], banner: 'action action'
13
17
 
18
+ def invoke_route
19
+ say_status :invoke, :route, :white
20
+ end
21
+
14
22
  def create_route
15
- Rails::Generators.invoke('resource_route', [name])
23
+ blocks = []
24
+
25
+ Effective::CodeWriter.new('config/routes.rb') do |w|
26
+ namespaces.each do |namespace|
27
+ index = nil
28
+
29
+ w.within(blocks.last) do
30
+ index = w.first { |line, depth| depth == 1 && line == "namespace :#{namespace} do"}
31
+ end
32
+
33
+ index ? (blocks << index) : break
34
+ end
35
+
36
+ content = namespaces[blocks.length..-1].map { |ns| "namespace :#{ns} do"} + [resources].flatten + (['end'] * (namespaces.length - blocks.length))
37
+
38
+ w.within(blocks.last) do
39
+ if content.length == 1 && w.find { |line, depth| depth == 1 && line == content.first }
40
+ say_status :identical, content.first, :blue
41
+ else
42
+ w.insert_after_last(content) { |line, depth| depth == 1 && line.start_with?('resources') } ||
43
+ w.insert_before_last(content) { |line, depth| depth == 1 && line.start_with?('root') } ||
44
+ w.insert_before_last(content) { |line, depth| line == 'end' }
45
+
46
+ say_status :route, content.join("\n\t\t")
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def resources
55
+ @resources ||= (
56
+ resources = "resources :#{plural_name}"
57
+
58
+ if ((crud_actions - ['show']) == invoked_actions)
59
+ resources << ', except: [:show]'
60
+ elsif (crud_actions - invoked_actions).present?
61
+ resources << ', only: ['
62
+ resources << (crud_actions & invoked_actions).map { |action| ':' + action }.join(', ')
63
+ resources << ']'
64
+ end
65
+
66
+ if (invoked_actions - crud_actions).present?
67
+ [resources + ' do'] + (invoked_actions - crud_actions).map { |action| "get :#{action}, on: :member" } + ['end']
68
+ else
69
+ resources
70
+ end
71
+ )
16
72
  end
17
73
 
18
74
  end
@@ -0,0 +1,51 @@
1
+ # rails generate effective:scaffold NAME [field[:type] field[:type]] [options]
2
+
3
+ # Generates a migration, model, datatable, routes, controller, views
4
+
5
+ # rails generate effective:scaffold Thing
6
+ # rails generate effective:scaffold admin/thing name:string details:text --actions index show edit update
7
+ # rails generate effective:scaffold admin/thing name:string details:text
8
+
9
+ module Effective
10
+ module Generators
11
+ class ScaffoldControllerGenerator < Rails::Generators::NamedBase
12
+ include Helpers
13
+
14
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
15
+
16
+ desc 'Creates an Effective Scaffold'
17
+
18
+ argument :attributes, type: :array, default: [], banner: 'field[:type] field[:type]'
19
+ class_option :actions, type: :array, default: ['crud'], desc: 'Included actions', banner: 'index show'
20
+
21
+ def invoke_controller
22
+ Rails::Generators.invoke('effective:controller', [name] + invoked_actions + invoked_attributes_args)
23
+ end
24
+
25
+ def invoke_route
26
+ Rails::Generators.invoke('effective:route', [name] + invoked_actions)
27
+ end
28
+
29
+ def invoke_ability
30
+ Rails::Generators.invoke('effective:ability', [name] + invoked_actions)
31
+ end
32
+
33
+ def invoke_menu
34
+ Rails::Generators.invoke('effective:menu', [name])
35
+ end
36
+
37
+ def invoke_datatable
38
+ Rails::Generators.invoke('effective:datatable', [name] + invoked_attributes)
39
+ end
40
+
41
+ def invoke_views
42
+ Rails::Generators.invoke('effective:views', [name] + invoked_actions + invoked_attributes_args)
43
+ end
44
+
45
+ def invoke_form
46
+ Rails::Generators.invoke('effective:form', [name] + invoked_attributes)
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  # rails generate effective:scaffold Thing
4
4
  # rails generate effective:scaffold admin/thing name:string details:text --actions index show edit update
5
+ # rails generate effective:scaffold admin/thing name:string details:text --actions crud-show
5
6
  # rails generate effective:scaffold admin/thing name:string details:text
6
7
 
7
8
  module Effective
@@ -9,77 +10,49 @@ module Effective
9
10
  class ScaffoldGenerator < Rails::Generators::NamedBase
10
11
  include Helpers
11
12
 
12
- source_root File.expand_path(('../' * 4) + 'app/scaffolds', __FILE__)
13
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
13
14
 
14
15
  desc 'Creates an Effective Scaffold'
15
16
 
16
17
  argument :attributes, type: :array, default: [], banner: 'field[:type] field[:type]'
17
18
  class_option :actions, type: :array, default: ['crud'], desc: 'Included actions', banner: 'index show'
18
19
 
19
- def create_migration
20
- Rails::Generators.invoke('effective:migration', [name] + invoked_attributes)
20
+ def invoke_model
21
+ Rails::Generators.invoke('effective:model', [name] + invoked_attributes)
21
22
  end
22
23
 
23
- def create_model
24
- Rails::Generators.invoke('effective:model', [name] + invoked_attributes)
24
+ def invoke_migration
25
+ Rails::Generators.invoke('effective:migration', [name] + invoked_attributes)
25
26
  end
26
27
 
27
- def create_datatable
28
- Rails::Generators.invoke('effective:datatable', [name] + invoked_attributes)
28
+ def invoke_controller
29
+ Rails::Generators.invoke('effective:controller', [name] + invoked_actions + invoked_attributes_args)
29
30
  end
30
31
 
31
- def create_routes
32
+ def invoke_route
32
33
  Rails::Generators.invoke('effective:route', [name] + invoked_actions)
33
34
  end
34
35
 
35
- def create_controller
36
- Rails::Generators.invoke('effective:controller', [name] + invoked_actions, attributes: invoked_attributes)
36
+ def invoke_ability
37
+ Rails::Generators.invoke('effective:ability', [name] + invoked_actions)
37
38
  end
38
39
 
39
- def create_views
40
- Rails::Generators.invoke('effective:view', [name] + invoked_actions, attributes: invoked_attributes)
40
+ def invoke_menu
41
+ Rails::Generators.invoke('effective:menu', [name])
41
42
  end
42
43
 
43
- end
44
- end
45
- end
46
-
47
- # class_name
48
- # class_path
49
- # file_path
50
-
51
-
52
-
53
- # require "rails/generators/rails/resource/resource_generator"
54
-
55
- # module Effective
56
- # module Generators
57
- # class ScaffoldGenerator < ResourceGenerator # :nodoc:
58
- # remove_hook_for :resource_controller
59
- # remove_class_option :actions
60
-
61
- # class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
62
- # class_option :stylesheet_engine, desc: "Engine for Stylesheets"
63
- # class_option :assets, type: :boolean
64
- # class_option :resource_route, type: :boolean
65
- # class_option :scaffold_stylesheet, type: :boolean
66
-
67
- # def handle_skip
68
- # @options = @options.merge(stylesheets: false) unless options[:assets]
69
- # @options = @options.merge(stylesheet_engine: false) unless options[:stylesheets] && options[:scaffold_stylesheet]
70
- # end
44
+ def invoke_datatable
45
+ Rails::Generators.invoke('effective:datatable', [name] + invoked_attributes)
46
+ end
71
47
 
72
- # hook_for :scaffold_controller, required: true
48
+ def invoke_views
49
+ Rails::Generators.invoke('effective:views', [name] + invoked_actions + invoked_attributes_args)
50
+ end
73
51
 
74
- # hook_for :assets do |assets|
75
- # invoke assets, [controller_name]
76
- # end
52
+ def invoke_form
53
+ Rails::Generators.invoke('effective:form', [name] + invoked_attributes)
54
+ end
77
55
 
78
- # hook_for :stylesheet_engine do |stylesheet_engine|
79
- # if behavior == :invoke
80
- # invoke stylesheet_engine, [controller_name]
81
- # end
82
- # end
83
- # end
84
- # end
85
- # end
56
+ end
57
+ end
58
+ end