easy_params 0.7.0 → 0.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f63905ee9e223d63e2f3855f7e67ace0f3d2abd14ed97fb1f706f73082f73c90
4
- data.tar.gz: 1ce09ad5d8a018e97c9c46eb21b32f5dd537d448830578c8b760ee2c99d903ca
3
+ metadata.gz: 520468dff4cbd537cef3eec5d4d0c15607e0616143812746c559a7697294bc1c
4
+ data.tar.gz: 338dc40010d0a02ab8819289ad64e9355fa2ee302fbb93ae82723b15a9b8d94c
5
5
  SHA512:
6
- metadata.gz: 935a110dbedbace4f05a9df987aec2932fec9ccec5cbc9dbb3b4a3460b42971cd7cdafa60c391b24ad334f74e39086c7d81df191e9622844ebda0f4ed9ca952f
7
- data.tar.gz: 9ebe7ebeae1961a820e396361e020b5b8cfe712ad81480dcf14b64784d7de6cf16e66e8c8b9eaf44272c0aaed20805ab255bfe1effc073c4f2aebb355f21339a
6
+ metadata.gz: b0f00c2a25862648b027d96b0491ddfdc6f838cf5fef010c8fa263436afb2ea463f85a3ea8b46ca3839e4036d8a8a73fd98a577fb78f2f8a204a1515b130022f
7
+ data.tar.gz: 86a0f9d29e30d44d40d3af84604d458b8b8cffc190d9e960de3a3858d550161b33970520ea3d3e03d3ad3e221532f0fc28a48abd3882eb0c760a186465328d21
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- easy_params (0.7.0)
4
+ easy_params (0.8.0)
5
5
  activemodel (>= 3.2)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -10,6 +10,40 @@ Provides an easy way to define structure, validation rules, type coercion, and d
10
10
 
11
11
  Available types: `integer`, `decimal`, `float`, `bool`, `string`, `array`, `date`, `datetime`, `time`
12
12
 
13
+ ### Registering Custom Types
14
+
15
+ You can register custom types using `EasyParams.register_type`:
16
+
17
+ ```ruby
18
+ # Register a weight type that converts between units
19
+ EasyParams.register_type :weight do |value|
20
+ case value.to_s.downcase
21
+ when /^(\d+(?:\.\d+)?)\s*kg$/i
22
+ $1.to_f
23
+ when /^(\d+(?:\.\d+)?)\s*lbs?$/i
24
+ $1.to_f * 0.453592 # Convert pounds to kg
25
+ when /^(\d+(?:\.\d+)?)\s*g$/i
26
+ $1.to_f / 1000.0 # Convert grams to kg
27
+ else
28
+ value.to_f
29
+ end
30
+ end
31
+
32
+ # Now you can use the weight type in your params classes
33
+ class PersonParams < EasyParams::Base
34
+ weight :mass, presence: true
35
+ weight :target_weight, default: 70.0
36
+ array :weights, of: :weight, default: [65.0, 70.0]
37
+ end
38
+
39
+ # Usage
40
+ person = PersonParams.new(mass: '75.5 kg', target_weight: '165 lbs')
41
+ # person.mass = 75.5
42
+ # person.target_weight ≈ 74.84 (converted from lbs to kg)
43
+ ```
44
+
45
+ Custom types work with all EasyParams features including validation, arrays, nested structures, and inheritance.
46
+
13
47
  ## Installation
14
48
 
15
49
  Add this line to your application's Gemfile:
@@ -80,6 +114,67 @@ There are two special types:
80
114
  # input: nil => items.map(&:qty) == [2, 1]
81
115
  ```
82
116
 
117
+ ### Schema Extension
118
+
119
+ You can dynamically extend nested schema definitions using the `#{param_name}_schema` method. This creates a subclass of the original schema that replaces the parent class in the schema, allowing you to add validations, methods, or modify attributes at runtime.
120
+
121
+ ```ruby
122
+ class PostParams < EasyParams::Base
123
+ has :post, default: {} do
124
+ integer :id
125
+ string :title
126
+ string :content
127
+ date :published_at, default: Date.today
128
+ end
129
+ end
130
+
131
+ # Extend with additional validations and methods
132
+ PostParams.post_schema do
133
+ validates :title, :content, presence: true, if: :published?
134
+
135
+ def published?
136
+ published_at.present?
137
+ end
138
+ end
139
+
140
+ # Now the validation will run conditionally
141
+ params = PostParams.new(id: 1)
142
+ params.valid? # => false (because published_at has default value, so published? returns true)
143
+ ```
144
+
145
+ You can also extend collection schemas:
146
+
147
+ ```ruby
148
+ class CommentParams < EasyParams::Base
149
+ each :comments, default: [{}, {}] do
150
+ integer :post_id
151
+ string :author
152
+ string :text
153
+ end
154
+ end
155
+
156
+ # Extend with additional attributes and validations
157
+ CommentParams.comments_schema do
158
+ string :author, default: 'Anonymous'
159
+ date :created_at, default: Date.today
160
+ validates :post_id, presence: true
161
+ end
162
+
163
+ # Default values are preserved and merged with input
164
+ params = CommentParams.new({})
165
+ params.comments.size # => 2
166
+ params.comments.first.author # => 'Anonymous'
167
+ params.comments.first.created_at # => Date.today
168
+ ```
169
+
170
+ **Key Features:**
171
+ - **Preserves defaults**: Original default values are maintained when extending schemas
172
+ - **Runtime flexibility**: Add validations and methods by creating subclasses dynamically
173
+ - **Replaces parent**: The new subclass completely replaces the original schema class
174
+ - **Collection support**: Works with both `has` (struct) and `each` (collection) parameters
175
+
176
+ ### Validation errors
177
+
83
178
  ```ruby
84
179
  # app/params/api/v2/icqa/move_params.rb
85
180
  class Api::V1::Carts::MoveParams < EasyParams::Base
@@ -97,7 +192,9 @@ class Api::V1::Carts::MoveParams < EasyParams::Base
97
192
  end
98
193
  end
99
194
  ```
100
- Validation messages for nested attributes will look like this.
195
+
196
+ Validation messages for nested attributes a set on top level and each nested object has `errors` set.
197
+ Errors will look like this.
101
198
  ```ruby
102
199
  {
103
200
  :"sections[0].id"=>an_instance_of(Array),
@@ -2,7 +2,7 @@
2
2
 
3
3
  module EasyParams
4
4
  # Implements validations logic and nesting structures
5
- class Base
5
+ class Base # rubocop:disable Metrics/ClassLength
6
6
  include ActiveModel::Model
7
7
  include EasyParams::Types::Struct
8
8
  include EasyParams::Validation
@@ -19,6 +19,11 @@ module EasyParams
19
19
  def inherited(subclass)
20
20
  super
21
21
  subclass.clone_schema(self)
22
+ subclass.clone_schemas(self)
23
+ end
24
+
25
+ def schemas
26
+ @schemas ||= {}
22
27
  end
23
28
 
24
29
  def name
@@ -41,7 +46,8 @@ module EasyParams
41
46
  raise ArgumentError, "definition for attribute #{param_name.inspect} must be a subclass of EasyParams::Base"
42
47
  end
43
48
 
44
- type = EasyParams::Types::Each.with_type(definition, &block)
49
+ handle_schema_definition(param_name, definition, collection: true, &block)
50
+ type = EasyParams::Types::Each.of(schemas[param_name].new)
45
51
  type = customize_type(type, default, &normalize)
46
52
  attribute(param_name, type)
47
53
  end
@@ -52,7 +58,8 @@ module EasyParams
52
58
  raise ArgumentError, "definition for attribute #{param_name.inspect} must be a subclass of EasyParams::Base"
53
59
  end
54
60
 
55
- type = (definition || Class.new(EasyParams::Base).tap { |c| c.class_eval(&block) }).new
61
+ handle_schema_definition(param_name, definition, &block)
62
+ type = schemas[param_name].new
56
63
  type = customize_type(type, default, &normalize)
57
64
  attribute(param_name, type)
58
65
  end
@@ -68,6 +75,10 @@ module EasyParams
68
75
  @schema = parent.schema.dup
69
76
  end
70
77
 
78
+ def clone_schemas(parent)
79
+ @schemas = parent.schemas.dup
80
+ end
81
+
71
82
  def define_type_method(type_name)
72
83
  define_singleton_method(type_name) do |param_name, default: nil, normalize: nil, **validations|
73
84
  validates param_name, **validations if validations.any?
@@ -83,6 +94,26 @@ module EasyParams
83
94
  type = type.normalize(&normalize) if normalize
84
95
  type
85
96
  end
97
+
98
+ def handle_schema_definition(param_name, definition = nil, collection: false, &block)
99
+ schemas[param_name] = definition || Class.new(EasyParams::Base).tap { |c| c.class_eval(&block) }
100
+ define_schema_method(param_name, collection: collection)
101
+ end
102
+
103
+ def define_schema_method(param_name, collection: false)
104
+ define_singleton_method("#{param_name}_schema") do |&block|
105
+ default = schema[param_name].read_default
106
+ schemas[param_name] = Class.new(schemas[param_name]).tap { |c| c.class_eval(&block) }
107
+ type = create_schema_type(param_name, collection, default)
108
+ attribute(param_name, type)
109
+ end
110
+ end
111
+
112
+ def create_schema_type(param_name, collection, default)
113
+ type = schemas[param_name].new
114
+ type = EasyParams::Types::Each.of(type) if collection
115
+ customize_type(type, default)
116
+ end
86
117
  end
87
118
 
88
119
  def attributes
@@ -41,9 +41,8 @@ module EasyParams
41
41
 
42
42
  # base interface for array of structs type
43
43
  class StructsCollection < Collection
44
- def with_type(definition = nil, &block)
45
- of_type = (definition || Class.new(EasyParams::Base).tap { |c| c.class_eval(&block) }).new
46
- self.class.new(@title, @default, @normalize_proc, of: of_type)
44
+ def read_default
45
+ @default
47
46
  end
48
47
  end
49
48
  end
@@ -8,6 +8,10 @@ module EasyParams
8
8
  false
9
9
  end
10
10
 
11
+ def read_default
12
+ @default
13
+ end
14
+
11
15
  def default(value)
12
16
  self.default = value
13
17
  self
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EasyParams
4
- VERSION = '0.7.0'
4
+ VERSION = '0.8.0'
5
5
  end
data/lib/easy_params.rb CHANGED
@@ -39,20 +39,20 @@ module EasyParams
39
39
  register_type :string, &:to_s
40
40
  register_type(:decimal) { |v| v.to_f.to_d }
41
41
  register_type(:bool) do |v|
42
- BOOLEAN_MAP.fetch(v.to_s) { raise CoercionError }
42
+ v.is_a?(TrueClass) || v.is_a?(FalseClass) ? v : BOOLEAN_MAP.fetch(v.to_s) { raise CoercionError }
43
43
  end
44
44
  register_type(:date) do |v|
45
- ::Date.parse(v)
45
+ v.is_a?(Date) ? v : Date.parse(v)
46
46
  rescue ArgumentError, RangeError
47
47
  raise CoercionError, 'cannot be coerced'
48
48
  end
49
49
  register_type(:datetime) do |v|
50
- ::DateTime.parse(v)
50
+ v.is_a?(DateTime) ? v : DateTime.parse(v)
51
51
  rescue ArgumentError
52
52
  raise CoercionError, 'cannot be coerced'
53
53
  end
54
54
  register_type(:time) do |v|
55
- ::Time.parse(v)
55
+ v.is_a?(Time) ? v : Time.parse(v)
56
56
  rescue ArgumentError
57
57
  raise CoercionError, 'cannot be coerced'
58
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy_params
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrii Baran