wallaby-active_record 0.1.1

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 (24) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +31 -0
  4. data/lib/adapters/wallaby/active_record/cancancan_provider.rb +9 -0
  5. data/lib/adapters/wallaby/active_record/default_provider.rb +9 -0
  6. data/lib/adapters/wallaby/active_record/model_decorator/fields_builder/association_builder.rb +52 -0
  7. data/lib/adapters/wallaby/active_record/model_decorator/fields_builder/polymorphic_builder.rb +52 -0
  8. data/lib/adapters/wallaby/active_record/model_decorator/fields_builder/sti_builder.rb +50 -0
  9. data/lib/adapters/wallaby/active_record/model_decorator/fields_builder.rb +66 -0
  10. data/lib/adapters/wallaby/active_record/model_decorator/title_field_finder.rb +32 -0
  11. data/lib/adapters/wallaby/active_record/model_decorator.rb +155 -0
  12. data/lib/adapters/wallaby/active_record/model_finder.rb +45 -0
  13. data/lib/adapters/wallaby/active_record/model_pagination_provider.rb +34 -0
  14. data/lib/adapters/wallaby/active_record/model_service_provider/normalizer.rb +54 -0
  15. data/lib/adapters/wallaby/active_record/model_service_provider/permitter.rb +68 -0
  16. data/lib/adapters/wallaby/active_record/model_service_provider/querier/transformer.rb +78 -0
  17. data/lib/adapters/wallaby/active_record/model_service_provider/querier.rb +172 -0
  18. data/lib/adapters/wallaby/active_record/model_service_provider/validator.rb +37 -0
  19. data/lib/adapters/wallaby/active_record/model_service_provider.rb +166 -0
  20. data/lib/adapters/wallaby/active_record/pundit_provider.rb +19 -0
  21. data/lib/adapters/wallaby/active_record.rb +7 -0
  22. data/lib/wallaby/active_record/version.rb +7 -0
  23. data/lib/wallaby/active_record.rb +38 -0
  24. metadata +152 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 92d10c82a2195970de2aa77c1ad8cd776c03fe68c264678e962bab91a01e7e0d
4
+ data.tar.gz: 629a2839e2e558ca281f2a48777fbfa20a0a7a77a7497ca8b2a185a5d731d8ff
5
+ SHA512:
6
+ metadata.gz: 71820d2020d94d02e2a02063c70faa630a58ae3e2f6ca0422f043a9f3042cf4eeedf336fe8d8f4fcb37f3c0471715427a34329dfacacd27444deb45aef57d04f
7
+ data.tar.gz: 392802282ef7e87b47c7018544f1324fea3d6750304d88e0d28b1ccf7a27d869cbd68dfc4ed58d03610f9a48b80b10f0a55f37bfdbb3af82ea7234997d71930c
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010-2019 Google LLC. http://angular.io/license
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Wallaby::ActiveRecord
2
+
3
+ This gem contains the ActiveRecord ORM adapter for Wallaby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'wallaby-active_record'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```shell
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```shell
22
+ $ gem install wallaby-active_record
23
+ ```
24
+
25
+ ## Contributing
26
+
27
+ Bug reports and pull requests are welcome on GitHub at https://github.com/wallaby-rails/wallaby-active_record.
28
+
29
+ ## License
30
+
31
+ This project is [MIT Licensed](LICENSE)
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ # Cancancan provider for ActiveRecord
6
+ class CancancanProvider < CancancanAuthorizationProvider
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ # Default provider for ActiveRecord
6
+ class DefaultProvider < DefaultAuthorizationProvider
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ class ModelDecorator
6
+ class FieldsBuilder
7
+ # To build the metadata for associations
8
+ class AssociationBuilder
9
+ # Update the metadata
10
+ # @param metadata [Hash]
11
+ # @param reflection [ActiveRecord::Reflection]
12
+ def update(metadata, reflection)
13
+ type = reflection.macro
14
+ metadata[:is_association] = true
15
+ metadata[:sort_disabled] = true
16
+ metadata[:is_through] = through?(reflection)
17
+ metadata[:has_scope] = scope?(reflection)
18
+ metadata[:foreign_key] = foreign_key_for(reflection, type)
19
+ end
20
+
21
+ private
22
+
23
+ # @param reflection [ActiveRecord::Reflection]
24
+ # @param type [Symbol]
25
+ # @return [String] foreign key
26
+ def foreign_key_for(reflection, type)
27
+ if type == :belongs_to || reflection.polymorphic?
28
+ reflection.foreign_key
29
+ elsif reflection.collection?
30
+ # @see https://github.com/rails/rails/blob/92703a9ea5d8b96f30e0b706b801c9185ef14f0e/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
31
+ reflection.name.to_s.singularize << '_ids'
32
+ else
33
+ reflection.association_foreign_key
34
+ end
35
+ end
36
+
37
+ # @param reflection [ActiveRecord::Reflection]
38
+ # @return [Boolean] whether it's a through relation
39
+ def through?(reflection)
40
+ reflection.is_a? ::ActiveRecord::Reflection::ThroughReflection
41
+ end
42
+
43
+ # @param reflection [ActiveRecord::Reflection]
44
+ # @return [Boolean] whether it has scope
45
+ def scope?(reflection)
46
+ reflection.scope.present?
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ class ModelDecorator
6
+ class FieldsBuilder
7
+ # To build the metadata for polymorphic
8
+ class PolymorphicBuilder
9
+ # update the metadata
10
+ # @param metadata [Hash]
11
+ # @param reflection [ActiveRecord::Reflection]
12
+ def update(metadata, reflection)
13
+ if reflection.polymorphic?
14
+ metadata[:is_polymorphic] = true
15
+ metadata[:polymorphic_type] = reflection.foreign_type
16
+ metadata[:polymorphic_list] = polymorphic_list_for(reflection)
17
+ else
18
+ metadata[:class] = reflection.klass
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ # @param reflection [ActiveRecord::Reflection]
25
+ # @return [Array<Class>] a list of classes for this polymorphism
26
+ def polymorphic_list_for(reflection)
27
+ all_model_class.each_with_object([]) do |model_class, list|
28
+ list << model_class if polymorphic_defined? model_class, reflection.name
29
+ end
30
+ end
31
+
32
+ # @return [Array<Class>] a list of all ActiveRecord classes
33
+ def all_model_class
34
+ Map
35
+ .mode_map
36
+ .select { |_, mode| mode == ::Wallaby::ActiveRecord }.keys
37
+ end
38
+
39
+ # @param model_class [Class]
40
+ # @param polymorphic_name [String] polymorphic name
41
+ # @return [Boolean] if polymorphism defined?
42
+ def polymorphic_defined?(model_class, polymorphic_name)
43
+ polymorphic_name_sym = polymorphic_name.try(:to_sym)
44
+ model_class.reflections.any? do |_field_name, reflection|
45
+ reflection.options[:as].try(:to_sym) == polymorphic_name_sym
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ class ModelDecorator
6
+ class FieldsBuilder
7
+ # To build the metadata for sti column
8
+ class StiBuilder
9
+ # @param model_class [Class]
10
+ def initialize(model_class)
11
+ @model_class = model_class
12
+ end
13
+
14
+ # update the metadata
15
+ # @param metadata [Hash]
16
+ # @param column [ActiveRecord::ConnectionAdapters::Column]
17
+ def update(metadata, column)
18
+ return unless @model_class.inheritance_column == column.name
19
+
20
+ metadata[:type] = 'sti'
21
+ metadata[:sti_class_list] = sti_list(find_parent_of(@model_class))
22
+ end
23
+
24
+ private
25
+
26
+ # @param klass [Class]
27
+ # @return [Array<Class>] a list of STI classes for this model
28
+ def sti_list(klass)
29
+ list = klass.descendants << klass
30
+ list.sort_by(&:name)
31
+ end
32
+
33
+ # @param klass [Class]
34
+ # @return [Class] the top parent class in the STI hierarchy
35
+ def find_parent_of(klass)
36
+ parent = klass
37
+ parent = parent.superclass until top_parent?(parent.superclass)
38
+ parent
39
+ end
40
+
41
+ # @param klass [Class]
42
+ # @return [Boolean] whether the class is ActiveRecord base class
43
+ def top_parent?(klass)
44
+ klass == ModelFinder.base || ModuleUtils.try_to(klass, :abstract_class?)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ class ModelDecorator
6
+ # To search and build the metadata for fields
7
+ class FieldsBuilder
8
+ # @param model_class [Class]
9
+ def initialize(model_class)
10
+ @model_class = model_class
11
+ end
12
+
13
+ # @return [Hash<String, Hash>] a hash for general fields
14
+ def general_fields
15
+ @model_class.columns.each_with_object({}) do |column, fields|
16
+ metadata = {
17
+ type: to_type(column).freeze,
18
+ label: @model_class.human_attribute_name(column.name)
19
+ }
20
+ sti_builder.update(metadata, column)
21
+ fields[column.name] = metadata
22
+ end
23
+ end
24
+
25
+ # @return [Hash<String, Hash>] a hash for general fields
26
+ def association_fields
27
+ @model_class.reflections.each_with_object({}) do |(name, ref), fields|
28
+ metadata = {
29
+ type: ref.macro.to_s,
30
+ label: @model_class.human_attribute_name(name)
31
+ }
32
+ association_builder.update(metadata, ref)
33
+ polymorphic_builder.update(metadata, ref)
34
+ fields[name] = metadata
35
+ end
36
+ end
37
+
38
+ protected
39
+
40
+ # Detect active_storage type
41
+ # @param column [ActiveRecord::ConnectionAdapters::Column]
42
+ # @return [String] field type
43
+ def to_type(column)
44
+ return 'active_storage' if @model_class.respond_to?("with_attached_#{column.name}")
45
+
46
+ column.type.to_s
47
+ end
48
+
49
+ # @return [Wallaby::ActiveRecord::ModelDecorator::StiBuilder]
50
+ def sti_builder
51
+ @sti_builder ||= StiBuilder.new(@model_class)
52
+ end
53
+
54
+ # @return [Wallaby::ActiveRecord::ModelDecorator::AssociationBuilder]
55
+ def association_builder
56
+ @association_builder ||= AssociationBuilder.new
57
+ end
58
+
59
+ # @return [Wallaby::ActiveRecord::ModelDecorator::PolymorphicBuilder]
60
+ def polymorphic_builder
61
+ @polymorphic_builder ||= PolymorphicBuilder.new
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ class ModelDecorator
6
+ # Try to find the field that can be used as title
7
+ class TitleFieldFinder
8
+ TITLE_FIELD_TYPES = %w(string).freeze
9
+
10
+ # @param model_class [Class]
11
+ # @param fields [Hash] fields metadata
12
+ def initialize(model_class, fields)
13
+ @model_class = model_class
14
+ @fields = fields
15
+ end
16
+
17
+ # @return [String] field name that can be used as title
18
+ def find
19
+ possible_title_fields = @fields.select do |_field_name, metadata|
20
+ TITLE_FIELD_TYPES.include? metadata[:type]
21
+ end
22
+ target_field = possible_title_fields.keys.find do |field_name|
23
+ TITLE_NAMES.any? { |v| field_name.to_s.index v }
24
+ end
25
+ target_field \
26
+ || possible_title_fields.keys.first \
27
+ || @model_class.primary_key
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ # Modal decorator for ActiveRecord
6
+ class ModelDecorator < ::Wallaby::ModelDecorator
7
+ # Data types to exclude for index page
8
+ INDEX_EXCLUSIVE_DATA_TYPES =
9
+ (['', 'medium', 'long'] * 2)
10
+ .zip(%w(blob text) * 3).map(&:join)
11
+ .concat(%w(binary citext hstore json jsonb tsvector xml)).freeze
12
+
13
+ # Class to exclude for show page
14
+ SHOW_EXCLUSIVE_CLASS_NAMES = %w(ActiveStorage::Attachment ActiveStorage::Blob).freeze
15
+
16
+ # Data types to exclude for form page
17
+ FORM_EXCLUSIVE_DATA_TYPES = %w(created_at updated_at).freeze
18
+
19
+ # Origin metadata directly coming from ActiveRecord.
20
+ #
21
+ # It needs to be frozen so that we can keep the metadata integrity
22
+ # @example sample fields:
23
+ # model_decorator.fields
24
+ # # =>
25
+ # {
26
+ # # general field
27
+ # id: { name: 'id', type: 'integer', label: 'Id' },
28
+ # # association field
29
+ # category: {
30
+ # 'name' => 'category',
31
+ # 'type' => 'belongs_to',
32
+ # 'label' => 'Category',
33
+ # 'is_association' => true,
34
+ # 'is_through' => false,
35
+ # 'has_scope' => false,
36
+ # 'foreign_key' => 'category_id',
37
+ # 'class' => Category
38
+ # }
39
+ # }
40
+ # @return [ActiveSupport::HashWithIndifferentAccess] metadata
41
+ def fields
42
+ @fields ||= ::ActiveSupport::HashWithIndifferentAccess.new.tap do |hash|
43
+ # NOTE: There is a chance that people create ActiveRecord class
44
+ # before they do the migration, so initialising the fields will raise
45
+ # all kinds of error. Therefore, we need to check the table existence
46
+ if @model_class.table_exists?
47
+ hash.merge! general_fields
48
+ hash.merge! association_fields
49
+ hash.except!(*foreign_keys_from_associations)
50
+ end
51
+ end.freeze
52
+ end
53
+
54
+ # A copy of {#fields} for index page
55
+ # @return [ActiveSupport::HashWithIndifferentAccess] metadata
56
+ def index_fields
57
+ @index_fields ||= Utils.clone fields
58
+ end
59
+
60
+ # A copy of {#fields} for show page
61
+ # @return [ActiveSupport::HashWithIndifferentAccess] metadata
62
+ def show_fields
63
+ @show_fields ||= Utils.clone fields
64
+ end
65
+
66
+ # A copy of {#fields} for form (new/edit) page
67
+ # @return [ActiveSupport::HashWithIndifferentAccess] metadata
68
+ def form_fields
69
+ @form_fields ||= Utils.clone fields
70
+ end
71
+
72
+ # @return [Array<String>] a list of field names for index page (note: only primitive SQL types are included).
73
+ def index_field_names
74
+ @index_field_names ||=
75
+ index_fields.reject do |_field_name, metadata|
76
+ metadata[:is_association] \
77
+ || INDEX_EXCLUSIVE_DATA_TYPES.include?(metadata[:type])
78
+ end.keys
79
+ end
80
+
81
+ # @return [Array<String>] a list of field names for show page (note: **ActiveStorage** fields are excluded).
82
+ def show_field_names
83
+ @show_field_names ||=
84
+ show_fields.reject do |_field_name, metadata|
85
+ SHOW_EXCLUSIVE_CLASS_NAMES.include? metadata[:class].try(:name)
86
+ end.keys
87
+ end
88
+
89
+ # @return [Array<String>] a list of field names for form (new/edit) page (note: complex fields are excluded).
90
+ def form_field_names
91
+ @form_field_names ||=
92
+ form_fields.reject do |field_name, metadata|
93
+ field_name == primary_key \
94
+ || FORM_EXCLUSIVE_DATA_TYPES.include?(field_name) \
95
+ || metadata[:has_scope] || metadata[:is_through]
96
+ end.keys
97
+ end
98
+
99
+ # @return [ActiveModel::Errors] errors for resource
100
+ def form_active_errors(resource)
101
+ resource.errors
102
+ end
103
+
104
+ # @return [String] primary key for the resource
105
+ def primary_key
106
+ @primary_key ||= @model_class.primary_key
107
+ end
108
+
109
+ # To guess the title for resource.
110
+ #
111
+ # It will go through the fields and try to find out the one that looks
112
+ # like a name or text to represent this resource. Otherwise, it will fall
113
+ # back to primary key.
114
+ #
115
+ # @param resource [Object]
116
+ # @return [String] the title of given resource
117
+ def guess_title(resource)
118
+ ModuleUtils.try_to resource, title_field_finder.find
119
+ end
120
+
121
+ protected
122
+
123
+ # @return [Wallaby::ActiveRecord::ModelDecorator::FieldsBuilder]
124
+ def field_builder
125
+ @field_builder ||= FieldsBuilder.new @model_class
126
+ end
127
+
128
+ # @return [Wallaby::ActiveRecord::ModelDecorator::TitleFieldFinder]
129
+ def title_field_finder
130
+ @title_field_finder ||=
131
+ TitleFieldFinder.new @model_class, general_fields
132
+ end
133
+
134
+ # @!method general_fields
135
+ # (see Wallaby::ActiveRecord::ModelDecorator::FieldsBuilder#general_fields)
136
+ # @see Wallaby::ActiveRecord::ModelDecorator::FieldsBuilder#general_fields
137
+
138
+ # @!method association_fields
139
+ # (see Wallaby::ActiveRecord::ModelDecorator::FieldsBuilder#association_fields)
140
+ # @see Wallaby::ActiveRecord::ModelDecorator::FieldsBuilder#association_fields
141
+ delegate :general_fields, :association_fields, to: :field_builder
142
+
143
+ # Find out all the foreign keys for association fields
144
+ # @param fields [Hash] metadata of fields
145
+ # @return [Array<String>] a list of foreign keys
146
+ def foreign_keys_from_associations(fields = association_fields)
147
+ fields.each_with_object([]) do |(_field_name, metadata), keys|
148
+ keys << metadata[:foreign_key] if metadata[:foreign_key]
149
+ keys << metadata[:polymorphic_type] if metadata[:polymorphic_type]
150
+ keys
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ # Model finder
6
+ class ModelFinder < ::Wallaby::ModelFinder
7
+ # @return [Array<Class>] a list of ActiveRecord subclasses
8
+ def all
9
+ self.class.base.descendants.reject do |model_class|
10
+ abstract?(model_class) || anonymous?(model_class) || habtm?(model_class)
11
+ end.sort_by(&:to_s)
12
+ end
13
+
14
+ # This is only for ActiveRecord
15
+ # @return [ApplicationRecord, ActiveRecord::Base] base ActiveRecord class
16
+ def self.base
17
+ return ::ApplicationRecord if defined? ::ApplicationRecord
18
+
19
+ ::ActiveRecord::Base
20
+ end
21
+
22
+ private
23
+
24
+ # Is model class abstract?
25
+ # @param model_class [Class]
26
+ # @return [Boolean]
27
+ def abstract?(model_class)
28
+ model_class.abstract_class?
29
+ end
30
+
31
+ # @see Wallaby::ModuleUtils.anonymous_class?
32
+ def anonymous?(model_class)
33
+ ModuleUtils.anonymous_class? model_class
34
+ end
35
+
36
+ # Check and see if given model class is intermediate class that generated
37
+ # for has and belongs to many assocation
38
+ # @param model_class [Class]
39
+ # @return [Boolean]
40
+ def habtm?(model_class)
41
+ model_class.name.index('HABTM')
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ # Model pagination provider
6
+ class ModelPaginationProvider < ::Wallaby::ModelPaginationProvider
7
+ # Check if collection has pagination feature
8
+ # @return [Boolean]
9
+ def paginatable?
10
+ # `total_count` is a method that kaminari uses
11
+ (@collection && @collection.respond_to?(:total_count)).tap do |paginatable|
12
+ next if paginatable
13
+
14
+ Rails.logger.warn I18n.t('errors.activerecord.paginatable', collection: @collection.inspect)
15
+ end
16
+ end
17
+
18
+ # @return [Integer] total count for the query
19
+ def total
20
+ @collection.total_count
21
+ end
22
+
23
+ # @return [Integer] page size from parameters or configuration
24
+ def page_size
25
+ @params[:per].try(:to_i) || Wallaby.configuration.pagination.page_size
26
+ end
27
+
28
+ # @return [Integer] page number from parameters
29
+ def page_number
30
+ [@params[:page].to_i, 1].max
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wallaby
4
+ class ActiveRecord
5
+ class ModelServiceProvider
6
+ # Normalize the values for a model
7
+ class Normalizer
8
+ # @param model_decorator [Wallaby::ModelDecorator]
9
+ def initialize(model_decorator)
10
+ @model_decorator = model_decorator
11
+ end
12
+
13
+ # @param params [ActionController::Parameters]
14
+ def normalize(params)
15
+ params.each do |field_name, values|
16
+ metadata =
17
+ @model_decorator.metadata_of(field_name).presence || @model_decorator.form_metadata_of(field_name)
18
+ type = metadata[:type].try(:[], /range|point|binary/)
19
+ next unless type
20
+
21
+ public_send "normalize_#{type}_values", params, field_name, values
22
+ end
23
+ params
24
+ end
25
+
26
+ # Turn values into range
27
+ # @param params [ActionController::Parameters]
28
+ # @param field_name [String]
29
+ # @param values [Array]
30
+ def normalize_range_values(params, field_name, values)
31
+ normalized = Array(values).map(&:presence).compact
32
+ params[field_name] = (normalized.present? && values.length == 2) && (values.first...values.last) || nil
33
+ end
34
+
35
+ # Turn values into points
36
+ # @param params [ActionController::Parameters]
37
+ # @param field_name [String]
38
+ # @param values [Array]
39
+ def normalize_point_values(params, field_name, values)
40
+ normalized = Array(values).map(&:presence).compact
41
+ params[field_name] = normalized.present? && values.map(&:to_f) || nil
42
+ end
43
+
44
+ # Turn values into binary
45
+ # @param params [ActionController::Parameters]
46
+ # @param field_name [String]
47
+ # @param values [Object]
48
+ def normalize_binary_values(params, field_name, values)
49
+ params[field_name] = values.is_a?(::ActionDispatch::Http::UploadedFile) && values.read || nil
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end