wallaby-active_record 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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