-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce
Scaffolding::Attribute
(#124)
* Add Scaffolding::Attribute class * Use Scaffolding::Attribute in Transformer * Add comment to Scaffolding::Attribute description * Extract more logic from Transformer to Scaffolding::Attribute * Remove line builder for now * Edit method in Scaffolding::Attribute to avoid large spacing from standardrb * Fixing Standard Ruby * Fixing Standard Ruby * Add proper question method for attribute object * Add number field to list of valid attributes * Add is_boolean? to the Attribute class * Fix bugs related to the Attribute class * Explicitly use is_boolean? method for attribute instance * Ensure is_boolean? checks against original type * Fixing Standard Ruby * Fix one more area in the transformer for attribute.name * Account for multiple file attachment links in show page for file field * Undo unnecessary change to file partial * Scaffold files partial if attribute is multiple
- Loading branch information
Showing
2 changed files
with
366 additions
and
281 deletions.
There are no files selected for viewing
214 changes: 214 additions & 0 deletions
214
bullet_train-super_scaffolding/lib/scaffolding/attribute.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
# This class provides helpful methods for determining when and how we | ||
# should apply logic for each attribute when Super Scaffolding a new model. | ||
# | ||
# For example, we determine which association to use based off of the | ||
# attribute passed to `bin/super-scaffold` as opposed to the models (classes) themselves. | ||
# Rails has ActiveRecord::Reflection::AssociationReflection, but this is only useful | ||
# after we've declared the associations. Since we haven't declared the associations in | ||
# the models yet, we determine the association for the attribute based on its suffix. | ||
# | ||
# i.e. - bin/super-scaffold crud Project tag_ids:super_select{class_name=Projects::Tag} | ||
# Here, we determine the association for the `tag_ids` attribute by its suffix, `_ids`. | ||
|
||
class Scaffolding::Attribute | ||
attr_accessor :name, :type, :options, :scaffolding_type, :attribute_index, :original_type | ||
|
||
# @attribute_definition [String]: The raw attribute name, type, and options that are passed to bin/super-scaffold. | ||
# @scaffoldng_type [Key]: The type of scaffolder we're using to Super Scaffold the model as a whole. | ||
# @attribute_index [Integer]: The index taken from the Array of all the attributes of the model. | ||
def initialize(attribute_definition, scaffolding_type, attribute_index) | ||
parts = attribute_definition.split(":") | ||
self.name = parts.shift | ||
self.type, self.options = get_type_and_options_from(parts) | ||
self.scaffolding_type = scaffolding_type | ||
self.attribute_index = attribute_index | ||
|
||
# We mutate `type` within the transformer, so `original_type` allows us | ||
# to access what the developer originally passed to bin/super-scaffold. | ||
# (Refer to sql_type_to_field_type_mapping in the transformer) | ||
self.original_type = type | ||
|
||
# Ensure `options` is a hash. | ||
self.options = if options | ||
options.split(",").map { |s| | ||
option_name, option_value = s.split("=") | ||
[option_name.to_sym, option_value || true] | ||
}.to_h | ||
else | ||
{} | ||
end | ||
|
||
options[:label] ||= "label_string" | ||
end | ||
|
||
def is_first_attribute? | ||
attribute_index == 0 && scaffolding_type == :crud | ||
end | ||
|
||
# if this is the first attribute of a newly scaffolded model, that field is required. | ||
def is_required? | ||
return false if type == "file_field" | ||
options[:required] || is_first_attribute? | ||
end | ||
|
||
def is_association? | ||
is_belongs_to? || is_has_many? | ||
end | ||
|
||
def is_belongs_to? | ||
is_id? && !is_vanilla? | ||
end | ||
|
||
def is_has_many? | ||
is_ids? && !is_vanilla? | ||
end | ||
|
||
def is_vanilla? | ||
options&.key?(:vanilla) | ||
end | ||
|
||
def is_multiple? | ||
options&.key?(:multiple) || is_has_many? | ||
end | ||
|
||
def is_boolean? | ||
original_type == "boolean" | ||
end | ||
|
||
# Sometimes we need all the magic of a `*_id` field, but without the scoping stuff. | ||
# Possibly only ever used internally by `join-model`. | ||
def is_unscoped? | ||
options[:unscoped] | ||
end | ||
|
||
def is_id? | ||
name.match?(/_id$/) | ||
end | ||
|
||
def is_ids? | ||
name.match?(/_ids$/) | ||
end | ||
|
||
def name_without_id | ||
name.gsub(/_id$/, "") | ||
end | ||
|
||
def name_without_ids | ||
name.gsub(/_ids$/, "").pluralize | ||
end | ||
|
||
def name_without_id_suffix | ||
if is_ids? | ||
name_without_ids | ||
elsif is_id? | ||
name_without_id | ||
else | ||
name | ||
end | ||
end | ||
|
||
def title_case | ||
if is_ids? | ||
# user_ids should be 'Users' | ||
name_without_ids.humanize.titlecase | ||
elsif is_id? | ||
name_without_id.humanize.titlecase | ||
else | ||
name.humanize.titlecase | ||
end | ||
end | ||
|
||
def collection_name | ||
is_ids? ? name_without_ids : name_without_id.pluralize | ||
end | ||
|
||
# Field on the show view. | ||
def partial_name | ||
return options[:attribute] if options[:attribute] | ||
|
||
case type | ||
when "trix_editor", "ckeditor" | ||
"html" | ||
when "buttons", "super_select", "options", "boolean" | ||
if is_ids? | ||
"has_many" | ||
elsif is_id? | ||
"belongs_to" | ||
else | ||
"option#{"s" if is_multiple?}" | ||
end | ||
when "cloudinary_image" | ||
options[:height] = 200 | ||
"image" | ||
when "phone_field" | ||
"phone_number" | ||
when "date_field" | ||
"date" | ||
when "date_and_time_field" | ||
"date_and_time" | ||
when "email_field" | ||
"email" | ||
when "emoji_field" | ||
"text" | ||
when "color_picker" | ||
"code" | ||
when "text_field" | ||
"text" | ||
when "text_area" | ||
"text" | ||
when "file_field" | ||
"file#{"s" if is_multiple?}" | ||
when "password_field" | ||
"text" | ||
when "number_field" | ||
"number" | ||
else | ||
raise "Invalid field type: #{type}." | ||
end | ||
end | ||
|
||
def default_value | ||
case type | ||
when "text_field", "password_field", "text_area" | ||
"'Alternative String Value'" | ||
when "email_field" | ||
"'[email protected]'" | ||
when "phone_field" | ||
"'+19053871234'" | ||
when "color_picker" | ||
"'#47E37F'" | ||
end | ||
end | ||
|
||
def special_processing | ||
case type | ||
when "date_field" | ||
"assign_date(strong_params, :#{name})" | ||
when "date_and_time_field" | ||
"assign_date_and_time(strong_params, :#{name})" | ||
when "buttons" | ||
if is_boolean? | ||
"assign_boolean(strong_params, :#{name})" | ||
elsif is_multiple? | ||
"assign_checkboxes(strong_params, :#{name})" | ||
end | ||
when "options" | ||
if is_multiple? | ||
"assign_checkboxes(strong_params, :#{name})" | ||
end | ||
when "super_select" | ||
if is_boolean? | ||
"assign_boolean(strong_params, :#{name})" | ||
elsif is_multiple? | ||
"assign_select_options(strong_params, :#{name})" | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
# i.e. - multiple_buttons:buttons{multiple} | ||
def get_type_and_options_from(parts) | ||
parts.join(":").scan(/^(.*){(.*)}/).first || parts.join(":") | ||
end | ||
end |
Oops, something went wrong.