effective_developer 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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