tramway-core 1.17.2.1 → 1.17.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/app/controllers/tramway/core/application_controller.rb +12 -12
  4. data/app/decorators/tramway/core/application_decorator.rb +12 -26
  5. data/app/decorators/tramway/core/associations/class_helper.rb +7 -13
  6. data/app/decorators/tramway/core/associations/object_helper.rb +28 -2
  7. data/app/decorators/tramway/core/attributes/view_helper.rb +26 -0
  8. data/app/decorators/tramway/core/concerns/attributes_decorator_helper.rb +47 -26
  9. data/app/decorators/tramway/core/delegating/class_helper.rb +2 -0
  10. data/app/forms/tramway/core/application_form.rb +96 -145
  11. data/app/forms/tramway/core/application_forms/association_object_helpers.rb +27 -0
  12. data/app/forms/tramway/core/application_forms/constant_class_actions.rb +7 -0
  13. data/app/forms/tramway/core/application_forms/constant_object_actions.rb +20 -0
  14. data/app/forms/tramway/core/application_forms/properties_object_helper.rb +23 -0
  15. data/app/forms/tramway/core/extendable_form.rb +3 -70
  16. data/app/forms/tramway/core/extendable_forms_helpers/class_builder.rb +34 -0
  17. data/app/forms/tramway/core/extendable_forms_helpers/ignored_properties_helper.rb +11 -0
  18. data/app/forms/tramway/core/extendable_forms_helpers/more_properties_helper.rb +27 -0
  19. data/app/forms/tramway/core/extendable_forms_helpers/properties_helper.rb +16 -0
  20. data/app/forms/tramway/core/extendable_forms_helpers/submit/class_helpers.rb +18 -0
  21. data/app/forms/tramway/core/extendable_forms_helpers/submit/object_helpers.rb +7 -0
  22. data/app/forms/tramway/core/extendable_forms_helpers/validators.rb +40 -0
  23. data/app/helpers/tramway/core/application_helper.rb +2 -0
  24. data/app/helpers/tramway/core/copy_to_clipboard_helper.rb +6 -10
  25. data/app/helpers/tramway/core/inputs_helper.rb +93 -0
  26. data/app/helpers/tramway/core/title_helper.rb +17 -22
  27. data/app/models/tramway/core/application_record.rb +38 -40
  28. data/app/uploaders/image_defaults.rb +2 -1
  29. data/app/uploaders/photo_uploader.rb +8 -8
  30. data/app/views/tramway/core/shared/_input.html.haml +26 -0
  31. data/config/initializers/plurals.rb +2 -2
  32. data/lib/tramway/collection.rb +4 -6
  33. data/lib/tramway/collections.rb +4 -0
  34. data/lib/tramway/collections/helper.rb +15 -14
  35. data/lib/tramway/core.rb +22 -22
  36. data/lib/tramway/core/engine.rb +4 -8
  37. data/lib/tramway/core/generators.rb +6 -0
  38. data/lib/tramway/core/generators/install_generator.rb +17 -17
  39. data/lib/tramway/core/version.rb +1 -1
  40. data/lib/tramway/error.rb +12 -1
  41. data/lib/yaml/errors.yml +35 -0
  42. metadata +22 -3
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ApplicationForms::AssociationObjectHelpers
4
+ def define_association_method(association, class_name)
5
+ if class_name.is_a? Array
6
+ define_polymorphic_association association, class_name
7
+ else
8
+ self.class.send(:define_method, "#{association}=") do |value|
9
+ super class_name.find value
10
+ end
11
+ end
12
+ end
13
+
14
+ def define_polymorphic_association(association, _class_name)
15
+ self.class.send(:define_method, "#{association}=") do |value|
16
+ association_class = value.split('_')[0..-2].join('_').camelize
17
+ association_class = association_class.constantize if association_class.is_a? String
18
+ if association_class.nil?
19
+ Tramway::Error.raise_error :tramway, :core, :application_form, :initialize, :polymorphic_class_is_nil,
20
+ association_name: association
21
+ else
22
+ super association_class.find value.split('_')[-1]
23
+ send "#{association}_type=", association_class.to_s
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ApplicationForms::ConstantClassActions
4
+ def validation_group_class
5
+ ActiveModel
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ApplicationForms::ConstantObjectActions
4
+ def delegating(object)
5
+ %i[to_key errors].each { |method| self.class.send(:define_method, method) { object.send method } }
6
+ end
7
+
8
+ def build_errors; end
9
+
10
+ def attributes
11
+ properties.reduce({}) do |hash, property|
12
+ value = if model.respond_to? :values
13
+ model.values[property.first.to_s]
14
+ else
15
+ model.send(property.first.to_s)
16
+ end
17
+ hash.merge! property.first => value
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ApplicationForms::PropertiesObjectHelper
4
+ def form_properties(**args)
5
+ @form_properties = args
6
+ end
7
+
8
+ def form_properties_additional(**args)
9
+ @form_properties_additional = args
10
+ end
11
+
12
+ def properties
13
+ return @form_properties if @form_properties
14
+
15
+ yaml_config_file_path = Rails.root.join('app', 'forms', "#{self.class.name.underscore}.yml")
16
+
17
+ return [] unless File.exist? yaml_config_file_path
18
+
19
+ @form_properties = YAML.load_file(yaml_config_file_path).deep_symbolize_keys
20
+ @form_properties.deep_merge! @form_properties_additional if @form_properties_additional
21
+ @form_properties
22
+ end
23
+ end
@@ -2,80 +2,13 @@
2
2
 
3
3
  class Tramway::Core::ExtendableForm
4
4
  class << self
5
+ include Tramway::Core::ExtendableFormsHelpers::ClassBuilder
6
+
5
7
  def new(name, simple_properties: {}, **more_properties)
6
8
  if Object.const_defined? name
7
9
  name.constantize
8
10
  else
9
- Object.const_set(name, Class.new(::Tramway::Core::ApplicationForm) do
10
- properties(*simple_properties.keys) if simple_properties.keys.any?
11
-
12
- define_method 'submit' do |params|
13
- model.values ||= {}
14
- extended_params = params.except(*simple_properties.keys).except(*jsonb_ignored_properties(more_properties))
15
- params.each do |key, value|
16
- method_name = "#{key}="
17
- send method_name, value if respond_to?(method_name)
18
- end
19
- model.values = extended_params.permit!.to_h.reduce(model.values) do |hash, pair|
20
- hash.merge! pair[0] => pair[1]
21
- end
22
- super(params) && model.errors.empty?
23
- end
24
-
25
- define_method 'properties' do
26
- hash = simple_properties.each_with_object({}) do |property, h|
27
- h.merge! property[0] => property[1] unless model.class.state_machines.keys.include?(property[0])
28
- end
29
- more_properties.reduce(hash) do |h, property|
30
- h.merge! property[0] => {
31
- extended_form_property: property[1][:object]
32
- }
33
- end
34
- end
35
-
36
- more_properties.each do |property|
37
- define_method property[0] do
38
- model.values[property[0]] if model.values
39
- end
40
-
41
- case property[1][:object].field_type
42
- when 'file'
43
- field = property[1][:object]
44
- define_method "#{property[0]}=" do |value|
45
- file_instance = property[1][:association_model].find_or_create_by "#{model.class.name.underscore}_id" => model.id, "#{field.class.name.underscore}_id" => field.id
46
- file_instance.file = value
47
- file_instance.save!
48
- end
49
- else
50
- next unless property[1][:validates].present?
51
-
52
- define_method "#{property[0]}=" do |value|
53
- property[1][:validates].each do |pair|
54
- case pair[0].to_s
55
- when 'presence'
56
- validator_object = "#{pair[0].to_s.camelize}Validator".constantize.new(attributes: :not_blank)
57
- if pair[1] == 'true' && !validator_object.send(:valid?, value)
58
- model.errors.add property[0],
59
- I18n.t("activerecord.errors.models.#{model.class.name.underscore}.attributes.default.#{pair[0]}", value: value)
60
- end
61
- when 'inclusion'
62
- in_array = pair[1][:in]
63
- unless value.in? in_array
64
- model.errors.add property[0],
65
- I18n.t("activerecord.errors.models.#{model.class.name.underscore}.attributes.default.#{pair[0]}", value: value)
66
- end
67
- end
68
- end
69
- end
70
- end
71
- end
72
-
73
- define_method :jsonb_ignored_properties do |properties|
74
- properties.map do |property|
75
- property[0].to_s if property[1][:object].field_type == 'file'
76
- end.compact
77
- end
78
- end)
11
+ build_form_class name, simple_properties, more_properties
79
12
  end
80
13
  end
81
14
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ExtendableFormsHelpers::ClassBuilder
4
+ def build_form_class(name, simple_properties, more_properties)
5
+ Object.const_set(name, Class.new(::Tramway::Core::ApplicationForm) do
6
+ properties(*simple_properties.keys) if simple_properties.keys.any?
7
+
8
+ include Tramway::Core::ExtendableFormsHelpers::Submit::ObjectHelpers
9
+ include Tramway::Core::ExtendableFormsHelpers::Validators
10
+ extend Tramway::Core::ExtendableFormsHelpers::Submit::ClassHelpers
11
+ extend Tramway::Core::ExtendableFormsHelpers::PropertiesHelper
12
+ extend Tramway::Core::ExtendableFormsHelpers::MorePropertiesHelper
13
+ extend Tramway::Core::ExtendableFormsHelpers::IgnoredPropertiesHelper
14
+
15
+ define_submit_method simple_properties, more_properties
16
+ define_properties_method simple_properties, more_properties
17
+ define_ignored_properties_method
18
+
19
+ more_properties.each do |property|
20
+ define_property_method property[0]
21
+
22
+ case property[1][:object].field_type
23
+ when 'file'
24
+ field = property[1][:object]
25
+ define_file_property_assignment_method property field
26
+ else
27
+ next unless property[1][:validates].present?
28
+
29
+ define_assignment_method property
30
+ end
31
+ end
32
+ end)
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ExtendableFormsHelpers::IgnoredPropertiesHelper
4
+ def define_ignored_properties_method
5
+ define_method :jsonb_ignored_properties do |properties|
6
+ properties.map do |property|
7
+ property[0].to_s if property[1][:object].field_type == 'file'
8
+ end.compact
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ExtendableFormsHelpers::MorePropertiesHelper
4
+ def define_property_method(property_name)
5
+ define_method property_name do
6
+ model.values[property_name] if model.values
7
+ end
8
+ end
9
+
10
+ def define_assignment_method(property)
11
+ define_method "#{property[0]}=" do |value|
12
+ property[1][:validates].each do |pair|
13
+ make_validates property[0], pair, value
14
+ end
15
+ end
16
+ end
17
+
18
+ def define_file_property_assignment_method(property, field)
19
+ define_method "#{property[0]}=" do |value|
20
+ file_instance = property[1][:association_model].find_or_create_by(
21
+ "#{model.class.name.underscore}_id" => model.id, "#{field.class.name.underscore}_id" => field.id
22
+ )
23
+ file_instance.file = value
24
+ file_instance.save!
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ExtendableFormsHelpers::PropertiesHelper
4
+ def define_properties_method(simple_properties, more_properties)
5
+ define_method 'properties' do
6
+ hash = simple_properties.each_with_object({}) do |property, h|
7
+ h.merge! property[0] => property[1] unless model.class.state_machines.keys.include?(property[0])
8
+ end
9
+ more_properties.reduce(hash) do |h, property|
10
+ h.merge! property[0] => {
11
+ extended_form_property: property[1][:object]
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ExtendableFormsHelpers::Submit::ClassHelpers
4
+ def define_submit_method(simple_properties, more_properties)
5
+ define_method 'submit' do |params|
6
+ model.values ||= {}
7
+ extended_params = extended(simple_properties, more_properties, params)
8
+ params.each do |key, value|
9
+ method_name = "#{key}="
10
+ send method_name, value if respond_to?(method_name)
11
+ end
12
+ model.values = extended_params.reduce(model.values) do |hash, pair|
13
+ hash.merge! pair[0] => pair[1]
14
+ end
15
+ super(params) && model.errors.empty?
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ExtendableFormsHelpers::Submit::ObjectHelpers
4
+ def extended(simple_properties, more_properties, params)
5
+ params.except(*simple_properties.keys).except(*jsonb_ignored_properties(more_properties)).permit!.to_h
6
+ end
7
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::ExtendableFormsHelpers::Validators
4
+ def make_validates(property_name, validation, value)
5
+ case validation[0].to_s
6
+ when 'presence'
7
+ presence_validator property_name, validation, value
8
+ when 'inclusion'
9
+ inclusion_validator property_name, validation, value
10
+ end
11
+ end
12
+
13
+ def presence_validator(property_name, validation, value)
14
+ validator_object = PresenceValidator.new(attributes: :not_blank)
15
+ return if validation[1] == 'false'
16
+ return if validation[1] == 'true' && validator_object.send(:valid?, value)
17
+
18
+ model.errors.add(
19
+ property_name,
20
+ I18n.t(
21
+ "activerecord.errors.models.#{model.class.name.underscore}.attributes.default.#{validation[0]}",
22
+ value: value
23
+ )
24
+ )
25
+ end
26
+
27
+ def inclusion_validator(property_name, validation, value)
28
+ in_array = validation[1][:in]
29
+
30
+ return if value.in? in_array
31
+
32
+ model.errors.add(
33
+ property_name,
34
+ I18n.t(
35
+ "activerecord.errors.models.#{model.class.name.underscore}.attributes.default.#{validation[0]}",
36
+ value: value
37
+ )
38
+ )
39
+ end
40
+ end
@@ -0,0 +1,2 @@
1
+ module Tramway::Core::ApplicationHelper
2
+ end
@@ -1,15 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Tramway
4
- module Core
5
- module CopyToClipboardHelper
6
- def copy_to_clipboard(id)
7
- button_tag class: 'btn btn-info clipboard-btn',
8
- data: { clipboard_action: 'copy', clipboard_target: "##{id}" },
9
- style: 'margin-left: 15px' do
10
- fa_icon 'copy'
11
- end
12
- end
3
+ module Tramway::Core::CopyToClipboardHelper
4
+ def copy_to_clipboard(id)
5
+ button_tag class: 'btn btn-info clipboard-btn',
6
+ data: { clipboard_action: 'copy', clipboard_target: "##{id}" },
7
+ style: 'margin-left: 15px' do
8
+ fa_icon 'copy'
13
9
  end
14
10
  end
15
11
  end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::InputsHelper
4
+ def association_params(form_object:, property:, value:, object:, options: {})
5
+ full_class_name_association = form_object.class.full_class_name_association(property)
6
+ unless full_class_name_association
7
+ raise "It seems you've defined association attributes with `property` method. Please, use `association` method. `association :#{property}`"
8
+ end
9
+ if full_class_name_association.is_a? Array
10
+ raise "It seems you've used `association` input type in the Form. Please, use `polymorphic_association` type. `#{property}: :polymorphic_association`"
11
+ end
12
+
13
+ {
14
+ label: false,
15
+ input_html: {
16
+ name: "#{object}[#{property}]",
17
+ id: "#{object}_#{property}",
18
+ value: (form_object.send(property) || form_object.model.send("#{property}_id") || value)
19
+ },
20
+ selected: (form_object.model.send("#{property}_id") || value),
21
+ collection: full_class_name_association.active.map do |obj|
22
+ decorator_class(full_class_name_association).decorate obj
23
+ end.sort_by(&:name)
24
+ }.merge options
25
+ end
26
+
27
+ def polymorphic_association_params(object:, form_object:, property:, value:, options: {})
28
+ full_class_names = form_object.model.class.send("#{property}_type").values.map &:constantize
29
+ collection = full_class_names.map do |class_name|
30
+ class_name.active.map do |obj|
31
+ decorator_class(class_name).decorate obj
32
+ end
33
+ end.flatten.sort_by { |obj| obj.name.to_s }
34
+ builded_value = if form_object.send(property).present?
35
+ "#{form_object.send(property).class.to_s.underscore}_#{form_object.send(property).id}"
36
+ else
37
+ "#{value[:type]&.underscore}_#{value[:id]}"
38
+ end
39
+ {
40
+ as: :select,
41
+ label: false,
42
+ input_html: {
43
+ name: "#{object}[#{property}]",
44
+ id: "#{object}_#{property}",
45
+ value: builded_value
46
+ },
47
+ selected: builded_value,
48
+ collection: collection,
49
+ label_method: lambda do |obj|
50
+ "#{obj.class.model_name.human} | #{obj.name}"
51
+ end,
52
+ value_method: lambda do |obj|
53
+ "#{obj.class.to_s.underscore.sub(/_decorator$/, '')}_#{obj.id}"
54
+ end
55
+ }.merge options
56
+ end
57
+
58
+ def value_from_params(model_class:, property:, type:)
59
+ case type
60
+ when :polymorphic_association, 'polymorphic_association'
61
+ {
62
+ id: params.dig(model_class.to_s.underscore, property.to_s),
63
+ type: params.dig(model_class.to_s.underscore, "#{property}_type")
64
+ }
65
+ else
66
+ params.dig(model_class.to_s.underscore, property.to_s)
67
+ end
68
+ end
69
+
70
+ def default_params(property:, object:, form_object:, value:, options: {})
71
+ {
72
+ label: false,
73
+ input_html: {
74
+ name: "#{object}[#{property}]",
75
+ id: "#{object}_#{property}",
76
+ value: (form_object.send(property) || form_object.model.send(property) || value)
77
+ },
78
+ selected: (form_object.model.send(property) || value)
79
+ }.merge options
80
+ end
81
+
82
+ def else_params(property:, object:, type:, form_object:, value:, options: {})
83
+ {
84
+ as: type,
85
+ label: false,
86
+ input_html: {
87
+ name: "#{object}[#{property}]",
88
+ id: "#{object}_#{property}",
89
+ value: (form_object.send(property) || form_object.model.send(property) || value)
90
+ }
91
+ }.merge options
92
+ end
93
+ end