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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f51f2db556dd32be5a81cd6fcf0fc54cca58cd662e61da38289a95a8aecb6d64
4
- data.tar.gz: '03957b2a34172dabb98a2ef32fdcb16d3be71eb65963546b746826915e74d4be'
3
+ metadata.gz: 329a1d276e3bc2b2a89c230d35b8ea6613abc957d34de2319455458e88658ca2
4
+ data.tar.gz: 27994643a5a47719703a204d849732a9c67382fab553a4decad9984b2b5f08d0
5
5
  SHA512:
6
- metadata.gz: 8f692ccec6f4cebec181c086562d5b1c5bf59a6e2106cd12fff70df47addf7043157de95d89c5c637068826068e90349a5a5f07acac79c85b8a3ed4f1c63c2bc
7
- data.tar.gz: ce76ef0471d6f6f7721c70bcf4645a99ecd8866ece6d48ab3809601a162579f5df90cf5042cce74b9f7ba6dc9e3c3dada590013ee9b614132035ef6fc2108479
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 %>
@@ -1,5 +1,5 @@
1
1
  module BulletTrain
2
2
  module SuperScaffolding
3
- VERSION = "1.0.32"
3
+ VERSION = "1.0.35"
4
4
  end
5
5
  end
@@ -7,16 +7,16 @@ class Scaffolding::BlockManipulator
7
7
  end
8
8
 
9
9
  #
10
- # Wrap a block of ruby code inside another block
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
- def block_indentation_size(line_number)
142
- lines[line_number].scan(/^\s+/).first.size
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
- def indentation_of(line_number)
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.block_indentation_size(within)
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 = create(:scaffolding_completely_concrete_tangible_thing, #{previous_assignment})")
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
- is_required = attribute_options[:required] || (scaffolding_options[:type] == :crud && index == 0)
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
- "text"
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
- field_options[:include_blank] = "t('.fields.#{name}.placeholder')"
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
- "assert_equal tangible_thing_data['#{name}']['record']['id'], tangible_thing.#{name}.record.id"
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.write
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. Do you want to add this resource to the sidebar menu? (y/N)"
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 with a puzzle piece, but after you hit enter I'll open two different pages where you can view other icon options. When you find one you like, hover your mouse over it and then come back here and and enter the name of the icon you want to use. (Or hit enter to skip this step.)"
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
- puts "Did you find an icon you wanted to use? 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 puzzle piece:"
1437
- icon_name = $stdin.gets.chomp
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
- "button",
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.32
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-02 00:00:00.000000000 Z
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