bullet_train-super_scaffolding 1.0.32 → 1.0.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v1/scaffolding/completely_concrete/tangible_things_endpoint.rb +1 -0
- data/app/serializers/api/v1/scaffolding/completely_concrete/tangible_thing_serializer.rb +8 -0
- data/app/views/account/scaffolding/completely_concrete/tangible_things/_form.html.erb +1 -1
- data/lib/bullet_train/super_scaffolding/version.rb +1 -1
- data/lib/scaffolding/block_manipulator.rb +36 -10
- data/lib/scaffolding/file_manipulator.rb +71 -0
- data/lib/scaffolding/routes_file_manipulator.rb +19 -63
- data/lib/scaffolding/transformer.rb +68 -68
- data/lib/scaffolding.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 329a1d276e3bc2b2a89c230d35b8ea6613abc957d34de2319455458e88658ca2
|
4
|
+
data.tar.gz: 27994643a5a47719703a204d849732a9c67382fab553a4decad9984b2b5f08d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4956a720055fb48e9d26dba793a131b12291cc338bfd025743c34d78fd0f6d6c1f36a702f98ce46c49d10930448b6c87faa8c75d3a8b021d47552c95f2aecbf2
|
7
|
+
data.tar.gz: 783996a02a3482d95714a3d66c0b3119b54b3dd0d7cbbcb58b8f44af403be5c4b2f6524c3a8df7ccb5034f1f3a82ba45f8673f49521b206db9164164d7410748
|
@@ -16,6 +16,7 @@ class Api::V1::Scaffolding::CompletelyConcrete::TangibleThingsEndpoint < Api::V1
|
|
16
16
|
optional :date_field_value, type: Date, allow_blank: true, desc: Api.heading(:date_field_value)
|
17
17
|
optional :date_and_time_field_value, type: DateTime, allow_blank: true, desc: Api.heading(:date_and_time_field_value)
|
18
18
|
optional :email_field_value, type: String, allow_blank: true, desc: Api.heading(:email_field_value)
|
19
|
+
optional :file_field_value, type: String, allow_blank: true, desc: Api.heading(:file_field_value)
|
19
20
|
optional :password_field_value, type: String, allow_blank: true, desc: Api.heading(:password_field_value)
|
20
21
|
optional :phone_field_value, type: String, allow_blank: true, desc: Api.heading(:phone_field_value)
|
21
22
|
optional :option_value, type: String, allow_blank: true, desc: Api.heading(:option_value)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
class Api::V1::Scaffolding::CompletelyConcrete::TangibleThingSerializer < Api::V1::ApplicationSerializer
|
2
2
|
set_type "scaffolding/completely_concrete/tangible_thing"
|
3
|
+
singleton_class.include Rails.application.routes.url_helpers
|
3
4
|
|
4
5
|
attributes :id,
|
5
6
|
:absolutely_abstract_creative_concept_id,
|
@@ -25,5 +26,12 @@ class Api::V1::Scaffolding::CompletelyConcrete::TangibleThingSerializer < Api::V
|
|
25
26
|
:created_at,
|
26
27
|
:updated_at
|
27
28
|
|
29
|
+
# 🚅 skip this section when scaffolding.
|
30
|
+
attribute :file_field_value do |object|
|
31
|
+
rails_blob_path(object.file_field_value, disposition: "attachment", only_path: true) if object.file_field_value.attached?
|
32
|
+
end
|
33
|
+
# 🚅 stop any skipping we're doing now.
|
34
|
+
# 🚅 super scaffolding will insert file-related logic above this line.
|
35
|
+
|
28
36
|
belongs_to :absolutely_abstract_creative_concept, serializer: Api::V1::Scaffolding::AbsolutelyAbstract::CreativeConceptSerializer
|
29
37
|
end
|
@@ -17,7 +17,7 @@
|
|
17
17
|
</div>
|
18
18
|
</div>
|
19
19
|
|
20
|
-
<%= render 'shared/fields/color_picker', method: :color_picker_value, options: t('scaffolding/completely_concrete/tangible_things.fields.color_picker_value.options') %>
|
20
|
+
<%= render 'shared/fields/color_picker', method: :color_picker_value, options: {color_picker_options: t('scaffolding/completely_concrete/tangible_things.fields.color_picker_value.options')} %>
|
21
21
|
<%= render 'shared/fields/cloudinary_image', method: :cloudinary_image_value %>
|
22
22
|
<%= render 'shared/fields/date_field', method: :date_field_value %>
|
23
23
|
<%= render 'shared/fields/date_and_time_field', method: :date_and_time_field_value %>
|
@@ -7,16 +7,16 @@ class Scaffolding::BlockManipulator
|
|
7
7
|
end
|
8
8
|
|
9
9
|
#
|
10
|
-
# Wrap a block of ruby code
|
10
|
+
# Wrap a block of ruby code with another block on the outside.
|
11
11
|
#
|
12
|
-
# @param [String] starting A string to search for at the start of the block. Eg "<%= updates_for context, collection do"
|
13
|
-
# @param [Array] with An array with two String elements. The text that should wrap the block. Eg ["<%= action_model_select_controller do %>", "<% end %>"]
|
12
|
+
# @param [String] `starting` A string to search for at the start of the block. Eg "<%= updates_for context, collection do"
|
13
|
+
# @param [Array] `with` An array with two String elements. The text that should wrap the block. Eg ["<%= action_model_select_controller do %>", "<% end %>"]
|
14
14
|
#
|
15
15
|
def wrap_block(starting:, with:)
|
16
16
|
with[0] += "\n" unless with[0].match?(/\n$/)
|
17
17
|
with[1] += "\n" unless with[1].match?(/\n$/)
|
18
18
|
starting_line = find_block_start(starting)
|
19
|
-
end_line = find_block_end(starting_from: starting_line)
|
19
|
+
end_line = find_block_end(starting_from: starting_line, lines: @lines)
|
20
20
|
|
21
21
|
final = []
|
22
22
|
block_indent = ""
|
@@ -55,13 +55,13 @@ class Scaffolding::BlockManipulator
|
|
55
55
|
end_line = @lines.count - 1
|
56
56
|
if within.present?
|
57
57
|
start_line = find_block_start(within)
|
58
|
-
end_line = find_block_end(starting_from: start_line)
|
58
|
+
end_line = find_block_end(starting_from: start_line, lines: @lines)
|
59
59
|
# start_line += 1 # ensure we actually insert the content _within_ the given block
|
60
60
|
# end_line += 1 if end_line == start_line
|
61
61
|
end
|
62
62
|
if after_block.present?
|
63
63
|
block_start = find_block_start(after_block)
|
64
|
-
block_end = find_block_end(starting_from: block_start)
|
64
|
+
block_end = find_block_end(starting_from: block_start, lines: @lines)
|
65
65
|
start_line = block_end
|
66
66
|
end_line = @lines.count - 1
|
67
67
|
end
|
@@ -102,15 +102,28 @@ class Scaffolding::BlockManipulator
|
|
102
102
|
|
103
103
|
def insert_block(block_content, after_block:)
|
104
104
|
block_start = find_block_start(after_block)
|
105
|
-
block_end = find_block_end(starting_from: block_start)
|
105
|
+
block_end = find_block_end(starting_from: block_start, lines: @lines)
|
106
106
|
insert_line(block_content[0], block_end)
|
107
107
|
insert_line(block_content[1], block_end + 1)
|
108
108
|
end
|
109
109
|
|
110
|
+
# TODO: Delete this because it only really needs to be in the FileManipulator
|
110
111
|
def write
|
111
112
|
File.write(@filepath, @lines.join)
|
112
113
|
end
|
113
114
|
|
115
|
+
def find_block_parent(starting_line_number, lines)
|
116
|
+
return nil unless indentation_of(starting_line_number, lines)
|
117
|
+
cursor = starting_line_number
|
118
|
+
while cursor >= 0
|
119
|
+
unless lines[cursor].match?(/^#{indentation_of(starting_line_number, lines)}/) || !lines[cursor].present?
|
120
|
+
return cursor
|
121
|
+
end
|
122
|
+
cursor -= 1
|
123
|
+
end
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
114
127
|
def find_block_start(starting_string)
|
115
128
|
matcher = Regexp.escape(starting_string)
|
116
129
|
starting_line = 0
|
@@ -124,7 +137,15 @@ class Scaffolding::BlockManipulator
|
|
124
137
|
starting_line
|
125
138
|
end
|
126
139
|
|
127
|
-
def find_block_end(starting_from:)
|
140
|
+
def find_block_end(starting_from:, lines:)
|
141
|
+
# This loop was previously in the RoutesFileManipulator.
|
142
|
+
lines.each_with_index do |line, line_number|
|
143
|
+
next unless line_number > starting_from
|
144
|
+
if /^#{indentation_of(starting_from, lines)}end\s+/.match?(line)
|
145
|
+
return line_number
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
128
149
|
depth = 0
|
129
150
|
current_line = starting_from
|
130
151
|
@lines[starting_from..@lines.count].each_with_index do |line, index|
|
@@ -138,7 +159,12 @@ class Scaffolding::BlockManipulator
|
|
138
159
|
current_line
|
139
160
|
end
|
140
161
|
|
141
|
-
|
142
|
-
|
162
|
+
# TODO: We shouldn't need this second argument, but since
|
163
|
+
# we have `lines` here and in the RoutesFileManipulator,
|
164
|
+
# the lines diverge from one another when we edit them individually.
|
165
|
+
def indentation_of(line_number, lines)
|
166
|
+
lines[line_number].match(/^( +)/)[1]
|
167
|
+
rescue
|
168
|
+
nil
|
143
169
|
end
|
144
170
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "scaffolding/block_manipulator"
|
2
|
+
|
3
|
+
# TODO: If we move this and the BlockManipulator into their own gems,
|
4
|
+
# we can probably call these methods with something shorter without `Scaffolding::`.
|
5
|
+
module Scaffolding::FileManipulator
|
6
|
+
# TODO: The block_manipulator shouldn't be an instance variable.
|
7
|
+
def self.find(lines, needle, within = nil, block_manipulator)
|
8
|
+
lines_within(lines, within, block_manipulator).each_with_index do |line, line_number|
|
9
|
+
return (within + (within ? 1 : 0) + line_number) if line.match?(needle)
|
10
|
+
end
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
# TODO: The block_manipulator shouldn't be an instance variable.
|
15
|
+
def self.lines_within(lines, within, block_manipulator)
|
16
|
+
return lines unless within
|
17
|
+
lines[(within + 1)..(block_manipulator.find_block_end(starting_from: within, lines: lines) + 1)]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.replace_line_in_file(file, content, in_place_of)
|
21
|
+
target_file_content = File.read(file)
|
22
|
+
|
23
|
+
if target_file_content.include?(content)
|
24
|
+
puts "No need to update '#{file}'. It already has '#{content}'."
|
25
|
+
else
|
26
|
+
puts "Updating '#{file}'."
|
27
|
+
target_file_content.gsub!(in_place_of, content)
|
28
|
+
File.write(file, target_file_content)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Pass in an array where this content should be inserted within the yml file.
|
33
|
+
# For example, to add content to admin.models pass in [:admin, :models]
|
34
|
+
def self.add_line_to_yml_file(file, content, location_array)
|
35
|
+
# First check that the given location array actually exists in the yml file:
|
36
|
+
yml = YAML.safe_load(File.read(file))
|
37
|
+
location_array.map!(&:to_s)
|
38
|
+
|
39
|
+
# TODO: Raise an error if we're returning nil.
|
40
|
+
return nil if yml.dig(*location_array).nil?
|
41
|
+
|
42
|
+
content += "\n" unless content[-1] == "\n"
|
43
|
+
# Find the location in the file where the location_array is
|
44
|
+
lines = File.readlines(file)
|
45
|
+
current_needle = location_array.shift.to_s
|
46
|
+
current_space = ""
|
47
|
+
insert_after = 1
|
48
|
+
lines.each_with_index do |line, index|
|
49
|
+
break if current_needle.nil?
|
50
|
+
if line.strip == current_needle + ":"
|
51
|
+
current_needle = location_array.shift.to_s
|
52
|
+
insert_after = index
|
53
|
+
current_space = line.match(/\s+/).to_s
|
54
|
+
end
|
55
|
+
end
|
56
|
+
new_lines = []
|
57
|
+
current_space += " "
|
58
|
+
lines.each_with_index do |line, index|
|
59
|
+
new_lines << line
|
60
|
+
new_lines << current_space + content if index == insert_after
|
61
|
+
end
|
62
|
+
File.write(file, new_lines.join)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.write(file_name, lines)
|
66
|
+
puts "Updating '#{file_name}'."
|
67
|
+
File.open(file_name, "w+") do |file|
|
68
|
+
file.puts(lines.join.strip + "\n")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "scaffolding/block_manipulator"
|
2
|
+
|
1
3
|
class Scaffolding::RoutesFileManipulator
|
2
4
|
attr_accessor :child, :parent, :lines, :transformer_options, :block_manipulator
|
3
5
|
|
@@ -66,7 +68,7 @@ class Scaffolding::RoutesFileManipulator
|
|
66
68
|
def find_namespaces(namespaces, within = nil)
|
67
69
|
namespaces = namespaces.dup
|
68
70
|
results = {}
|
69
|
-
block_end = find_block_end(within) if within
|
71
|
+
block_end = block_manipulator.find_block_end(starting_from: within, lines: lines) if within
|
70
72
|
lines.each_with_index do |line, line_number|
|
71
73
|
if within
|
72
74
|
next unless line_number > within
|
@@ -80,47 +82,20 @@ class Scaffolding::RoutesFileManipulator
|
|
80
82
|
results
|
81
83
|
end
|
82
84
|
|
83
|
-
|
84
|
-
lines[line_number].match(/^( +)/)[1]
|
85
|
-
rescue
|
86
|
-
nil
|
87
|
-
end
|
88
|
-
|
89
|
-
def find_block_parent(starting_line_number)
|
90
|
-
return nil unless indentation_of(starting_line_number)
|
91
|
-
cursor = starting_line_number
|
92
|
-
while cursor >= 0
|
93
|
-
unless lines[cursor].match?(/^#{indentation_of(starting_line_number)}/) || !lines[cursor].present?
|
94
|
-
return cursor
|
95
|
-
end
|
96
|
-
cursor -= 1
|
97
|
-
end
|
98
|
-
nil
|
99
|
-
end
|
100
|
-
|
101
|
-
def find_block_end(starting_line_number)
|
102
|
-
return nil unless indentation_of(starting_line_number)
|
103
|
-
lines.each_with_index do |line, line_number|
|
104
|
-
next unless line_number > starting_line_number
|
105
|
-
if /^#{indentation_of(starting_line_number)}end\s+/.match?(line)
|
106
|
-
return line_number
|
107
|
-
end
|
108
|
-
end
|
109
|
-
nil
|
110
|
-
end
|
111
|
-
|
85
|
+
# TODO: Remove this and use the BlockManipulator
|
112
86
|
def insert_before(new_lines, line_number, options = {})
|
113
87
|
options[:indent] ||= false
|
114
88
|
before = lines[0..(line_number - 1)]
|
115
|
-
new_lines = new_lines.map { |line| (indentation_of(line_number) + (options[:indent] ? " " : "") + line).gsub(/\s+$/, "") + "\n" }
|
89
|
+
new_lines = new_lines.map { |line| (block_manipulator.indentation_of(line_number, lines) + (options[:indent] ? " " : "") + line).gsub(/\s+$/, "") + "\n" }
|
116
90
|
after = lines[line_number..]
|
117
91
|
self.lines = before + (options[:prepend_newline] ? ["\n"] : []) + new_lines + after
|
118
92
|
end
|
119
93
|
|
94
|
+
# TODO: Remove this and use the BlockManipulator
|
120
95
|
def insert_after(new_lines, line_number, options = {})
|
121
96
|
options[:indent] ||= false
|
122
97
|
before = lines[0..line_number]
|
123
|
-
new_lines = new_lines.map { |line| (indentation_of(line_number) + (options[:indent] ? " " : "") + line).gsub(/\s+$/, "") + "\n" }
|
98
|
+
new_lines = new_lines.map { |line| (block_manipulator.indentation_of(line_number, lines) + (options[:indent] ? " " : "") + line).gsub(/\s+$/, "") + "\n" }
|
124
99
|
after = lines[(line_number + 1)..]
|
125
100
|
self.lines = before + new_lines + (options[:append_newline] ? ["\n"] : []) + after
|
126
101
|
end
|
@@ -129,7 +104,7 @@ class Scaffolding::RoutesFileManipulator
|
|
129
104
|
namespace_lines = find_namespaces(namespaces, within)
|
130
105
|
if namespace_lines[namespaces.last]
|
131
106
|
block_start = namespace_lines[namespaces.last]
|
132
|
-
insertion_point = find_block_end(block_start)
|
107
|
+
insertion_point = block_manipulator.find_block_end(starting_from: block_start, lines: lines)
|
133
108
|
insert_before(new_lines, insertion_point, indent: true, prepend_newline: (insertion_point > block_start + 1))
|
134
109
|
else
|
135
110
|
raise "we weren't able to insert the following lines into the namespace block for #{namespaces.join(" -> ")}:\n\n#{new_lines.join("\n")}"
|
@@ -191,21 +166,13 @@ class Scaffolding::RoutesFileManipulator
|
|
191
166
|
namespace_block_start.present? ? {namespace => namespace_block_start} : {}
|
192
167
|
end
|
193
168
|
|
194
|
-
def find(needle, within = nil)
|
195
|
-
lines_within(within).each_with_index do |line, line_number|
|
196
|
-
return (within + (within ? 1 : 0) + line_number) if line.match?(needle)
|
197
|
-
end
|
198
|
-
|
199
|
-
nil
|
200
|
-
end
|
201
|
-
|
202
169
|
def find_in_namespace(needle, namespaces, within = nil, ignore = nil)
|
203
170
|
if namespaces.any?
|
204
171
|
namespace_lines = find_namespaces(namespaces, within)
|
205
172
|
within = namespace_lines[namespaces.last]
|
206
173
|
end
|
207
174
|
|
208
|
-
lines_within(within).each_with_index do |line, line_number|
|
175
|
+
Scaffolding::FileManipulator.lines_within(lines, within, block_manipulator).each_with_index do |line, line_number|
|
209
176
|
# + 2 because line_number starts from 0, and within starts one line after
|
210
177
|
actual_line_number = (within + line_number + 2)
|
211
178
|
|
@@ -259,7 +226,7 @@ class Scaffolding::RoutesFileManipulator
|
|
259
226
|
# However, will not find namespace blocks inside namespace blocks.
|
260
227
|
def top_level_namespace_block_lines(within)
|
261
228
|
local_namespace_blocks = []
|
262
|
-
lines_within(within).each do |line|
|
229
|
+
Scaffolding::FileManipulator.lines_within(lines, within, block_manipulator).each do |line|
|
263
230
|
# i.e. - Retrieve "foo" from "namespace :foo do"
|
264
231
|
match_data = line.match(/(\s*namespace\s:)(.*)(\sdo$)/)
|
265
232
|
|
@@ -269,7 +236,7 @@ class Scaffolding::RoutesFileManipulator
|
|
269
236
|
namespace_name = match_data[2]
|
270
237
|
local_namespace = find_namespaces([namespace_name], within)
|
271
238
|
starting_line_number = local_namespace[namespace_name]
|
272
|
-
local_namespace_block = ((starting_line_number + 1)..(find_block_end(starting_line_number) + 1))
|
239
|
+
local_namespace_block = ((starting_line_number + 1)..(block_manipulator.find_block_end(starting_from: starting_line_number, lines: lines) + 1))
|
273
240
|
|
274
241
|
if local_namespace_blocks.empty?
|
275
242
|
local_namespace_blocks << local_namespace_block
|
@@ -296,11 +263,11 @@ class Scaffolding::RoutesFileManipulator
|
|
296
263
|
def namespace_blocks_directly_under_parent(within)
|
297
264
|
blocks = []
|
298
265
|
if lines[within].match?(/do$/)
|
299
|
-
parent_indentation_size = block_manipulator.
|
300
|
-
within_block_end = find_block_end(within)
|
266
|
+
parent_indentation_size = block_manipulator.indentation_of(within, lines).length
|
267
|
+
within_block_end = block_manipulator.find_block_end(starting_from: within, lines: lines)
|
301
268
|
within.upto(within_block_end) do |line_number|
|
302
269
|
if lines[line_number].match?(/^#{" " * (parent_indentation_size + 2)}namespace/)
|
303
|
-
namespace_block_lines = line_number..find_block_end(line_number)
|
270
|
+
namespace_block_lines = line_number..block_manipulator.find_block_end(starting_from: line_number, lines: lines)
|
304
271
|
blocks << namespace_block_lines
|
305
272
|
end
|
306
273
|
end
|
@@ -313,11 +280,6 @@ class Scaffolding::RoutesFileManipulator
|
|
313
280
|
find_or_convert_resource_block(parts.last, options)
|
314
281
|
end
|
315
282
|
|
316
|
-
def lines_within(within)
|
317
|
-
return lines unless within
|
318
|
-
lines[(within + 1)..(find_block_end(within) + 1)]
|
319
|
-
end
|
320
|
-
|
321
283
|
def find_or_convert_resource_block(parent_resource, options = {})
|
322
284
|
unless find_resource_block([parent_resource], options)
|
323
285
|
if (resource_line_number = find_resource([parent_resource], options))
|
@@ -337,8 +299,9 @@ class Scaffolding::RoutesFileManipulator
|
|
337
299
|
within
|
338
300
|
end
|
339
301
|
|
302
|
+
# TODO: Remove this and use the BlockManipulator
|
340
303
|
def insert(lines_to_add, within)
|
341
|
-
insertion_line = find_block_end(within)
|
304
|
+
insertion_line = block_manipulator.find_block_end(starting_from: within, lines: lines)
|
342
305
|
result_line = insertion_line
|
343
306
|
unless insertion_line == within + 1
|
344
307
|
# only put the extra space if we're adding this line after a block
|
@@ -370,7 +333,7 @@ class Scaffolding::RoutesFileManipulator
|
|
370
333
|
# add the new resource within that namespace.
|
371
334
|
line = "scope module: '#{parent_resource}' do"
|
372
335
|
# TODO you haven't tested this yet.
|
373
|
-
unless (scope_within = find(/#{line}/, parent_within))
|
336
|
+
unless (scope_within = Scaffolding::FileManipulator.find(lines, /#{line}/, parent_within, block_manipulator))
|
374
337
|
scope_within = insert([line, "end"], parent_within)
|
375
338
|
end
|
376
339
|
|
@@ -382,7 +345,7 @@ class Scaffolding::RoutesFileManipulator
|
|
382
345
|
|
383
346
|
# We want to see if there are any namespaces one level above the parent itself,
|
384
347
|
# because namespaces with the same name as the resource can exist on the same level.
|
385
|
-
parent_block_start = find_block_parent(parent_within)
|
348
|
+
parent_block_start = block_manipulator.find_block_parent(parent_within, lines)
|
386
349
|
namespace_line_within = find_or_create_namespaces(child_namespaces, parent_block_start)
|
387
350
|
find_or_create_resource([child_resource], options: "except: collection_actions", within: namespace_line_within)
|
388
351
|
unless find_namespaces(child_namespaces, within)[child_namespaces.last]
|
@@ -401,7 +364,7 @@ class Scaffolding::RoutesFileManipulator
|
|
401
364
|
# resources :projects_deliverables, path: 'projects/deliverables' do
|
402
365
|
# resources :objectives
|
403
366
|
# end
|
404
|
-
block_parent_within = find_block_parent(top_parent_namespace)
|
367
|
+
block_parent_within = block_manipulator.find_block_parent(top_parent_namespace, lines)
|
405
368
|
parent_namespaces_and_resource = (parent_namespaces + [parent_resource]).join("_")
|
406
369
|
parent_within = find_or_create_resource_block([parent_namespaces_and_resource], options: "path: '#{parent_namespaces_and_resource.tr("_", "/")}'", within: block_parent_within)
|
407
370
|
find_or_create_resource(child_namespaces + [child_resource], within: parent_within)
|
@@ -426,11 +389,4 @@ class Scaffolding::RoutesFileManipulator
|
|
426
389
|
return if concerns.empty?
|
427
390
|
"concerns: #{concerns}"
|
428
391
|
end
|
429
|
-
|
430
|
-
def write
|
431
|
-
puts "Updating '#{@filename}'."
|
432
|
-
File.open(@filename, "w+") do |file|
|
433
|
-
file.puts(lines.join.strip + "\n")
|
434
|
-
end
|
435
|
-
end
|
436
392
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "indefinite_article"
|
2
2
|
require "yaml"
|
3
|
+
require "scaffolding/file_manipulator"
|
3
4
|
require "scaffolding/class_names_transformer"
|
4
5
|
|
5
6
|
class Scaffolding::Transformer
|
@@ -20,6 +21,7 @@ class Scaffolding::Transformer
|
|
20
21
|
RUBY_NEW_FIELDS_HOOK = "# 🚅 super scaffolding will insert new fields above this line."
|
21
22
|
RUBY_ADDITIONAL_NEW_FIELDS_HOOK = "# 🚅 super scaffolding will also insert new fields above this line."
|
22
23
|
RUBY_EVEN_MORE_NEW_FIELDS_HOOK = "# 🚅 super scaffolding will additionally insert new fields above this line."
|
24
|
+
RUBY_FILES_HOOK = "# 🚅 super scaffolding will insert file-related logic above this line."
|
23
25
|
ENDPOINTS_HOOK = "# 🚅 super scaffolding will mount new endpoints above this line."
|
24
26
|
ERB_NEW_FIELDS_HOOK = "<%#{RUBY_NEW_FIELDS_HOOK} %>"
|
25
27
|
CONCERNS_HOOK = "# 🚅 add concerns above."
|
@@ -235,36 +237,6 @@ class Scaffolding::Transformer
|
|
235
237
|
end
|
236
238
|
end
|
237
239
|
|
238
|
-
# pass in an array where this content should be inserted within the yml file. For example, to add content
|
239
|
-
# to admin.models pass in [:admin, :models]
|
240
|
-
def add_line_to_yml_file(file, content, location_array)
|
241
|
-
# First check that the given location array actually exists in the yml file:
|
242
|
-
yml = YAML.safe_load(File.read(file))
|
243
|
-
location_array.map!(&:to_s)
|
244
|
-
return nil if yml.dig(*location_array).nil? # Should we raise an error?
|
245
|
-
content += "\n" unless content[-1] == "\n"
|
246
|
-
# Find the location in the file where the location_array is
|
247
|
-
lines = File.readlines(file)
|
248
|
-
current_needle = location_array.shift.to_s
|
249
|
-
current_space = ""
|
250
|
-
insert_after = 1
|
251
|
-
lines.each_with_index do |line, index|
|
252
|
-
break if current_needle.nil?
|
253
|
-
if line.strip == current_needle + ":"
|
254
|
-
current_needle = location_array.shift.to_s
|
255
|
-
insert_after = index
|
256
|
-
current_space = line.match(/\s+/).to_s
|
257
|
-
end
|
258
|
-
end
|
259
|
-
new_lines = []
|
260
|
-
current_space += " "
|
261
|
-
lines.each_with_index do |line, index|
|
262
|
-
new_lines << line
|
263
|
-
new_lines << current_space + content if index == insert_after
|
264
|
-
end
|
265
|
-
File.write(file, new_lines.join)
|
266
|
-
end
|
267
|
-
|
268
240
|
def add_line_to_file(file, content, hook, options = {})
|
269
241
|
increase_indent = options[:increase_indent]
|
270
242
|
add_before = options[:add_before]
|
@@ -346,23 +318,11 @@ class Scaffolding::Transformer
|
|
346
318
|
add_line_to_file(file, content, hook, options)
|
347
319
|
end
|
348
320
|
|
349
|
-
def replace_line_in_file(file, content, in_place_of)
|
350
|
-
target_file_content = File.read(file)
|
351
|
-
|
352
|
-
if target_file_content.include?(content)
|
353
|
-
puts "No need to update '#{file}'. It already has '#{content}'."
|
354
|
-
else
|
355
|
-
puts "Updating '#{file}'."
|
356
|
-
target_file_content.gsub!(in_place_of, content)
|
357
|
-
File.write(file, target_file_content)
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
321
|
def scaffold_replace_line_in_file(file, content, in_place_of)
|
362
322
|
file = transform_string(file)
|
363
323
|
# we specifically don't transform the content, we assume a builder function created this content.
|
364
324
|
in_place_of = transform_string(in_place_of)
|
365
|
-
replace_line_in_file(file, content, in_place_of)
|
325
|
+
Scaffolding::FileManipulator.replace_line_in_file(file, content, in_place_of)
|
366
326
|
end
|
367
327
|
|
368
328
|
# if class_name isn't specified, we use `child`.
|
@@ -451,8 +411,8 @@ class Scaffolding::Transformer
|
|
451
411
|
model_names = class_names || [child]
|
452
412
|
role_file = "./config/models/roles.yml"
|
453
413
|
model_names.each do |model_name|
|
454
|
-
add_line_to_yml_file(role_file, "#{model_name}: read", [:default, :models])
|
455
|
-
add_line_to_yml_file(role_file, "#{model_name}: manage", [:admin, :models])
|
414
|
+
Scaffolding::FileManipulator.add_line_to_yml_file(role_file, "#{model_name}: read", [:default, :models])
|
415
|
+
Scaffolding::FileManipulator.add_line_to_yml_file(role_file, "#{model_name}: manage", [:admin, :models])
|
456
416
|
end
|
457
417
|
end
|
458
418
|
|
@@ -480,7 +440,7 @@ class Scaffolding::Transformer
|
|
480
440
|
current_parent = working_parents.pop
|
481
441
|
end
|
482
442
|
|
483
|
-
setup_lines << current_transformer.transform_string("@tangible_thing =
|
443
|
+
setup_lines << current_transformer.transform_string("@tangible_thing = build(:scaffolding_completely_concrete_tangible_thing, #{previous_assignment})")
|
484
444
|
|
485
445
|
setup_lines
|
486
446
|
end
|
@@ -615,7 +575,9 @@ class Scaffolding::Transformer
|
|
615
575
|
is_id = name.match?(/_id$/)
|
616
576
|
is_ids = name.match?(/_ids$/)
|
617
577
|
# if this is the first attribute of a newly scaffolded model, that field is required.
|
618
|
-
|
578
|
+
unless type == "file_field"
|
579
|
+
is_required = attribute_options[:required] || (scaffolding_options[:type] == :crud && index == 0)
|
580
|
+
end
|
619
581
|
is_vanilla = attribute_options&.key?(:vanilla)
|
620
582
|
is_belongs_to = is_id && !is_vanilla
|
621
583
|
is_has_many = is_ids && !is_vanilla
|
@@ -653,6 +615,8 @@ class Scaffolding::Transformer
|
|
653
615
|
"date_and_time"
|
654
616
|
when "email_field"
|
655
617
|
"email"
|
618
|
+
when "emoji_field"
|
619
|
+
"text"
|
656
620
|
when "color_picker"
|
657
621
|
"code"
|
658
622
|
when "text_field"
|
@@ -660,7 +624,7 @@ class Scaffolding::Transformer
|
|
660
624
|
when "text_area"
|
661
625
|
"text"
|
662
626
|
when "file_field"
|
663
|
-
"
|
627
|
+
"file"
|
664
628
|
else
|
665
629
|
raise "Invalid attribute type: #{type}."
|
666
630
|
end
|
@@ -765,16 +729,21 @@ class Scaffolding::Transformer
|
|
765
729
|
# field on the form.
|
766
730
|
field_attributes = {method: ":#{name}"}
|
767
731
|
field_options = {}
|
732
|
+
options = {}
|
768
733
|
|
769
734
|
if scaffolding_options[:type] == :crud && index == 0
|
770
735
|
field_options[:autofocus] = "true"
|
771
736
|
end
|
772
737
|
|
773
738
|
if is_id && type == "super_select"
|
774
|
-
|
739
|
+
options[:include_blank] = "t('.fields.#{name}.placeholder')"
|
775
740
|
# add_additional_step :yellow, transform_string("We've added a reference to a `placeholder` to the form for the select or super_select field, but unfortunately earlier versions of the scaffolded locales Yaml don't include a reference to `fields: *fields` under `form`. Please add it, otherwise your form won't be able to locate the appropriate placeholder label.")
|
776
741
|
end
|
777
742
|
|
743
|
+
if type == "color_picker"
|
744
|
+
field_options[:color_picker_options] = "t('#{child.pluralize.underscore}.fields.color_picker_value.options')"
|
745
|
+
end
|
746
|
+
|
778
747
|
# TODO: This feels incorrect.
|
779
748
|
# Should we adjust the partials to only use `{multiple: true}` or `html_options: {multiple_true}`?
|
780
749
|
if is_multiple
|
@@ -792,12 +761,19 @@ class Scaffolding::Transformer
|
|
792
761
|
end
|
793
762
|
|
794
763
|
# https://stackoverflow.com/questions/21582464/is-there-a-ruby-hashto-s-equivalent-for-the-new-hash-syntax
|
795
|
-
if field_options.any?
|
764
|
+
if field_options.any? || options.any?
|
796
765
|
field_options_key = if ["buttons", "super_select", "options"].include?(type)
|
766
|
+
if options.any?
|
767
|
+
field_attributes[:options] = "{" + field_options.map { |key, value| "#{key}: #{value}" }.join(", ") + "}"
|
768
|
+
end
|
769
|
+
|
797
770
|
:html_options
|
798
771
|
else
|
772
|
+
field_options.merge!(options)
|
773
|
+
|
799
774
|
:options
|
800
775
|
end
|
776
|
+
|
801
777
|
field_attributes[field_options_key] = "{" + field_options.map { |key, value| "#{key}: #{value}" }.join(", ") + "}"
|
802
778
|
end
|
803
779
|
|
@@ -1034,7 +1010,7 @@ class Scaffolding::Transformer
|
|
1034
1010
|
# TODO The serializers can't handle these `has_rich_text` attributes.
|
1035
1011
|
unless type == "trix_editor"
|
1036
1012
|
scaffold_add_line_to_file("./app/views/account/scaffolding/completely_concrete/tangible_things/_tangible_thing.json.jbuilder", ":#{name},", RUBY_NEW_FIELDS_HOOK, prepend: true, suppress_could_not_find: true)
|
1037
|
-
scaffold_add_line_to_file("./app/serializers/api/v1/scaffolding/completely_concrete/tangible_thing_serializer.rb", ":#{name},", RUBY_NEW_FIELDS_HOOK, prepend: true)
|
1013
|
+
scaffold_add_line_to_file("./app/serializers/api/v1/scaffolding/completely_concrete/tangible_thing_serializer.rb", ":#{name},", RUBY_NEW_FIELDS_HOOK, prepend: true) unless type == "file_field"
|
1038
1014
|
|
1039
1015
|
assertion = case type
|
1040
1016
|
when "date_field"
|
@@ -1043,13 +1019,31 @@ class Scaffolding::Transformer
|
|
1043
1019
|
"assert_equal DateTime.parse(tangible_thing_data['#{name}']), tangible_thing.#{name}"
|
1044
1020
|
when "file_field"
|
1045
1021
|
# TODO: If we want to use Cloudinary to handle our files, we should make sure we're getting a URL.
|
1046
|
-
"
|
1022
|
+
"assert tangible_thing_data['#{name}'].match?('foo.txt') unless response.status == 201"
|
1047
1023
|
else
|
1048
1024
|
"assert_equal tangible_thing_data['#{name}'], tangible_thing.#{name}"
|
1049
1025
|
end
|
1050
1026
|
scaffold_add_line_to_file("./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_endpoint_test.rb", assertion, RUBY_NEW_FIELDS_HOOK, prepend: true)
|
1051
1027
|
end
|
1052
1028
|
|
1029
|
+
# File fields are handled in a specific way when using the jsonapi-serializer.
|
1030
|
+
if type == "file_field"
|
1031
|
+
file_name = "./app/serializers/api/v1/scaffolding/completely_concrete/tangible_thing_serializer.rb"
|
1032
|
+
content = <<~RUBY
|
1033
|
+
attribute :#{name} do |object|
|
1034
|
+
rails_blob_path(object.#{name}, disposition: "attachment", only_path: true) if object.#{name}.attached?
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
RUBY
|
1038
|
+
hook = RUBY_FILES_HOOK
|
1039
|
+
scaffold_add_line_to_file(file_name, content, hook, prepend: true)
|
1040
|
+
|
1041
|
+
# We also want to make sure we attach the dummy file in the endpoint test on setup
|
1042
|
+
file_name = "./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_endpoint_test.rb"
|
1043
|
+
content = "@#{child.underscore}.#{name} = Rack::Test::UploadedFile.new(\"test/support/foo.txt\")"
|
1044
|
+
scaffold_add_line_to_file(file_name, content, hook, prepend: true)
|
1045
|
+
end
|
1046
|
+
|
1053
1047
|
# scaffold_add_line_to_file("./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_controller_test.rb", "assert_equal tangible_thing_attributes['#{name.gsub('_', '-')}'], tangible_thing.#{name}", RUBY_NEW_FIELDS_HOOK, prepend: true)
|
1054
1048
|
|
1055
1049
|
if attribute_assignment
|
@@ -1291,15 +1285,6 @@ class Scaffolding::Transformer
|
|
1291
1285
|
|
1292
1286
|
add_has_many_association
|
1293
1287
|
|
1294
|
-
# Adds file attachment to factory
|
1295
|
-
attributes.each do |attribute|
|
1296
|
-
attribute_name, partial_type = attribute.split(":")
|
1297
|
-
if partial_type == "file_field"
|
1298
|
-
content = "#{attribute_name} { Rack::Test::UploadedFile.new(\"test/support/foo.txt\") }"
|
1299
|
-
scaffold_replace_line_in_file("./test/factories/scaffolding/completely_concrete/tangible_things.rb", content, "#{attribute_name} { nil }")
|
1300
|
-
end
|
1301
|
-
end
|
1302
|
-
|
1303
1288
|
if class_names_transformer.belongs_to_needs_class_definition?
|
1304
1289
|
scaffold_replace_line_in_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", transform_string("belongs_to :absolutely_abstract_creative_concept, class_name: \"Scaffolding::AbsolutelyAbstract::CreativeConcept\"\n"), transform_string("belongs_to :absolutely_abstract_creative_concept\n"))
|
1305
1290
|
end
|
@@ -1347,7 +1332,7 @@ class Scaffolding::Transformer
|
|
1347
1332
|
end
|
1348
1333
|
|
1349
1334
|
unless cli_options["skip-table"]
|
1350
|
-
scaffold_replace_line_in_file("./app/views/account/scaffolding/completely_concrete/tangible_things/_index.html.erb", transform_string("<tbody data-reorder=\"<%= url_for [:reorder, :account, context, collection] %>\">"), "<tbody>")
|
1335
|
+
scaffold_replace_line_in_file("./app/views/account/scaffolding/completely_concrete/tangible_things/_index.html.erb", transform_string("<tbody data-controller=\"sortable\" data-sortable-reorder-path-value=\"<%= url_for [:reorder, :account, context, collection] %>\">"), "<tbody>")
|
1351
1336
|
end
|
1352
1337
|
|
1353
1338
|
unless cli_options["skip-controller"]
|
@@ -1401,7 +1386,7 @@ class Scaffolding::Transformer
|
|
1401
1386
|
add_additional_step :yellow, "We weren't able to automatically add your `#{routes_namespace}` routes for you. In theory this should be very rare, so if you could reach out on Slack, you could probably provide context that will help us fix whatever the problem was. In the meantime, to add the routes manually, we've got a guide at https://blog.bullettrain.co/nested-namespaced-rails-routing-examples/ ."
|
1402
1387
|
end
|
1403
1388
|
|
1404
|
-
routes_manipulator.
|
1389
|
+
Scaffolding::FileManipulator.write("config/routes.rb", routes_manipulator.lines)
|
1405
1390
|
end
|
1406
1391
|
|
1407
1392
|
unless cli_options["skip-parent"]
|
@@ -1412,13 +1397,18 @@ class Scaffolding::Transformer
|
|
1412
1397
|
icon_name = cli_options["sidebar"]
|
1413
1398
|
else
|
1414
1399
|
puts ""
|
1415
|
-
puts "Hey, models that are scoped directly off of a Team (or nothing) are eligible to be added to the sidebar.
|
1400
|
+
puts "Hey, models that are scoped directly off of a Team (or nothing) are eligible to be added to the sidebar."
|
1401
|
+
puts "Do you want to add this resource to the sidebar menu? (y/N)"
|
1416
1402
|
response = $stdin.gets.chomp
|
1417
1403
|
if response.downcase[0] == "y"
|
1418
1404
|
puts ""
|
1419
|
-
puts "OK, great! Let's do this! By default these menu items appear
|
1405
|
+
puts "OK, great! Let's do this! By default these menu items appear as a #{font_awesome? ? "puzzle piece" : "gift icon"},"
|
1406
|
+
puts "but after you hit enter I'll open #{font_awesome? ? "two different pages" : "a page"} where you can view other icon options."
|
1407
|
+
puts "When you find one you like, hover your mouse over it and then come back here and"
|
1408
|
+
puts "enter the name of the icon you want to use."
|
1409
|
+
puts "(Or hit enter when choosing to skip this step.)"
|
1420
1410
|
$stdin.gets.chomp
|
1421
|
-
if `which open`.present?
|
1411
|
+
if (TerminalCommands.macosx? && `which open`.present?) || TerminalCommands.linux?
|
1422
1412
|
TerminalCommands.open_file_or_link("https://themify.me/themify-icons")
|
1423
1413
|
if font_awesome?
|
1424
1414
|
TerminalCommands.open_file_or_link("https://fontawesome.com/icons?d=gallery&s=light")
|
@@ -1433,8 +1423,18 @@ class Scaffolding::Transformer
|
|
1433
1423
|
puts ""
|
1434
1424
|
end
|
1435
1425
|
puts ""
|
1436
|
-
|
1437
|
-
|
1426
|
+
|
1427
|
+
loop do
|
1428
|
+
puts "Did you find an icon you wanted to use?"
|
1429
|
+
puts "Enter the full CSS class here (e.g. 'ti ti-world'#{" or 'fal fa-puzzle-piece'" if font_awesome?}) or hit enter to just use the #{font_awesome? ? "puzzle piece" : "gift icon"}:"
|
1430
|
+
icon_name = $stdin.gets.chomp
|
1431
|
+
unless icon_name.match?(/ti\s.*/) || icon_name.match?(/fal\s.*/) || icon_name.strip.empty?
|
1432
|
+
puts ""
|
1433
|
+
puts "Please enter the full CSS class or hit enter."
|
1434
|
+
next
|
1435
|
+
end
|
1436
|
+
break
|
1437
|
+
end
|
1438
1438
|
puts ""
|
1439
1439
|
unless icon_name.length > 0 || icon_name.downcase == "y"
|
1440
1440
|
icon_name = "fal fa-puzzle-piece ti ti-gift"
|
data/lib/scaffolding.rb
CHANGED
@@ -2,12 +2,13 @@ module Scaffolding
|
|
2
2
|
def self.valid_attribute_type?(type)
|
3
3
|
[
|
4
4
|
"boolean",
|
5
|
-
"
|
5
|
+
"buttons",
|
6
6
|
"cloudinary_image",
|
7
7
|
"color_picker",
|
8
8
|
"date_and_time_field",
|
9
9
|
"date_field",
|
10
10
|
"email_field",
|
11
|
+
"emoji_field",
|
11
12
|
"file_field",
|
12
13
|
"options",
|
13
14
|
"password_field",
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet_train-super_scaffolding
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.35
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Culver
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-07-
|
11
|
+
date: 2022-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: standard
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- lib/scaffolding.rb
|
167
167
|
- lib/scaffolding/block_manipulator.rb
|
168
168
|
- lib/scaffolding/class_names_transformer.rb
|
169
|
+
- lib/scaffolding/file_manipulator.rb
|
169
170
|
- lib/scaffolding/oauth_providers.rb
|
170
171
|
- lib/scaffolding/routes_file_manipulator.rb
|
171
172
|
- lib/scaffolding/script.rb
|