bullet_train-super_scaffolding 1.3.20 → 1.3.22

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: e630cb440a6fca50b4cdf0b905cddd1732fe1966c9c79a6b6a55d28095936c21
4
- data.tar.gz: c080a11f2b8452ef7864fb8f573a0b93482fe30660503e386815d73f9de5e622
3
+ metadata.gz: e9210f83ebe43b35a2f22c251699259b4c019dda13edfd97ee8b86a36b1c4076
4
+ data.tar.gz: 377c1286afbd0dc549bd606abaf4f4c5eca77461c80586344ebf0cb7905db1fd
5
5
  SHA512:
6
- metadata.gz: 24e161b7f3a2ce0be67fbf4c58327990d7b29d57bee0036da2324bff3017aa398438758c9b6894628e680e56e69eb73400b4288756a03b2129ed7d4faacb5e9a
7
- data.tar.gz: c728ddd209bb35a33ea30bdad45363e9e20223564fca6f6b9812025696ecf4cba41e55550c77ba29b8e153b7533f4604b4d32c9dab8c593f3738163afced6a70
6
+ metadata.gz: df76ecad025409b2d23c5140f5d7b05710bcfd8ac8723c2f6dab2055d83d37f301a17224a9280313829f517a514004b5ecb78837474642365dde18da9f293494
7
+ data.tar.gz: 135cb316d50b1d502e2d8803e33de8181252efb6672778107f00e066807e2324668e4f0e1c2f35a1ccdbbb32f3fa23289203ec3cb0098abe6c4593f2db434bca
@@ -15,10 +15,16 @@ class Account::Scaffolding::CompletelyConcrete::TangibleThingsController < Accou
15
15
 
16
16
  # GET /account/scaffolding/absolutely_abstract/creative_concepts/:absolutely_abstract_creative_concept_id/completely_concrete/tangible_things/new
17
17
  def new
18
+ # 🚅 skip this section when scaffolding.
19
+ @tangible_thing.address_value = Address.new
20
+ # 🚅 stop any skipping we're doing now.
18
21
  end
19
22
 
20
23
  # GET /account/scaffolding/completely_concrete/tangible_things/:id/edit
21
24
  def edit
25
+ # 🚅 skip this section when scaffolding.
26
+ @tangible_thing.address_value ||= Address.new
27
+ # 🚅 stop any skipping we're doing now.
22
28
  end
23
29
 
24
30
  # POST /account/scaffolding/absolutely_abstract/creative_concepts/:absolutely_abstract_creative_concept_id/completely_concrete/tangible_things
@@ -67,6 +67,16 @@ if defined?(Api::V1::ApplicationController)
67
67
  # 🚅 skip this section when scaffolding.
68
68
  multiple_button_values: [],
69
69
  multiple_option_values: [],
70
+ address_value_attributes: [
71
+ :id,
72
+ :address_one,
73
+ :address_two,
74
+ :city,
75
+ :region_id,
76
+ :region_name,
77
+ :country_id,
78
+ :postal_code
79
+ ],
70
80
  multiple_super_select_values: []
71
81
  # 🚅 stop any skipping we're doing now.
72
82
  # 🚅 super scaffolding will insert new arrays above this line.
@@ -13,7 +13,10 @@ class Scaffolding::CompletelyConcrete::TangibleThing < ApplicationRecord
13
13
  # 🚅 add oauth providers above.
14
14
 
15
15
  has_one_attached :file_field_value
16
+
16
17
  has_one :team, through: :absolutely_abstract_creative_concept
18
+ has_one :address_value, class_name: "Address", as: :addressable
19
+ accepts_nested_attributes_for :address_value
17
20
  # 🚅 add has_one associations above.
18
21
 
19
22
  # 🚅 add scopes above.
@@ -49,6 +49,8 @@
49
49
  <%= render 'shared/fields/super_select', method: :multiple_super_select_values, other_options: {search: true}, html_options: {multiple: true} %>
50
50
  </div>
51
51
  </div>
52
+
53
+ <%= render 'shared/fields/address_field', method: :address_value %>
52
54
 
53
55
  <%= render 'shared/fields/text_area', method: :text_area_value %>
54
56
  <%= render 'shared/fields/trix_editor', method: :action_text_value %>
@@ -23,6 +23,7 @@
23
23
  <%= render 'shared/attributes/email', attribute: :email_field_value %>
24
24
  <%= render 'shared/attributes/text', attribute: :password_field_value %>
25
25
  <%= render 'shared/attributes/phone_number', attribute: :phone_field_value %>
26
+ <%= render 'shared/attributes/address', attribute: :address_value %>
26
27
  <%= render 'shared/attributes/option', attribute: :option_value %>
27
28
  <%= render 'shared/attributes/options', attribute: :multiple_option_values %>
28
29
  <%= render 'shared/attributes/option', attribute: :super_select_value %>
@@ -95,6 +95,10 @@ en:
95
95
  _: &phone_field_value Phone Field Value
96
96
  label: *phone_field_value
97
97
  heading: *phone_field_value
98
+ address_value:
99
+ _: &address_value Address Value
100
+ label: *address_value
101
+ heading: *address_value
98
102
  option_value:
99
103
  _: &option_value Option Value
100
104
  label: *option_value
@@ -215,6 +219,7 @@ en:
215
219
  file_field_value: *file_field_value
216
220
  password_field_value: *password_field_value
217
221
  phone_field_value: *phone_field_value
222
+ address_value: *address_value
218
223
  option_value: *option_value
219
224
  multiple_option_values: *multiple_option_values
220
225
  super_select_value: *super_select_value
@@ -1,5 +1,5 @@
1
1
  module BulletTrain
2
2
  module SuperScaffolding
3
- VERSION = "1.3.20"
3
+ VERSION = "1.3.22"
4
4
  end
5
5
  end
@@ -0,0 +1,214 @@
1
+ # This class provides helpful methods for determining when and how we
2
+ # should apply logic for each attribute when Super Scaffolding a new model.
3
+ #
4
+ # For example, we determine which association to use based off of the
5
+ # attribute passed to `bin/super-scaffold` as opposed to the models (classes) themselves.
6
+ # Rails has ActiveRecord::Reflection::AssociationReflection, but this is only useful
7
+ # after we've declared the associations. Since we haven't declared the associations in
8
+ # the models yet, we determine the association for the attribute based on its suffix.
9
+ #
10
+ # i.e. - bin/super-scaffold crud Project tag_ids:super_select{class_name=Projects::Tag}
11
+ # Here, we determine the association for the `tag_ids` attribute by its suffix, `_ids`.
12
+
13
+ class Scaffolding::Attribute
14
+ attr_accessor :name, :type, :options, :scaffolding_type, :attribute_index, :original_type
15
+
16
+ # @attribute_definition [String]: The raw attribute name, type, and options that are passed to bin/super-scaffold.
17
+ # @scaffoldng_type [Key]: The type of scaffolder we're using to Super Scaffold the model as a whole.
18
+ # @attribute_index [Integer]: The index taken from the Array of all the attributes of the model.
19
+ def initialize(attribute_definition, scaffolding_type, attribute_index)
20
+ parts = attribute_definition.split(":")
21
+ self.name = parts.shift
22
+ self.type, self.options = get_type_and_options_from(parts)
23
+ self.scaffolding_type = scaffolding_type
24
+ self.attribute_index = attribute_index
25
+
26
+ # We mutate `type` within the transformer, so `original_type` allows us
27
+ # to access what the developer originally passed to bin/super-scaffold.
28
+ # (Refer to sql_type_to_field_type_mapping in the transformer)
29
+ self.original_type = type
30
+
31
+ # Ensure `options` is a hash.
32
+ self.options = if options
33
+ options.split(",").map { |s|
34
+ option_name, option_value = s.split("=")
35
+ [option_name.to_sym, option_value || true]
36
+ }.to_h
37
+ else
38
+ {}
39
+ end
40
+
41
+ options[:label] ||= "label_string"
42
+ end
43
+
44
+ def is_first_attribute?
45
+ attribute_index == 0 && scaffolding_type == :crud
46
+ end
47
+
48
+ # if this is the first attribute of a newly scaffolded model, that field is required.
49
+ def is_required?
50
+ return false if type == "file_field"
51
+ options[:required] || is_first_attribute?
52
+ end
53
+
54
+ def is_association?
55
+ is_belongs_to? || is_has_many?
56
+ end
57
+
58
+ def is_belongs_to?
59
+ is_id? && !is_vanilla?
60
+ end
61
+
62
+ def is_has_many?
63
+ is_ids? && !is_vanilla?
64
+ end
65
+
66
+ def is_vanilla?
67
+ options&.key?(:vanilla)
68
+ end
69
+
70
+ def is_multiple?
71
+ options&.key?(:multiple) || is_has_many?
72
+ end
73
+
74
+ def is_boolean?
75
+ original_type == "boolean"
76
+ end
77
+
78
+ # Sometimes we need all the magic of a `*_id` field, but without the scoping stuff.
79
+ # Possibly only ever used internally by `join-model`.
80
+ def is_unscoped?
81
+ options[:unscoped]
82
+ end
83
+
84
+ def is_id?
85
+ name.match?(/_id$/)
86
+ end
87
+
88
+ def is_ids?
89
+ name.match?(/_ids$/)
90
+ end
91
+
92
+ def name_without_id
93
+ name.gsub(/_id$/, "")
94
+ end
95
+
96
+ def name_without_ids
97
+ name.gsub(/_ids$/, "").pluralize
98
+ end
99
+
100
+ def name_without_id_suffix
101
+ if is_ids?
102
+ name_without_ids
103
+ elsif is_id?
104
+ name_without_id
105
+ else
106
+ name
107
+ end
108
+ end
109
+
110
+ def title_case
111
+ if is_ids?
112
+ # user_ids should be 'Users'
113
+ name_without_ids.humanize.titlecase
114
+ elsif is_id?
115
+ name_without_id.humanize.titlecase
116
+ else
117
+ name.humanize.titlecase
118
+ end
119
+ end
120
+
121
+ def collection_name
122
+ is_ids? ? name_without_ids : name_without_id.pluralize
123
+ end
124
+
125
+ # Field on the show view.
126
+ def partial_name
127
+ return options[:attribute] if options[:attribute]
128
+
129
+ case type
130
+ when "trix_editor", "ckeditor"
131
+ "html"
132
+ when "buttons", "super_select", "options", "boolean"
133
+ if is_ids?
134
+ "has_many"
135
+ elsif is_id?
136
+ "belongs_to"
137
+ else
138
+ "option#{"s" if is_multiple?}"
139
+ end
140
+ when "cloudinary_image"
141
+ options[:height] = 200
142
+ "image"
143
+ when "phone_field"
144
+ "phone_number"
145
+ when "date_field"
146
+ "date"
147
+ when "date_and_time_field"
148
+ "date_and_time"
149
+ when "email_field"
150
+ "email"
151
+ when "emoji_field"
152
+ "text"
153
+ when "color_picker"
154
+ "code"
155
+ when "text_field"
156
+ "text"
157
+ when "text_area"
158
+ "text"
159
+ when "file_field"
160
+ "file#{"s" if is_multiple?}"
161
+ when "password_field"
162
+ "text"
163
+ when "number_field"
164
+ "number"
165
+ else
166
+ raise "Invalid field type: #{type}."
167
+ end
168
+ end
169
+
170
+ def default_value
171
+ case type
172
+ when "text_field", "password_field", "text_area"
173
+ "'Alternative String Value'"
174
+ when "email_field"
175
+ "'another.email@test.com'"
176
+ when "phone_field"
177
+ "'+19053871234'"
178
+ when "color_picker"
179
+ "'#47E37F'"
180
+ end
181
+ end
182
+
183
+ def special_processing
184
+ case type
185
+ when "date_field"
186
+ "assign_date(strong_params, :#{name})"
187
+ when "date_and_time_field"
188
+ "assign_date_and_time(strong_params, :#{name})"
189
+ when "buttons"
190
+ if is_boolean?
191
+ "assign_boolean(strong_params, :#{name})"
192
+ elsif is_multiple?
193
+ "assign_checkboxes(strong_params, :#{name})"
194
+ end
195
+ when "options"
196
+ if is_multiple?
197
+ "assign_checkboxes(strong_params, :#{name})"
198
+ end
199
+ when "super_select"
200
+ if is_boolean?
201
+ "assign_boolean(strong_params, :#{name})"
202
+ elsif is_multiple?
203
+ "assign_select_options(strong_params, :#{name})"
204
+ end
205
+ end
206
+ end
207
+
208
+ private
209
+
210
+ # i.e. - multiple_buttons:buttons{multiple}
211
+ def get_type_and_options_from(parts)
212
+ parts.join(":").scan(/^(.*){(.*)}/).first || parts.join(":")
213
+ end
214
+ end
@@ -2,6 +2,7 @@ require "indefinite_article"
2
2
  require "yaml"
3
3
  require "scaffolding/file_manipulator"
4
4
  require "scaffolding/class_names_transformer"
5
+ require "scaffolding/attribute"
5
6
 
6
7
  class Scaffolding::Transformer
7
8
  attr_accessor :child, :parent, :parents, :class_names_transformer, :cli_options, :additional_steps, :namespace, :suppress_could_not_find
@@ -628,15 +629,10 @@ class Scaffolding::Transformer
628
629
  }
629
630
 
630
631
  # add attributes to various views.
631
- attributes.each_with_index do |attribute, index|
632
- first_table_cell = index == 0 && scaffolding_options[:type] == :crud
632
+ attributes.each_with_index do |attribute_definition, index|
633
+ attribute = Scaffolding::Attribute.new(attribute_definition, scaffolding_options[:type], index)
633
634
 
634
- parts = attribute.split(":")
635
- name = parts.shift
636
- type = parts.join(":")
637
- boolean_buttons = type == "boolean"
638
-
639
- if first_table_cell && ["trix_editor", "ckeditor", "text_area"].include?(type)
635
+ if attribute.is_first_attribute? && ["trix_editor", "ckeditor", "text_area"].include?(attribute.type)
640
636
  puts ""
641
637
  puts "The first attribute of your model cannot be any of the following types:".red
642
638
  puts "1. trix_editor"
@@ -648,130 +644,24 @@ class Scaffolding::Transformer
648
644
  exit
649
645
  end
650
646
 
651
- # extract any options they passed in with the field.
652
- # will extract options declared with either [] or {}.
653
- type, attribute_options = type.scan(/^(.*){(.*)}/).first || type
654
-
655
- # create a hash of the options.
656
- attribute_options = if attribute_options
657
- attribute_options.split(",").map { |s|
658
- option_name, option_value = s.split("=")
659
- [option_name.to_sym, option_value || true]
660
- }.to_h
661
- else
662
- {}
647
+ if sql_type_to_field_type_mapping[attribute.type]
648
+ attribute.type = sql_type_to_field_type_mapping[attribute.type]
663
649
  end
664
650
 
665
- attribute_options[:label] ||= "label_string"
666
-
667
- if sql_type_to_field_type_mapping[type]
668
- type = sql_type_to_field_type_mapping[type]
669
- end
670
-
671
- is_id = name.match?(/_id$/)
672
- is_ids = name.match?(/_ids$/)
673
- # if this is the first attribute of a newly scaffolded model, that field is required.
674
- unless type == "file_field"
675
- is_required = attribute_options[:required] || (scaffolding_options[:type] == :crud && index == 0)
676
- end
677
- is_vanilla = attribute_options&.key?(:vanilla)
678
- is_belongs_to = is_id && !is_vanilla
679
- is_has_many = is_ids && !is_vanilla
680
- is_multiple = attribute_options&.key?(:multiple) || is_has_many
681
- is_association = is_belongs_to || is_has_many
682
-
683
- # Sometimes we need all the magic of a `*_id` field, but without the scoping stuff.
684
- # Possibly only ever used internally by `join-model`.
685
- is_unscoped = attribute_options[:unscoped]
686
-
687
- name_without_id = name.gsub(/_id$/, "")
688
- name_without_ids = name.gsub(/_ids$/, "").pluralize
689
- collection_name = is_ids ? name_without_ids : name_without_id.pluralize
690
-
691
- # field on the show view.
692
- attribute_partial ||= attribute_options[:attribute] || case type
693
- when "trix_editor", "ckeditor"
694
- "html"
695
- when "buttons", "super_select", "options", "boolean"
696
- if is_ids
697
- "has_many"
698
- elsif is_id
699
- "belongs_to"
700
- else
701
- "option#{"s" if is_multiple}"
702
- end
703
- when "cloudinary_image"
704
- attribute_options[:height] = 200
705
- "image"
706
- when "phone_field"
707
- "phone_number"
708
- when "date_field"
709
- "date"
710
- when "date_and_time_field"
711
- "date_and_time"
712
- when "email_field"
713
- "email"
714
- when "emoji_field"
715
- "text"
716
- when "color_picker"
717
- "code"
718
- when "text_field"
719
- "text"
720
- when "text_area"
721
- "text"
722
- when "number_field"
723
- "number"
724
- when "file_field"
725
- "file#{"s" if is_multiple}"
726
- when "password_field"
727
- "text"
728
- else
729
- raise "Invalid field type: #{type}."
730
- end
731
-
732
- cell_attributes = if boolean_buttons
651
+ cell_attributes = if attribute.is_boolean?
733
652
  ' class="text-center"'
734
653
  end
735
654
 
736
- # e.g. from `person_id` to `person` or `person_ids` to `people`.
737
- attribute_name = if is_ids
738
- name_without_ids
739
- elsif is_id
740
- name_without_id
741
- else
742
- name
743
- end
744
-
745
- title_case = if is_ids
746
- # user_ids should be 'Users'
747
- name_without_ids.humanize.titlecase
748
- elsif is_id
749
- name_without_id.humanize.titlecase
750
- else
751
- name.humanize.titlecase
752
- end
753
-
754
- attribute_assignment = case type
755
- when "text_field", "password_field", "text_area"
756
- "'Alternative String Value'"
757
- when "email_field"
758
- "'another.email@test.com'"
759
- when "phone_field"
760
- "'+19053871234'"
761
- when "color_picker"
762
- "'#47E37F'"
763
- end
764
-
765
655
  # don't do table columns for certain types of fields and attribute partials
766
- if ["trix_editor", "ckeditor", "text_area"].include?(type) || ["html", "has_many"].include?(attribute_partial)
656
+ if ["trix_editor", "ckeditor", "text_area"].include?(attribute.type) || ["html", "has_many"].include?(attribute.partial_name)
767
657
  cli_options["skip-table"] = true
768
658
  end
769
659
 
770
- if type == "none"
660
+ if attribute.type == "none"
771
661
  cli_options["skip-form"] = true
772
662
  end
773
663
 
774
- if attribute_partial == "none"
664
+ if attribute.partial_name == "none"
775
665
  cli_options["skip-show"] = true
776
666
  cli_options["skip-table"] = true
777
667
  end
@@ -780,26 +670,26 @@ class Scaffolding::Transformer
780
670
  # MODEL VALIDATIONS
781
671
  #
782
672
 
783
- unless cli_options["skip-form"] || is_unscoped
673
+ unless cli_options["skip-form"] || attribute.is_unscoped?
784
674
 
785
675
  file_name = "./app/models/scaffolding/completely_concrete/tangible_thing.rb"
786
676
 
787
- if is_association
788
- field_content = if attribute_options[:source]
677
+ if attribute.is_association?
678
+ field_content = if attribute.options[:source]
789
679
  <<~RUBY
790
- def valid_#{collection_name}
791
- #{attribute_options[:source]}
680
+ def valid_#{attribute.collection_name}
681
+ #{attribute.options[:source]}
792
682
  end
793
683
 
794
684
  RUBY
795
685
  else
796
- add_additional_step :yellow, transform_string("You'll need to implement the `valid_#{collection_name}` method of `Scaffolding::CompletelyConcrete::TangibleThing` in `./app/models/scaffolding/completely_concrete/tangible_thing.rb`. This is the method that will be used to populate the `#{type}` field and also validate that users aren't trying to exploit multitenancy.")
686
+ add_additional_step :yellow, transform_string("You'll need to implement the `valid_#{attribute.collection_name}` method of `Scaffolding::CompletelyConcrete::TangibleThing` in `./app/models/scaffolding/completely_concrete/tangible_thing.rb`. This is the method that will be used to populate the `#{attribute.type}` field and also validate that users aren't trying to exploit multitenancy.")
797
687
 
798
688
  <<~RUBY
799
- def valid_#{collection_name}
800
- raise "please review and implement `valid_#{collection_name}` in `app/models/scaffolding/completely_concrete/tangible_thing.rb`."
801
- # please specify what objects should be considered valid for assigning to `#{name_without_id}`.
802
- # the resulting code should probably look something like `team.#{collection_name}`.
689
+ def valid_#{attribute.collection_name}
690
+ raise "please review and implement `valid_#{attribute.collection_name}` in `app/models/scaffolding/completely_concrete/tangible_thing.rb`."
691
+ # please specify what objects should be considered valid for assigning to `#{attribute.name_without_id}`.
692
+ # the resulting code should probably look something like `team.#{attribute.collection_name}`.
803
693
  end
804
694
 
805
695
  RUBY
@@ -807,8 +697,8 @@ class Scaffolding::Transformer
807
697
 
808
698
  scaffold_add_line_to_file(file_name, field_content, METHODS_HOOK, prepend: true)
809
699
 
810
- if is_belongs_to
811
- scaffold_add_line_to_file(file_name, "validates :#{name_without_id}, scope: true", VALIDATIONS_HOOK, prepend: true)
700
+ if attribute.is_belongs_to?
701
+ scaffold_add_line_to_file(file_name, "validates :#{attribute.name_without_id}, scope: true", VALIDATIONS_HOOK, prepend: true)
812
702
  end
813
703
 
814
704
  # TODO we need to add a multitenancy check for has many associations.
@@ -820,51 +710,51 @@ class Scaffolding::Transformer
820
710
  # FORM FIELD
821
711
  #
822
712
 
823
- unless cli_options["skip-form"] || attribute_options[:readonly]
713
+ unless cli_options["skip-form"] || attribute.options[:readonly]
824
714
 
825
715
  # add `has_rich_text` for trix editor fields.
826
- if type == "trix_editor"
716
+ if attribute.type == "trix_editor"
827
717
  file_name = "./app/models/scaffolding/completely_concrete/tangible_thing.rb"
828
- scaffold_add_line_to_file(file_name, "has_rich_text :#{name}", HAS_ONE_HOOK, prepend: true)
718
+ scaffold_add_line_to_file(file_name, "has_rich_text :#{attribute.name}", HAS_ONE_HOOK, prepend: true)
829
719
  end
830
720
 
831
721
  # field on the form.
832
- field_attributes = {method: ":#{name}"}
722
+ field_attributes = {method: ":#{attribute.name}"}
833
723
  field_options = {}
834
724
  options = {}
835
725
 
836
- if scaffolding_options[:type] == :crud && index == 0
726
+ if attribute.is_first_attribute?
837
727
  field_options[:autofocus] = "true"
838
728
  end
839
729
 
840
- if is_id && type == "super_select"
841
- options[:include_blank] = "t('.fields.#{name}.placeholder')"
730
+ if attribute.is_id? && attribute.type == "super_select"
731
+ options[:include_blank] = "t('.fields.#{attribute.name}.placeholder')"
842
732
  # 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.")
843
733
  end
844
734
 
845
- if type == "color_picker"
846
- field_options[:color_picker_options] = "t('#{child.pluralize.underscore}.fields.#{name}.options')"
735
+ if attribute.type == "color_picker"
736
+ field_options[:color_picker_options] = "t('#{child.pluralize.underscore}.fields.#{attribute.name}.options')"
847
737
  end
848
738
 
849
739
  # When rendering a super_select element we need to use `html_options: {multiple: true}`,
850
740
  # but all other fields simply use `multiple: true` to work.
851
- if is_multiple
852
- if type == "super_select"
741
+ if attribute.is_multiple?
742
+ if attribute.type == "super_select"
853
743
  field_options[:multiple] = "true"
854
744
  else
855
745
  field_attributes[:multiple] = "true"
856
746
  end
857
747
  end
858
748
 
859
- valid_values = if is_id
860
- "valid_#{name_without_id.pluralize}"
861
- elsif is_ids
862
- "valid_#{collection_name}"
749
+ valid_values = if attribute.is_id?
750
+ "valid_#{attribute.name_without_id.pluralize}"
751
+ elsif attribute.is_ids?
752
+ "valid_#{attribute.collection_name}"
863
753
  end
864
754
 
865
755
  # https://stackoverflow.com/questions/21582464/is-there-a-ruby-hashto-s-equivalent-for-the-new-hash-syntax
866
756
  if field_options.any? || options.any?
867
- field_options_key = if ["buttons", "super_select", "options"].include?(type)
757
+ field_options_key = if ["buttons", "super_select", "options"].include?(attribute.type)
868
758
  if options.any?
869
759
  field_attributes[:options] = "{" + field_options.map { |key, value| "#{key}: #{value}" }.join(", ") + "}"
870
760
  end
@@ -879,17 +769,17 @@ class Scaffolding::Transformer
879
769
  field_attributes[field_options_key] = "{" + field_options.map { |key, value| "#{key}: #{value}" }.join(", ") + "}"
880
770
  end
881
771
 
882
- if is_association
883
- short = attribute_options[:class_name].underscore.split("/").last
884
- case type
772
+ if attribute.is_association?
773
+ short = attribute.options[:class_name].underscore.split("/").last
774
+ case attribute.type
885
775
  when "buttons", "options"
886
- field_attributes["\n options"] = "@tangible_thing.#{valid_values}.map { |#{short}| [#{short}.id, #{short}.#{attribute_options[:label]}] }"
776
+ field_attributes["\n options"] = "@tangible_thing.#{valid_values}.map { |#{short}| [#{short}.id, #{short}.#{attribute.options[:label]}] }"
887
777
  when "super_select"
888
- field_attributes["\n choices"] = "@tangible_thing.#{valid_values}.map { |#{short}| [#{short}.#{attribute_options[:label]}, #{short}.id] }"
778
+ field_attributes["\n choices"] = "@tangible_thing.#{valid_values}.map { |#{short}| [#{short}.#{attribute.options[:label]}, #{short}.id] }"
889
779
  end
890
780
  end
891
781
 
892
- field_content = "<%= render 'shared/fields/#{type}'#{", " if field_attributes.any?}#{field_attributes.map { |key, value| "#{key}: #{value}" }.join(", ")} %>"
782
+ field_content = "<%= render 'shared/fields/#{attribute.type}'#{", " if field_attributes.any?}#{field_attributes.map { |key, value| "#{key}: #{value}" }.join(", ")} %>"
893
783
 
894
784
  # TODO Add more of these from other packages?
895
785
  is_core_model = ["Team", "User", "Membership"].include?(child)
@@ -904,24 +794,24 @@ class Scaffolding::Transformer
904
794
 
905
795
  unless cli_options["skip-show"]
906
796
 
907
- if is_id
797
+ if attribute.is_id?
908
798
  <<~ERB
909
- <% if @tangible_thing.#{name_without_id} %>
799
+ <% if @tangible_thing.#{attribute.name_without_id} %>
910
800
  <div class="form-group">
911
- <label class="col-form-label"><%= t('.fields.#{name}.heading') %></label>
801
+ <label class="col-form-label"><%= t('.fields.#{attribute.name}.heading') %></label>
912
802
  <div>
913
- <%= link_to @tangible_thing.#{name_without_id}.#{attribute_options[:label]}, [:account, @tangible_thing.#{name_without_id}] %>
803
+ <%= link_to @tangible_thing.#{attribute.name_without_id}.#{attribute.options[:label]}, [:account, @tangible_thing.#{attribute.name_without_id}] %>
914
804
  </div>
915
805
  </div>
916
806
  <% end %>
917
807
  ERB
918
- elsif is_ids
808
+ elsif attribute.is_ids?
919
809
  <<~ERB
920
- <% if @tangible_thing.#{collection_name}.any? %>
810
+ <% if @tangible_thing.#{attribute.collection_name}.any? %>
921
811
  <div class="form-group">
922
- <label class="col-form-label"><%= t('.fields.#{name}.heading') %></label>
812
+ <label class="col-form-label"><%= t('.fields.#{attribute.name}.heading') %></label>
923
813
  <div>
924
- <%= @tangible_thing.#{collection_name}.map { |#{name_without_ids}| link_to #{name_without_ids}.#{attribute_options[:label]}, [:account, #{name_without_ids}] }.to_sentence.html_safe %>
814
+ <%= @tangible_thing.#{attribute.collection_name}.map { |#{attribute.name_without_ids}| link_to #{attribute.name_without_ids}.#{attribute.options[:label]}, [:account, #{attribute.name_without_ids}] }.to_sentence.html_safe %>
925
815
  </div>
926
816
  </div>
927
817
  <% end %>
@@ -930,10 +820,10 @@ class Scaffolding::Transformer
930
820
 
931
821
  # this gets stripped and is one line, so indentation isn't a problem.
932
822
  field_content = <<-ERB
933
- <%= render 'shared/attributes/#{attribute_partial}', attribute: :#{attribute_name} %>
823
+ <%= render 'shared/attributes/#{attribute.partial_name}', attribute: :#{attribute.name_without_id_suffix} %>
934
824
  ERB
935
825
 
936
- if type == "password_field"
826
+ if attribute.type == "password_field"
937
827
  field_content.gsub!(/\s%>/, ", options: { password: true } %>")
938
828
  end
939
829
 
@@ -949,7 +839,7 @@ class Scaffolding::Transformer
949
839
  unless cli_options["skip-table"]
950
840
 
951
841
  # table header.
952
- field_content = "<th#{cell_attributes.present? ? " " + cell_attributes : ""}><%= t('.fields.#{attribute_name}.heading') %></th>"
842
+ field_content = "<th#{cell_attributes.present? ? " " + cell_attributes : ""}><%= t('.fields.#{attribute.name_without_id_suffix}.heading') %></th>"
953
843
 
954
844
  unless ["Team", "User"].include?(child)
955
845
  scaffold_add_line_to_file("./app/views/account/scaffolding/completely_concrete/tangible_things/_index.html.erb", field_content, "<%# 🚅 super scaffolding will insert new field headers above this line. %>", prepend: true)
@@ -971,16 +861,16 @@ class Scaffolding::Transformer
971
861
 
972
862
  table_cell_options = []
973
863
 
974
- if first_table_cell
864
+ if attribute.is_first_attribute?
975
865
  table_cell_options << "url: [:account, tangible_thing]"
976
866
  end
977
867
 
978
868
  # this gets stripped and is one line, so indentation isn't a problem.
979
869
  field_content = <<-ERB
980
- <td#{cell_attributes}><%= render 'shared/attributes/#{attribute_partial}', attribute: :#{attribute_name}#{", #{table_cell_options.join(", ")}" if table_cell_options.any?} %></td>
870
+ <td#{cell_attributes}><%= render 'shared/attributes/#{attribute.partial_name}', attribute: :#{attribute.name_without_id_suffix}#{", #{table_cell_options.join(", ")}" if table_cell_options.any?} %></td>
981
871
  ERB
982
872
 
983
- if type == "password_field"
873
+ if attribute.type == "password_field"
984
874
  field_content.gsub!(/\s%>/, ", options: { password: true } %>")
985
875
  end
986
876
 
@@ -998,26 +888,26 @@ class Scaffolding::Transformer
998
888
 
999
889
  yaml_template = <<~YAML
1000
890
 
1001
- <%= name %>: <% if is_association %>&<%= attribute_name %><% end %>
1002
- _: &#{name} #{title_case}
1003
- label: *#{name}
1004
- heading: *#{name}
891
+ <%= attribute.name %>: <% if attribute.is_association? %>&<%= attribute.name_without_id_suffix %><% end %>
892
+ _: &#{attribute.name} #{attribute.title_case}
893
+ label: *#{attribute.name}
894
+ heading: *#{attribute.name}
1005
895
 
1006
- <% if type == "super_select" %>
1007
- <% if is_required %>
1008
- placeholder: Select <% title_case.with_indefinite_article %>
896
+ <% if attribute.type == "super_select" %>
897
+ <% if attribute.is_required? %>
898
+ placeholder: Select <% attribute.title_case.with_indefinite_article %>
1009
899
  <% else %>
1010
900
  placeholder: None
1011
901
  <% end %>
1012
902
  <% end %>
1013
903
 
1014
- <% if boolean_buttons %>
904
+ <% if attribute.is_boolean? %>
1015
905
 
1016
906
  options:
1017
907
  yes: "Yes"
1018
908
  no: "No"
1019
909
 
1020
- <% elsif ["buttons", "super_select", "options"].include?(type) && !is_association %>
910
+ <% elsif ["buttons", "super_select", "options"].include?(attribute.type) && !attribute.is_association? %>
1021
911
 
1022
912
  options:
1023
913
  one: One
@@ -1026,7 +916,7 @@ class Scaffolding::Transformer
1026
916
 
1027
917
  <% end %>
1028
918
 
1029
- <% if type == "color_picker" %>
919
+ <% if attribute.type == "color_picker" %>
1030
920
  options:
1031
921
  - '#9C73D2'
1032
922
  - '#48CDFE'
@@ -1038,8 +928,8 @@ class Scaffolding::Transformer
1038
928
  - '#929292'
1039
929
  <% end %>
1040
930
 
1041
- <% if is_association %>
1042
- <%= attribute_name %>: *<%= attribute_name %>
931
+ <% if attribute.is_association? %>
932
+ <%= attribute.name_without_id_suffix %>: *<%= attribute.name_without_id_suffix %>
1043
933
  <% end %>
1044
934
  YAML
1045
935
 
@@ -1048,7 +938,7 @@ class Scaffolding::Transformer
1048
938
  scaffold_add_line_to_file("./config/locales/en/scaffolding/completely_concrete/tangible_things.en.yml", field_content, RUBY_NEW_FIELDS_HOOK, prepend: true)
1049
939
 
1050
940
  # active record's field label.
1051
- scaffold_add_line_to_file("./config/locales/en/scaffolding/completely_concrete/tangible_things.en.yml", "#{name}: *#{name}", "# 🚅 super scaffolding will insert new activerecord attributes above this line.", prepend: true)
941
+ scaffold_add_line_to_file("./config/locales/en/scaffolding/completely_concrete/tangible_things.en.yml", "#{attribute.name}: *#{attribute.name}", "# 🚅 super scaffolding will insert new activerecord attributes above this line.", prepend: true)
1052
942
 
1053
943
  end
1054
944
 
@@ -1056,46 +946,27 @@ class Scaffolding::Transformer
1056
946
  # STRONG PARAMETERS
1057
947
  #
1058
948
 
1059
- unless cli_options["skip-form"] || attribute_options[:readonly]
949
+ unless cli_options["skip-form"] || attribute.options[:readonly]
1060
950
 
1061
951
  # add attributes to strong params.
1062
952
  [
1063
953
  "./app/controllers/account/scaffolding/completely_concrete/tangible_things_controller.rb",
1064
954
  "./app/controllers/api/v1/scaffolding/completely_concrete/tangible_things_controller.rb"
1065
955
  ].each do |file|
1066
- if is_ids || is_multiple
1067
- scaffold_add_line_to_file(file, "#{name}: [],", RUBY_NEW_ARRAYS_HOOK, prepend: true)
1068
- if type == "file_field"
1069
- scaffold_add_line_to_file(file, "#{name}_removal: [],", RUBY_NEW_ARRAYS_HOOK, prepend: true)
956
+ if attribute.is_ids? || attribute.is_multiple?
957
+ scaffold_add_line_to_file(file, "#{attribute.name}: [],", RUBY_NEW_ARRAYS_HOOK, prepend: true)
958
+ if attribute.type == "file_field"
959
+ scaffold_add_line_to_file(file, "#{attribute.name}_removal: [],", RUBY_NEW_ARRAYS_HOOK, prepend: true)
1070
960
  end
1071
961
  else
1072
- scaffold_add_line_to_file(file, ":#{name},", RUBY_NEW_FIELDS_HOOK, prepend: true)
1073
- if type == "file_field"
1074
- scaffold_add_line_to_file(file, ":#{name}_removal,", RUBY_NEW_FIELDS_HOOK, prepend: true)
962
+ scaffold_add_line_to_file(file, ":#{attribute.name},", RUBY_NEW_FIELDS_HOOK, prepend: true)
963
+ if attribute.type == "file_field"
964
+ scaffold_add_line_to_file(file, ":#{attribute.name}_removal,", RUBY_NEW_FIELDS_HOOK, prepend: true)
1075
965
  end
1076
966
  end
1077
967
  end
1078
968
 
1079
- special_processing = case type
1080
- when "buttons"
1081
- if boolean_buttons
1082
- "assign_boolean(strong_params, :#{name})"
1083
- elsif is_multiple
1084
- "assign_checkboxes(strong_params, :#{name})"
1085
- end
1086
- when "options"
1087
- if is_multiple
1088
- "assign_checkboxes(strong_params, :#{name})"
1089
- end
1090
- when "super_select"
1091
- if boolean_buttons
1092
- "assign_boolean(strong_params, :#{name})"
1093
- elsif is_multiple
1094
- "assign_select_options(strong_params, :#{name})"
1095
- end
1096
- end
1097
-
1098
- scaffold_add_line_to_file("./app/controllers/account/scaffolding/completely_concrete/tangible_things_controller.rb", special_processing, RUBY_NEW_FIELDS_PROCESSING_HOOK, prepend: true) if special_processing
969
+ scaffold_add_line_to_file("./app/controllers/account/scaffolding/completely_concrete/tangible_things_controller.rb", attribute.special_processing, RUBY_NEW_FIELDS_PROCESSING_HOOK, prepend: true) if attribute.special_processing
1099
970
  end
1100
971
 
1101
972
  #
@@ -1105,61 +976,61 @@ class Scaffolding::Transformer
1105
976
  unless cli_options["skip-api"]
1106
977
 
1107
978
  # TODO The serializers can't handle these `has_rich_text` attributes.
1108
- unless type == "trix_editor"
1109
- unless type == "file_field"
1110
- scaffold_add_line_to_file("./app/views/api/v1/scaffolding/completely_concrete/tangible_things/_tangible_thing.json.jbuilder", ":#{name},", RUBY_NEW_FIELDS_HOOK, prepend: true, suppress_could_not_find: true)
979
+ unless attribute.type == "trix_editor"
980
+ unless attribute.type == "file_field"
981
+ scaffold_add_line_to_file("./app/views/api/v1/scaffolding/completely_concrete/tangible_things/_tangible_thing.json.jbuilder", ":#{attribute.name},", RUBY_NEW_FIELDS_HOOK, prepend: true, suppress_could_not_find: true)
1111
982
  end
1112
983
 
1113
- assertion = case type
984
+ assertion = case attribute.type
1114
985
  when "date_field"
1115
- "assert_equal_or_nil Date.parse(tangible_thing_data['#{name}']), tangible_thing.#{name}"
986
+ "assert_equal_or_nil Date.parse(tangible_thing_data['#{attribute.name}']), tangible_thing.#{attribute.name}"
1116
987
  when "date_and_time_field"
1117
- "assert_equal_or_nil DateTime.parse(tangible_thing_data['#{name}']), tangible_thing.#{name}"
988
+ "assert_equal_or_nil DateTime.parse(tangible_thing_data['#{attribute.name}']), tangible_thing.#{attribute.name}"
1118
989
  when "file_field"
1119
- if is_multiple
1120
- "assert_equal tangible_thing_data['#{name}'], @tangible_thing.#{name}.map{|file| rails_blob_path(file)} unless controller.action_name == 'create'"
990
+ if attribute.is_multiple?
991
+ "assert_equal tangible_thing_data['#{attribute.name}'], @tangible_thing.#{attribute.name}.map{|file| rails_blob_path(file)} unless controller.action_name == 'create'"
1121
992
  else
1122
- "assert_equal tangible_thing_data['#{name}'], rails_blob_path(@tangible_thing.#{name}) unless controller.action_name == 'create'"
993
+ "assert_equal tangible_thing_data['#{attribute.name}'], rails_blob_path(@tangible_thing.#{attribute.name}) unless controller.action_name == 'create'"
1123
994
  end
1124
995
  else
1125
- "assert_equal_or_nil tangible_thing_data['#{name}'], tangible_thing.#{name}"
996
+ "assert_equal_or_nil tangible_thing_data['#{attribute.name}'], tangible_thing.#{attribute.name}"
1126
997
  end
1127
998
  scaffold_add_line_to_file("./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_controller_test.rb", assertion, RUBY_NEW_FIELDS_HOOK, prepend: true)
1128
999
  end
1129
1000
 
1130
1001
  # File fields are handled in a specific way when using the jsonapi-serializer.
1131
- if type == "file_field"
1132
- jbuilder_content = if is_multiple
1002
+ if attribute.type == "file_field"
1003
+ jbuilder_content = if attribute.is_multiple?
1133
1004
  <<~RUBY
1134
- json.#{name} do
1135
- json.array! tangible_thing.#{name}.map { |file| url_for(file) }
1136
- end if tangible_thing.#{name}.attached?
1005
+ json.#{attribute.name} do
1006
+ json.array! tangible_thing.#{attribute.name}.map { |file| url_for(file) }
1007
+ end if tangible_thing.#{attribute.name}.attached?
1137
1008
  RUBY
1138
1009
  else
1139
- "json.#{name} url_for(tangible_thing.#{name}) if tangible_thing.#{name}.attached?"
1010
+ "json.#{attribute.name} url_for(tangible_thing.#{attribute.name}) if tangible_thing.#{attribute.name}.attached?"
1140
1011
  end
1141
1012
 
1142
1013
  scaffold_add_line_to_file("./app/views/api/v1/scaffolding/completely_concrete/tangible_things/_tangible_thing.json.jbuilder", jbuilder_content, RUBY_FILES_HOOK, prepend: true, suppress_could_not_find: true)
1143
1014
  # We also want to make sure we attach the dummy file in the API test on setup
1144
1015
  file_name = "./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_controller_test.rb"
1145
- content = if is_multiple
1016
+ content = if attribute.is_multiple?
1146
1017
  <<~RUBY
1147
- @#{child.underscore}.#{name} = [Rack::Test::UploadedFile.new("test/support/foo.txt")]
1148
- @another_#{child.underscore}.#{name} = [Rack::Test::UploadedFile.new("test/support/foo.txt")]
1018
+ @#{child.underscore}.#{attribute.name} = [Rack::Test::UploadedFile.new("test/support/foo.txt")]
1019
+ @another_#{child.underscore}.#{attribute.name} = [Rack::Test::UploadedFile.new("test/support/foo.txt")]
1149
1020
  RUBY
1150
1021
  else
1151
1022
  <<~RUBY
1152
- @#{child.underscore}.#{name} = Rack::Test::UploadedFile.new("test/support/foo.txt")
1153
- @another_#{child.underscore}.#{name} = Rack::Test::UploadedFile.new("test/support/foo.txt")
1023
+ @#{child.underscore}.#{attribute.name} = Rack::Test::UploadedFile.new("test/support/foo.txt")
1024
+ @another_#{child.underscore}.#{attribute.name} = Rack::Test::UploadedFile.new("test/support/foo.txt")
1154
1025
  RUBY
1155
1026
  end
1156
1027
  scaffold_add_line_to_file(file_name, content, RUBY_FILES_HOOK, prepend: true)
1157
1028
  end
1158
1029
 
1159
- if attribute_assignment
1160
- unless attribute_options[:readonly]
1161
- scaffold_add_line_to_file("./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_controller_test.rb", "#{name}: #{attribute_assignment},", RUBY_ADDITIONAL_NEW_FIELDS_HOOK, prepend: true)
1162
- scaffold_add_line_to_file("./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_controller_test.rb", "assert_equal @tangible_thing.#{name}, #{attribute_assignment}", RUBY_EVEN_MORE_NEW_FIELDS_HOOK, prepend: true)
1030
+ if attribute.default_value
1031
+ unless attribute.options[:readonly]
1032
+ scaffold_add_line_to_file("./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_controller_test.rb", "#{attribute.name}: #{attribute.default_value},", RUBY_ADDITIONAL_NEW_FIELDS_HOOK, prepend: true)
1033
+ scaffold_add_line_to_file("./test/controllers/api/v1/scaffolding/completely_concrete/tangible_things_controller_test.rb", "assert_equal @tangible_thing.#{attribute.name}, #{attribute.default_value}", RUBY_EVEN_MORE_NEW_FIELDS_HOOK, prepend: true)
1163
1034
  end
1164
1035
  end
1165
1036
  end
@@ -1176,7 +1047,7 @@ class Scaffolding::Transformer
1176
1047
  # It's OK that this won't be found most of the time.
1177
1048
  scaffold_add_line_to_file(
1178
1049
  "./app/views/api/v1/open_api/scaffolding/completely_concrete/tangible_things/_components.yaml.erb",
1179
- "<%= attribute :#{name} %>",
1050
+ "<%= attribute :#{attribute.name} %>",
1180
1051
  "<%# 🚅 super scaffolding will insert new attributes above this line. %>",
1181
1052
  prepend: true
1182
1053
  )
@@ -1184,7 +1055,7 @@ class Scaffolding::Transformer
1184
1055
  # It's OK that this won't be found most of the time.
1185
1056
  scaffold_add_line_to_file(
1186
1057
  "./app/views/api/v1/open_api/scaffolding/completely_concrete/tangible_things/_components.yaml.erb",
1187
- "<%= parameter :#{name} %>",
1058
+ "<%= parameter :#{attribute.name} %>",
1188
1059
  "<%# 🚅 super scaffolding will insert new parameter above this line. %>",
1189
1060
  prepend: true
1190
1061
  )
@@ -1198,20 +1069,20 @@ class Scaffolding::Transformer
1198
1069
 
1199
1070
  unless cli_options["skip-model"]
1200
1071
 
1201
- if is_belongs_to
1202
- unless attribute_options[:class_name]
1203
- attribute_options[:class_name] = name_without_id.classify
1072
+ if attribute.is_belongs_to?
1073
+ unless attribute.options[:class_name]
1074
+ attribute.options[:class_name] = attribute.name_without_id.classify
1204
1075
  end
1205
1076
 
1206
- file_name = "app/models/#{attribute_options[:class_name].underscore}.rb"
1077
+ file_name = "app/models/#{attribute.options[:class_name].underscore}.rb"
1207
1078
  unless File.exist?(file_name)
1208
- raise "You'll need to specify a `class_name` option for `#{name}` because there is no `#{attribute_options[:class_name].classify}` model defined in `#{file_name}`. Try again with `#{name}:#{type}[class_name=SomeClassName]`."
1079
+ raise "You'll need to specify a `class_name` option for `#{attribute.name}` because there is no `#{attribute.options[:class_name].classify}` model defined in `#{file_name}`. Try again with `#{attribute.name}:#{attribute.type}[class_name=SomeClassName]`."
1209
1080
  end
1210
1081
 
1211
1082
  modified_migration = false
1212
1083
 
1213
1084
  # find the database migration that defines this relationship.
1214
- expected_reference = "add_reference :#{class_names_transformer.table_name}, :#{name_without_id}"
1085
+ expected_reference = "add_reference :#{class_names_transformer.table_name}, :#{attribute.name_without_id}"
1215
1086
  migration_file_name = `grep "#{expected_reference}" db/migrate/*`.split(":").first
1216
1087
 
1217
1088
  # if that didn't work, see if we can find a creation of the reference when the table was created.
@@ -1219,7 +1090,7 @@ class Scaffolding::Transformer
1219
1090
  confirmation_reference = "create_table :#{class_names_transformer.table_name}"
1220
1091
  confirmation_migration_file_name = `grep "#{confirmation_reference}" db/migrate/*`.split(":").first
1221
1092
 
1222
- fallback_reference = "t.references :#{name_without_id}"
1093
+ fallback_reference = "t.references :#{attribute.name_without_id}"
1223
1094
  fallback_migration_file_name = `grep "#{fallback_reference}" db/migrate/* | grep #{confirmation_migration_file_name}`.split(":").first
1224
1095
 
1225
1096
  if fallback_migration_file_name == confirmation_migration_file_name
@@ -1227,10 +1098,10 @@ class Scaffolding::Transformer
1227
1098
  end
1228
1099
  end
1229
1100
 
1230
- unless is_required
1101
+ unless attribute.is_required?
1231
1102
 
1232
1103
  if migration_file_name
1233
- replace_in_file(migration_file_name, ":#{name_without_id}, null: false", ":#{name_without_id}, null: true")
1104
+ replace_in_file(migration_file_name, ":#{attribute.name_without_id}, null: false", ":#{attribute.name_without_id}, null: true")
1234
1105
  modified_migration = true
1235
1106
  else
1236
1107
  add_additional_step :yellow, "We would have expected there to be a migration that defined `#{expected_reference}`, but we didn't find one. Where was the reference added to this model? It's _probably_ the original creation of the table, but we couldn't find that either. Either way, you need to rollback, change 'null: false' to 'null: true' for this column, and re-run the migration (unless, of course, that attribute _is_ required, then you need to add a validation on the model)."
@@ -1238,10 +1109,10 @@ class Scaffolding::Transformer
1238
1109
 
1239
1110
  end
1240
1111
 
1241
- class_name_matches = name_without_id.tableize == attribute_options[:class_name].tableize.tr("/", "_")
1112
+ class_name_matches = attribute.name_without_id.tableize == attribute.options[:class_name].tableize.tr("/", "_")
1242
1113
 
1243
1114
  # but also, if namespaces are involved, just don't...
1244
- if attribute_options[:class_name].include?("::")
1115
+ if attribute.options[:class_name].include?("::")
1245
1116
  class_name_matches = false
1246
1117
  end
1247
1118
 
@@ -1249,24 +1120,24 @@ class Scaffolding::Transformer
1249
1120
  unless class_name_matches
1250
1121
  if migration_file_name
1251
1122
  # There are two forms this association creation can take.
1252
- replace_in_file(migration_file_name, "foreign_key: true", "foreign_key: {to_table: \"#{attribute_options[:class_name].tableize.tr("/", "_")}\"}", /t\.references :#{name_without_id}/)
1253
- replace_in_file(migration_file_name, "foreign_key: true", "foreign_key: {to_table: \"#{attribute_options[:class_name].tableize.tr("/", "_")}\"}", /add_reference :#{child.underscore.pluralize.tr("/", "_")}, :#{name_without_id}/)
1123
+ replace_in_file(migration_file_name, "foreign_key: true", "foreign_key: {to_table: \"#{attribute.options[:class_name].tableize.tr("/", "_")}\"}", /t\.references :#{attribute.name_without_id}/)
1124
+ replace_in_file(migration_file_name, "foreign_key: true", "foreign_key: {to_table: \"#{attribute.options[:class_name].tableize.tr("/", "_")}\"}", /add_reference :#{child.underscore.pluralize.tr("/", "_")}, :#{attribute.name_without_id}/)
1254
1125
 
1255
1126
  modified_migration = true
1256
1127
  else
1257
- add_additional_step :yellow, "We would have expected there to be a migration that defined `#{expected_reference}`, but we didn't find one. Where was the reference added to this model? It's _probably_ the original creation of the table. Either way, you need to rollback, change \"foreign_key: true\" to \"foreign_key: {to_table: '#{attribute_options[:class_name].tableize.tr("/", "_")}'}\" for this column, and re-run the migration."
1128
+ add_additional_step :yellow, "We would have expected there to be a migration that defined `#{expected_reference}`, but we didn't find one. Where was the reference added to this model? It's _probably_ the original creation of the table. Either way, you need to rollback, change \"foreign_key: true\" to \"foreign_key: {to_table: '#{attribute.options[:class_name].tableize.tr("/", "_")}'}\" for this column, and re-run the migration."
1258
1129
  end
1259
1130
  end
1260
1131
 
1261
- optional_line = ", optional: true" unless is_required
1132
+ optional_line = ", optional: true" unless attribute.is_required?
1262
1133
 
1263
1134
  # if the `belongs_to` is already there from `rails g model`..
1264
1135
  scaffold_replace_line_in_file(
1265
1136
  "./app/models/scaffolding/completely_concrete/tangible_thing.rb",
1266
1137
  class_name_matches ?
1267
- "belongs_to :#{name_without_id}#{optional_line}" :
1268
- "belongs_to :#{name_without_id}, class_name: \"#{attribute_options[:class_name]}\"#{optional_line}",
1269
- "belongs_to :#{name_without_id}"
1138
+ "belongs_to :#{attribute.name_without_id}#{optional_line}" :
1139
+ "belongs_to :#{attribute.name_without_id}, class_name: \"#{attribute.options[:class_name]}\"#{optional_line}",
1140
+ "belongs_to :#{attribute.name_without_id}"
1270
1141
  )
1271
1142
 
1272
1143
  # if it wasn't there, the replace will not have done anything, so we insert it entirely.
@@ -1274,8 +1145,8 @@ class Scaffolding::Transformer
1274
1145
  scaffold_add_line_to_file(
1275
1146
  "./app/models/scaffolding/completely_concrete/tangible_thing.rb",
1276
1147
  class_name_matches ?
1277
- "belongs_to :#{name_without_id}#{optional_line}" :
1278
- "belongs_to :#{name_without_id}, class_name: \"#{attribute_options[:class_name]}\"#{optional_line}",
1148
+ "belongs_to :#{attribute.name_without_id}#{optional_line}" :
1149
+ "belongs_to :#{attribute.name_without_id}, class_name: \"#{attribute.options[:class_name]}\"#{optional_line}",
1279
1150
  BELONGS_TO_HOOK,
1280
1151
  prepend: true
1281
1152
  )
@@ -1286,9 +1157,9 @@ class Scaffolding::Transformer
1286
1157
  end
1287
1158
 
1288
1159
  # Add `default: false` to boolean migrations.
1289
- if boolean_buttons
1160
+ if attribute.is_boolean?
1290
1161
  # Give priority to crud-field migrations if they exist.
1291
- add_column_reference = "add_column :#{class_names_transformer.table_name}, :#{name}"
1162
+ add_column_reference = "add_column :#{class_names_transformer.table_name}, :#{attribute.name}"
1292
1163
  create_table_reference = "create_table :#{class_names_transformer.table_name}"
1293
1164
  confirmation_migration_file_name = `grep "#{add_column_reference}" db/migrate/*`.split(":").first
1294
1165
  confirmation_migration_file_name ||= `grep "#{create_table_reference}" db/migrate/*`.split(":").first
@@ -1297,7 +1168,7 @@ class Scaffolding::Transformer
1297
1168
  File.open(confirmation_migration_file_name) do |migration_file|
1298
1169
  old_lines = migration_file.readlines
1299
1170
  old_lines.each do |line|
1300
- target_attribute = line.match?(/:#{class_names_transformer.table_name}, :#{name}, :boolean/) || line.match?(/\s*t\.boolean :#{name}/)
1171
+ target_attribute = line.match?(/:#{class_names_transformer.table_name}, :#{attribute.name}, :boolean/) || line.match?(/\s*t\.boolean :#{attribute.name}/)
1301
1172
  if target_attribute
1302
1173
  old_line = line
1303
1174
  new_line = "#{old_line.chomp}, default: false\n"
@@ -1315,40 +1186,40 @@ class Scaffolding::Transformer
1315
1186
 
1316
1187
  unless cli_options["skip-model"]
1317
1188
 
1318
- if is_required && !is_belongs_to
1319
- scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "validates :#{name}, presence: true", VALIDATIONS_HOOK, prepend: true)
1189
+ if attribute.is_required? && !attribute.is_belongs_to?
1190
+ scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "validates :#{attribute.name}, presence: true", VALIDATIONS_HOOK, prepend: true)
1320
1191
  end
1321
1192
 
1322
- case type
1193
+ case attribute.type
1323
1194
  when "file_field"
1324
- remove_file_methods = if is_multiple
1195
+ remove_file_methods = if attribute.is_multiple?
1325
1196
  <<~RUBY
1326
- def #{name}_removal?
1327
- #{name}_removal&.any?
1197
+ def #{attribute.name}_removal?
1198
+ #{attribute.name}_removal&.any?
1328
1199
  end
1329
1200
 
1330
- def remove_#{name}
1331
- #{name}_attachments.where(id: #{name}_removal).map(&:purge)
1201
+ def remove_#{attribute.name}
1202
+ #{attribute.name}_attachments.where(id: #{attribute.name}_removal).map(&:purge)
1332
1203
  end
1333
1204
 
1334
- def #{name}=(attachables)
1205
+ def #{attribute.name}=(attachables)
1335
1206
  attachables = Array(attachables).compact_blank
1336
1207
 
1337
1208
  if attachables.any?
1338
- attachment_changes["#{name}"] =
1339
- ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, #{name}.blobs + attachables)
1209
+ attachment_changes["#{attribute.name}"] =
1210
+ ActiveStorage::Attached::Changes::CreateMany.new("#{attribute.name}", self, #{attribute.name}.blobs + attachables)
1340
1211
  end
1341
1212
  end
1342
1213
 
1343
1214
  RUBY
1344
1215
  else
1345
1216
  <<~RUBY
1346
- def #{name}_removal?
1347
- #{name}_removal.present?
1217
+ def #{attribute.name}_removal?
1218
+ #{attribute.name}_removal.present?
1348
1219
  end
1349
1220
 
1350
- def remove_#{name}
1351
- #{name}.purge
1221
+ def remove_#{attribute.name}
1222
+ #{attribute.name}.purge
1352
1223
  end
1353
1224
  RUBY
1354
1225
  end
@@ -1358,7 +1229,7 @@ class Scaffolding::Transformer
1358
1229
  # HAS_ONE_HOOK or the HAS_MANY_HOOK. We move the string here so it's scaffolded above the proper hook.
1359
1230
  model_file_path = transform_string("./app/models/scaffolding/completely_concrete/tangible_thing.rb")
1360
1231
  model_contents = File.readlines(model_file_path)
1361
- reflection_declaration = is_multiple ? "has_many_attached :#{name}" : "has_one_attached :#{name}"
1232
+ reflection_declaration = attribute.is_multiple? ? "has_many_attached :#{attribute.name}" : "has_one_attached :#{attribute.name}"
1362
1233
 
1363
1234
  # Save the file without the hook so we can write it via the `scaffold_add_line_to_file` method below.
1364
1235
  model_without_attached_hook = model_contents.reject.each { |line| line.include?(reflection_declaration) }
@@ -1366,18 +1237,18 @@ class Scaffolding::Transformer
1366
1237
  model_without_attached_hook.each { |line| f.write(line) }
1367
1238
  end
1368
1239
 
1369
- hook_type = is_multiple ? HAS_MANY_HOOK : HAS_ONE_HOOK
1240
+ hook_type = attribute.is_multiple? ? HAS_MANY_HOOK : HAS_ONE_HOOK
1370
1241
  scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", reflection_declaration, hook_type, prepend: true)
1371
1242
 
1372
1243
  # TODO: We may need to edit these depending on how we save multiple files.
1373
- scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "attr_accessor :#{name}_removal", ATTR_ACCESSORS_HOOK, prepend: true)
1244
+ scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "attr_accessor :#{attribute.name}_removal", ATTR_ACCESSORS_HOOK, prepend: true)
1374
1245
  scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", remove_file_methods, METHODS_HOOK, prepend: true)
1375
- scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "after_validation :remove_#{name}, if: :#{name}_removal?", CALLBACKS_HOOK, prepend: true)
1246
+ scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "after_validation :remove_#{attribute.name}, if: :#{attribute.name}_removal?", CALLBACKS_HOOK, prepend: true)
1376
1247
  when "trix_editor"
1377
- scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "has_rich_text :#{name}", HAS_ONE_HOOK, prepend: true)
1248
+ scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "has_rich_text :#{attribute.name}", HAS_ONE_HOOK, prepend: true)
1378
1249
  when "buttons"
1379
- if boolean_buttons
1380
- scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "validates :#{name}, inclusion: [true, false]", VALIDATIONS_HOOK, prepend: true)
1250
+ if attribute.is_boolean?
1251
+ scaffold_add_line_to_file("./app/models/scaffolding/completely_concrete/tangible_thing.rb", "validates :#{attribute.name}, inclusion: [true, false]", VALIDATIONS_HOOK, prepend: true)
1381
1252
  end
1382
1253
  end
1383
1254
 
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.3.20
4
+ version: 1.3.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-31 00:00:00.000000000 Z
11
+ date: 2023-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: standard
@@ -162,6 +162,7 @@ files:
162
162
  - lib/bullet_train/super_scaffolding/version.rb
163
163
  - lib/bullet_train/terminal_commands.rb
164
164
  - lib/scaffolding.rb
165
+ - lib/scaffolding/attribute.rb
165
166
  - lib/scaffolding/block_manipulator.rb
166
167
  - lib/scaffolding/class_names_transformer.rb
167
168
  - lib/scaffolding/file_manipulator.rb