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.
- checksums.yaml +4 -4
- data/README.md +31 -9
- data/app/models/effective/code_writer.rb +218 -0
- data/lib/effective_developer/version.rb +1 -1
- data/lib/generators/effective/ability_generator.rb +78 -0
- data/lib/generators/effective/controller_generator.rb +27 -9
- data/lib/generators/effective/datatable_generator.rb +20 -2
- data/lib/generators/effective/form_generator.rb +46 -0
- data/lib/generators/effective/helpers.rb +94 -11
- data/lib/generators/effective/menu_generator.rb +52 -0
- data/lib/generators/effective/migration_generator.rb +16 -2
- data/lib/generators/effective/model_generator.rb +23 -1
- data/lib/generators/effective/route_generator.rb +58 -2
- data/lib/generators/effective/scaffold_controller_generator.rb +51 -0
- data/lib/generators/effective/scaffold_generator.rb +25 -52
- data/lib/generators/effective/views_generator.rb +47 -0
- data/lib/scaffolds/controllers/controller.rb +142 -0
- data/lib/scaffolds/datatables/datatable.rb +18 -0
- data/lib/scaffolds/forms/_form.html.haml +9 -0
- data/lib/{generators/effective_developer/csv_importer.rb.erb → scaffolds/importers/csv_importer.rb} +0 -0
- data/lib/scaffolds/models/model.rb +48 -0
- data/lib/scaffolds/views/_resource.html.haml +7 -0
- data/lib/scaffolds/views/edit.html.haml +3 -0
- data/lib/scaffolds/views/index.html.haml +10 -0
- data/lib/scaffolds/views/new.html.haml +3 -0
- data/lib/scaffolds/views/show.html.haml +3 -0
- data/lib/tasks/effective_csv_importer.rake +1 -1
- metadata +18 -12
- data/app/scaffolds/controllers/controller.rb +0 -13
- data/app/scaffolds/datatables/datatable.rb +0 -14
- data/app/scaffolds/models/model.rb +0 -15
- data/app/scaffolds/views/_form.html.haml +0 -15
- data/app/scaffolds/views/edit.html.haml +0 -7
- data/app/scaffolds/views/index.html.haml +0 -25
- data/app/scaffolds/views/new.html.haml +0 -5
- data/app/scaffolds/views/show.html.haml +0 -11
- data/lib/generators/effective/view_generator.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4dba9b31a7c6e5352ab958ac3a0f1927e5531013
|
4
|
+
data.tar.gz: 4f0127dc920f97aaef72cdad34b09aadac2356fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
218
|
-
|
219
|
-
rails generate
|
220
|
-
|
221
|
-
rails generate
|
222
|
-
|
223
|
-
rails generate
|
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
|
@@ -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) + '
|
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
|
-
|
20
|
+
def assign_actions
|
21
|
+
@actions = invoked_actions
|
22
|
+
end
|
16
23
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
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
|
-
|
29
|
+
self.class.send(:attr_reader, :attributes)
|
23
30
|
end
|
24
31
|
|
25
|
-
def
|
26
|
-
|
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',
|
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) + '
|
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',
|
33
|
+
template 'datatables/datatable.rb', File.join('app/models/effective/datatables', namespace_path, "#{plural_name}.rb")
|
16
34
|
end
|
17
35
|
|
18
36
|
end
|