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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f8689f1bc4992384e17f61c4d8c65dc4c9eff7d8
4
- data.tar.gz: a0f2a455a5bf32d0284deebbd5761ec246e146e0
3
+ metadata.gz: 4dba9b31a7c6e5352ab958ac3a0f1927e5531013
4
+ data.tar.gz: 4f0127dc920f97aaef72cdad34b09aadac2356fc
5
5
  SHA512:
6
- metadata.gz: 10cc81aa4428dc08fe8c2f94e9e63702d8ee95e7316b3f4ff9a6dd7cffd06a407e6e22f95233b4f292e2e02dc893fa84f1e2c515627741abcb481961c7cde5dc
7
- data.tar.gz: 0c6397219c15e4256b563d251c0d53bdb3ce20b1977ebeb719cd67bce70c0b5f99e718431c0aadb6e594d8be60d30a0b102d05509ec60e3a9ff82a27fdd638da
6
+ metadata.gz: 447cd0b8ed58bb3bdc2557454795d663ec5a491ee76e7bf967f1aaa7dd37e503ff4603be3217332a0bdf44c5bf018dc9e1e31cd52c94c3df2262b1845f73255c
7
+ data.tar.gz: 3d7dd836c6fe704dc80082cb4479e56443f77417f17603443a2caec606e5ad20f02c7e9bf5449846f46e53bdfa86401566612bbb54328c51636e05b0ee128840
data/README.md CHANGED
@@ -210,21 +210,43 @@ Override `before_import()` or `after_import()` to run code before or after the i
210
210
 
211
211
  # Scaffolding
212
212
 
213
- Scaffolding is the fastest way to build a rails app. Take advantage of scaffolding.
213
+ Scaffolding is the fastest way to build a rails app. The effective scaffolds try to improve on the rails built in ones.
214
214
 
215
+ To generate an entire resource (4 different examples):
215
216
 
216
217
  ```ruby
217
- rails generate scaffold product name:string # active_record, test_unit, resource_route, scaffold_controller, haml, test_unit, helper, assets
218
- rails generate scaffold_controller product # haml, test_unit, helper,
219
- rails generate model product # active_record, test_unit
220
- rails generate active_record:model product # test_unit
221
- rails generate resource_route product
222
- rails generate test_unit:model product
223
- rails generate mailer product
224
- rails generate job product
218
+ rails generate effective:scaffold thing name:string description:text
219
+
220
+ rails generate effective:scaffold admin/thing name:string description:text
221
+
222
+ rails generate effective:scaffold thing name:string description:text --actions crud archive
223
+
224
+ rails generate effective:scaffold admin/thing name:string description:text --actions crud-show unarchive
225
225
  ```
226
226
 
227
+ Or, read from the existing model, and just generate the controller, route, ability, menu, datatable, views, form (2 different examples):
228
+
229
+ ```ruby
230
+ rails generate effective:scaffold_controller thing
231
+
232
+ rails generate effective:scaffold_controller admin/thing --actions crud-show
233
+ ```
234
+
235
+ Or call each scaffold one at a time:
236
+
237
+ ```ruby
238
+ rails generate effective:model thing name:string description:text
239
+ rails generate effective:migration thing name:string description:text
240
+ rails generate effective:controller thing # or admin/thing
241
+ rails generate effective:route thing
242
+ rails generate effective:ability thing # CanCanCan
243
+ rails generate effective:menu thing # If app/views/*namespaces/_navbar.html.haml is present
244
+ rails generate effective:datatable thing
245
+ rails generate effective:views thing
246
+ rails generate effective:form thing
247
+ ```
227
248
 
249
+ These scaffold generators are still an ongoing work in progress. There is a lot more inspect the model and do the right thing type functionality to be implemented "soon".
228
250
 
229
251
  ## License
230
252
 
@@ -0,0 +1,218 @@
1
+ module Effective
2
+ class CodeWriter
3
+
4
+ attr_reader :lines
5
+ attr_reader :filename, :indent, :newline
6
+
7
+ def initialize(filename, indent: ' '.freeze, newline: "\n".freeze, &block)
8
+ @filename = filename
9
+ @indent = indent
10
+ @newline = newline
11
+
12
+ @from = []
13
+ @to = []
14
+
15
+ @lines = File.open(filename).readlines
16
+
17
+ block.call(self)
18
+
19
+ File.open(filename, 'w') do |file|
20
+ lines.each { |line| file.write(line) }
21
+ end
22
+ end
23
+
24
+ # Returns true if the insert happened, nil if no insert
25
+ def insert_after_last(content, &block)
26
+ index = last(&block)
27
+ return nil unless index
28
+
29
+ insert(content, index)
30
+ end
31
+
32
+ # Returns true if the insert happened, nil if no insert
33
+ def insert_before_last(content, &block)
34
+ index = last(&block)
35
+ return nil unless index
36
+
37
+ insert(content, index-1)
38
+ end
39
+
40
+ def within(content, &block)
41
+ content ||= 0
42
+
43
+ from = content.kind_of?(Integer) ? content : first { |line| line.start_with?(content) && open?(line) }
44
+ return nil unless from
45
+
46
+ from_depth = depth_at(from)
47
+
48
+ to = first(from: from) { |line, depth| depth == from_depth && close?(line) }
49
+ return nil unless to
50
+
51
+ @from.push(from); @to.push(to)
52
+ yield
53
+ @from.pop; @to.pop
54
+ end
55
+
56
+ def insert(content, index, depth = nil)
57
+ contents = (content.kind_of?(Array) ? content : content.split(newline)).map { |str| str.strip }
58
+
59
+ depth ||= depth_at(index)
60
+
61
+ # If the line we're inserting at is a block, fast-forward the end of the block. And add a newline.
62
+ if open?(index)
63
+ index = first(from: index) { |line| close?(line) } + 1
64
+ lines.insert(index, newline)
65
+ elsif !whitespace?(index) && (open?(contents) || !same?(contents, index))
66
+ index += 1
67
+ lines.insert(index, newline)
68
+ end
69
+
70
+ content_depth = 0
71
+
72
+ index = index + 1 # Insert after the given line
73
+
74
+ contents.each do |content|
75
+ content_depth -= 1 if close?(content)
76
+
77
+ if content == ''
78
+ lines.insert(index, newline)
79
+ else
80
+ lines.insert(index, (indent * (depth + content_depth)) + content + newline)
81
+ end
82
+
83
+ index += 1
84
+ content_depth += 1 if open?(content)
85
+ end
86
+
87
+ unless whitespace?(index) || close?(index)
88
+ if block?(contents) || !same?(contents, index)
89
+ lines.insert(index, newline)
90
+ end
91
+ end
92
+
93
+ true
94
+ end
95
+
96
+ def insert_raw(content, index, depth = 0)
97
+ contents = (content.kind_of?(Array) ? content : content.split(newline))
98
+
99
+ index = index + 1 # Insert after the given line
100
+
101
+ contents.each do |content|
102
+ if content.strip == ''
103
+ lines.insert(index, newline)
104
+ else
105
+ lines.insert(index, (indent * depth) + content + newline)
106
+ end
107
+
108
+ index += 1
109
+ end
110
+ end
111
+
112
+ # Iterate over the lines with a depth, and passed the stripped line to the passed block
113
+ def each_with_depth(&block)
114
+ depth = 0
115
+ from_depth = (@from.last ? depth_at(@from.last) : 0)
116
+
117
+ Array(lines).each_with_index do |line, index|
118
+ stripped = line.to_s.strip
119
+
120
+ depth -= 1 if close?(stripped)
121
+
122
+ block.call(stripped, depth - from_depth, index)
123
+ depth += 1 if open?(stripped)
124
+ end
125
+
126
+ nil
127
+ end
128
+
129
+ # Returns the index of the first line where the passed block returns true
130
+ def first(from: @from.last, to: @to.last, &block)
131
+ each_with_depth do |line, depth, index|
132
+ next if index < (from || 0)
133
+ return index if block.call(line, depth, index)
134
+ break if to == index
135
+ end
136
+ end
137
+ alias_method :find, :first
138
+
139
+ # Returns the index of the last line where the passed block returns true
140
+ def last(from: @from.last, to: @to.last, &block)
141
+ retval = nil
142
+
143
+ each_with_depth do |line, depth, index|
144
+ next if index < (from || 0)
145
+ retval = index if block.call(line, depth, index)
146
+ break if to == index
147
+ end
148
+
149
+ retval
150
+ end
151
+
152
+ # Returns an array of indexes for each line where the passed block returnst rue
153
+ def all(from: @from.last, to: @to.last, &block)
154
+ retval = []
155
+
156
+ each_with_depth do |line, depth, index|
157
+ next if index < (from || 0)
158
+ retval << index if block.call(line, depth, index)
159
+ break if to == index
160
+ end
161
+
162
+ retval
163
+ end
164
+ alias_method :select, :all
165
+
166
+ def depth_at(line_index)
167
+ if filename.end_with?('.haml')
168
+ return (lines[line_index].length - lines[line_index].lstrip.length) / indent.length
169
+ end
170
+
171
+ depth = 0
172
+
173
+ Array(lines).each_with_index do |line, index|
174
+ depth -= 1 if close?(line)
175
+ break if line_index == index
176
+ depth += 1 if open?(line)
177
+ end
178
+
179
+ depth
180
+ end
181
+
182
+ private
183
+
184
+ def open?(content)
185
+ stripped = ss(content)
186
+
187
+ [' do'].any? { |end_with| stripped.end_with?(end_with) } ||
188
+ ['class ', 'module ', 'def ', 'if '].any? { |start_with| stripped.start_with?(start_with) }
189
+ end
190
+
191
+ def close?(content)
192
+ stripped = ss(content, array_method: :last)
193
+ stripped.end_with?('end'.freeze) && !stripped.include?('do ')
194
+ end
195
+
196
+ def whitespace?(content)
197
+ ss(content).length == 0
198
+ end
199
+
200
+ def block?(content)
201
+ close?(content) || open?(content)
202
+ end
203
+
204
+ # Is the first word in each line the same?
205
+ def same?(a, b)
206
+ ss(a).split(' ').first == ss(b).split(' ').first
207
+ end
208
+
209
+ # Stripped string
210
+ def ss(value, array_method: :first)
211
+ value = case value
212
+ when Integer then lines[value]
213
+ when Array then value.send(array_method)
214
+ else value
215
+ end.strip
216
+ end
217
+ end
218
+ end
@@ -1,3 +1,3 @@
1
1
  module EffectiveDeveloper
2
- VERSION = '0.0.8'.freeze
2
+ VERSION = '0.0.9'.freeze
3
3
  end
@@ -0,0 +1,78 @@
1
+ # rails generate effective:ability NAME [action action] [options]
2
+
3
+ # Adds a route to app/models/ability.rb
4
+ # rails generate effective:ability Thing
5
+ # rails generate effective:ability Thing index edit create
6
+
7
+ module Effective
8
+ module Generators
9
+ class AbilityGenerator < Rails::Generators::NamedBase
10
+ include Helpers
11
+
12
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
13
+
14
+ desc 'Creates a CanCanCan ability.'
15
+
16
+ argument :actions, type: :array, default: ['crud'], banner: 'action action'
17
+
18
+ def invoke_ability
19
+ say_status :invoke, :ability, :white
20
+ end
21
+
22
+ def create_ability
23
+ Effective::CodeWriter.new('app/models/ability.rb') do |w|
24
+ if namespaces.blank?
25
+ if w.find { |line, depth| depth == 2 && line == ability }
26
+ say_status :identical, ability, :blue
27
+ else
28
+ w.insert_after_last(ability) { |line, depth| depth == 2 && line.start_with?('can ') } ||
29
+ w.insert_before_last(ability) { |line, depth| depth == 2 && line.start_with?('if') } ||
30
+ w.insert_before_last(ability) { |line, depth| depth == 2 && line == 'end' }
31
+
32
+ say_status :ability, ability
33
+ end
34
+ end
35
+
36
+ namespaces.each do |namespace|
37
+ w.within("if user.is?(:#{namespace})") do
38
+ if w.find { |line, depth| depth == 1 && line == ability }
39
+ say_status :identical, ability, :blue
40
+ else
41
+ w.insert_after_last(ability) { |line, depth| depth == 1 && line.start_with?('can ') } ||
42
+ w.insert_before_last(ability) { |line, depth| depth == 1 && line == 'end' }
43
+
44
+ say_status "#{namespace}_ability", ability
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def ability
54
+ @ability ||= (
55
+ abilities = []
56
+
57
+ if (crud_actions - invoked_actions).present?
58
+ abilities += (crud_actions & invoked_actions)
59
+ end
60
+
61
+ if (invoked_actions - crud_actions).present?
62
+ abilities += (invoked_actions - crud_actions)
63
+ end
64
+
65
+ abilities = ['manage'] if abilities.blank? || abilities == (crud_actions - ['show'])
66
+
67
+ if abilities.length == 1
68
+ abilities = ":#{abilities.first}"
69
+ else
70
+ abilities = '[' + abilities.map { |action| ':' + action }.join(', ') + ']'
71
+ end
72
+
73
+ "can #{abilities}, #{class_name}"
74
+ )
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,33 +1,51 @@
1
1
  # rails generate effective:controller NAME [action action] [options]
2
2
 
3
+ # Generates a controller
4
+ # rails generate effective:controller Thing
5
+ # rails generate effective:controller Thing index edit create
6
+ # rails generate effective:controller Thing index edit create --attributes name:string description:text
7
+
3
8
  module Effective
4
9
  module Generators
5
10
  class ControllerGenerator < Rails::Generators::NamedBase
6
11
  include Helpers
7
12
 
8
- source_root File.expand_path(('../' * 4) + 'app/scaffolds', __FILE__)
13
+ source_root File.expand_path(('../' * 4) + 'lib/scaffolds', __FILE__)
9
14
 
10
15
  desc 'Creates a controller in your app/controllers folder.'
11
16
 
12
17
  argument :actions, type: :array, default: ['crud'], banner: 'action action'
13
18
  class_option :attributes, type: :array, default: [], desc: 'Included permitted params, otherwise read from model'
14
19
 
15
- attr_accessor :attributes
20
+ def assign_actions
21
+ @actions = invoked_actions
22
+ end
16
23
 
17
- def initialize(args, *options)
18
- if options.kind_of?(Array) && options.second.kind_of?(Hash)
19
- self.attributes = options.second.delete(:attributes)
24
+ def assign_attributes
25
+ @attributes = (invoked_attributes.presence || klass_attributes).map do |attribute|
26
+ Rails::Generators::GeneratedAttribute.parse(attribute)
20
27
  end
21
28
 
22
- super
29
+ self.class.send(:attr_reader, :attributes)
23
30
  end
24
31
 
25
- def assign_actions
26
- @actions = invoked_actions
32
+ def invoke_controller
33
+ say_status :invoke, :controller, :white
27
34
  end
28
35
 
29
36
  def create_controller
30
- template 'controllers/controller.rb', File.join('app/controllers', class_path, "#{file_name}_controller.rb")
37
+ template 'controllers/controller.rb', File.join('app/controllers', namespace_path, "#{plural_name}_controller.rb")
38
+ end
39
+
40
+ protected
41
+
42
+ def permitted_param_for(attribute_name)
43
+ case attribute_name
44
+ when 'roles'
45
+ 'roles: EffectiveRoles.permitted_params'
46
+ else
47
+ ':' + attribute_name
48
+ end
31
49
  end
32
50
 
33
51
  end
@@ -1,18 +1,36 @@
1
1
  # rails generate effective:datatable NAME [field[:type] field[:type]] [options]
2
2
 
3
+ # TODO
4
+
5
+ # Generates a datatable
6
+ # rails generate effective:datatable Thing
7
+ # rails generate effective:controller Thing name:string description:text
8
+
3
9
  module Effective
4
10
  module Generators
5
11
  class DatatableGenerator < 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 an Effective::Datatable in your app/effective/datatables folder.'
11
17
 
12
18
  argument :attributes, type: :array, default: [], banner: 'field[:type] field[:type]'
13
19
 
20
+ def assign_attributes
21
+ @attributes = (invoked_attributes.presence || klass_attributes).map do |attribute|
22
+ Rails::Generators::GeneratedAttribute.parse(attribute)
23
+ end
24
+
25
+ self.class.send(:attr_reader, :attributes)
26
+ end
27
+
28
+ def invoke_datatable
29
+ say_status :invoke, :datatable, :white
30
+ end
31
+
14
32
  def create_datatable
15
- template 'datatables/datatable.rb', File.join('app/models/effective/datatables', class_path, "#{file_name}.rb")
33
+ template 'datatables/datatable.rb', File.join('app/models/effective/datatables', namespace_path, "#{plural_name}.rb")
16
34
  end
17
35
 
18
36
  end