plain_params 0.0.2 → 0.0.5

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: eb54893c12c29f4fe1decf5259e4d50502c300dcb9fc7d74a025d2c91a74dc54
4
- data.tar.gz: df9edd0784556630498b94bba2642b140507ef1deb762ebfac8f6458929a9b30
3
+ metadata.gz: 2732e1cfef1a1932dfb6b1b7b829145b3f765bfcd779600fb3ddecf3f324f867
4
+ data.tar.gz: e41d104a92928e9acfb726dc25fe57ca7cdc8d6c0341e9d90e0ef033154ba7bf
5
5
  SHA512:
6
- metadata.gz: f9d2f3ceb20c027834a3d72743a4348b8e569e99856d4c922b7dc752c5ccb8ed3b829d0fa33314bddd56382ed0fb8c9f2c2339c4dbca2161e49ae390965b2bdc
7
- data.tar.gz: 74ee1065560293d9b7bbbbcb3fbcabeee8d3761310c78259878e1f9215bc478efe8d423e3fbfd318dcc2e6bc810d14957fdd641ccb825207bb21713f26e0fb32
6
+ metadata.gz: 4b32b007ab99633269c6d13a6c865d4b01b0a5076920c5a2472893bed145786b61bb91a15d2f61925397fdf62b7c8c2c7ba418e698d30f9f0476b5324087dcd4
7
+ data.tar.gz: 01ee101b993dbf46d01bb5fc4f02992abb5f81eee616fbb46885e53a4aba13fe53a6aed6994c121eb299c81cd2797f528a5781c9a54ea2790935da654d57b7f6
data/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.0.5] - Unreleased
6
+
7
+ ### Added
8
+ - Comprehensive test suite with 100% coverage
9
+ - Example usage files demonstrating practical applications
10
+ - Rubocop configuration for code quality
11
+ - Rake tasks for common operations
12
+ - Support for Ruby 3.4+ implicit block parameter `it`
13
+
14
+ ### Changed
15
+ - Refactored main class to use modern Ruby syntax
16
+ - Improved code organization with separate methods for initialization
17
+ - Enhanced README with better examples and clearer documentation
18
+ - Simplified accessor setup logic
19
+
20
+ ### Fixed
21
+ - Virtual fields now properly support custom getter methods
22
+ - Fixed issue with virtual field initialization order
23
+
24
+ ## [0.0.4] - 2024-11-15
25
+
26
+ ### Added
27
+ - Initial release with basic functionality
28
+ - Support for real and virtual fields
29
+ - ActiveModel integration
30
+ - Basic validation support
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Gedean Dias
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1 +1,205 @@
1
- # plain_params
1
+ # PlainParams
2
+
3
+ A lightweight Ruby gem for parameter validation and organization using ActiveModel, without the overhead of ActiveRecord or complex validation libraries.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'plain_params'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ $ gem install plain_params
23
+ ```
24
+
25
+ ## Overview
26
+
27
+ PlainParams provides a simple way to handle parameter validation by dividing attributes into two categories:
28
+
29
+ - **Real fields**: Required attributes with automatic presence validation
30
+ - **Virtual fields**: Optional computed attributes that can depend on real fields
31
+
32
+ ## Basic Usage
33
+
34
+ ### Simple Example
35
+
36
+ ```ruby
37
+ class UserParams < PlainParams
38
+ @real_fields = %i[name email]
39
+ @virtual_fields = %i[display_name]
40
+
41
+ def display_name
42
+ name.upcase if name
43
+ end
44
+ end
45
+
46
+ # Create instance with parameters
47
+ params = UserParams.new(name: "john", email: "john@example.com")
48
+
49
+ # Access attributes
50
+ params.name # => "john"
51
+ params.email # => "john@example.com"
52
+ params.display_name # => "JOHN"
53
+
54
+ # Check validity
55
+ params.valid? # => true
56
+
57
+ # Get all values
58
+ params.values
59
+ # => {
60
+ # real: { name: "john", email: "john@example.com" },
61
+ # virtual: { display_name: "JOHN" }
62
+ # }
63
+ ```
64
+
65
+ ### Validation Example
66
+
67
+ ```ruby
68
+ class ProductParams < PlainParams
69
+ @real_fields = %i[name price quantity]
70
+ @virtual_fields = %i[total_value in_stock]
71
+
72
+ validates :price, numericality: { greater_than: 0 }
73
+ validates :quantity, numericality: { greater_than_or_equal_to: 0 }
74
+
75
+ def total_value
76
+ price * quantity if price && quantity
77
+ end
78
+
79
+ def in_stock
80
+ quantity && quantity > 0
81
+ end
82
+ end
83
+
84
+ # Invalid parameters
85
+ params = ProductParams.new(name: "Widget", price: -10)
86
+ params.valid? # => false
87
+ params.errors.full_messages
88
+ # => ["Price must be greater than 0", "Quantity can't be blank"]
89
+ ```
90
+
91
+ ### Advanced Example with Dependencies
92
+
93
+ ```ruby
94
+ class OrderParams < PlainParams
95
+ @real_fields = %i[customer_id items_count discount_percentage]
96
+ @virtual_fields = %i[has_discount order_size discount_multiplier]
97
+
98
+ validates :items_count, numericality: { greater_than: 0 }
99
+ validates :discount_percentage, numericality: {
100
+ greater_than_or_equal_to: 0,
101
+ less_than_or_equal_to: 100
102
+ }
103
+
104
+ def has_discount
105
+ discount_percentage && discount_percentage > 0
106
+ end
107
+
108
+ def order_size
109
+ return :small if items_count <= 5
110
+ return :medium if items_count <= 20
111
+ :large
112
+ end
113
+
114
+ def discount_multiplier
115
+ 1 - (discount_percentage.to_f / 100)
116
+ end
117
+ end
118
+
119
+ params = OrderParams.new(
120
+ customer_id: 123,
121
+ items_count: 10,
122
+ discount_percentage: 15
123
+ )
124
+
125
+ params.order_size # => :medium
126
+ params.has_discount # => true
127
+ params.discount_multiplier # => 0.85
128
+ ```
129
+
130
+ ## Features
131
+
132
+ ### ActiveModel Integration
133
+
134
+ PlainParams integrates seamlessly with ActiveModel, providing:
135
+
136
+ - Full validation support
137
+ - Attribute naming conventions
138
+ - Error handling
139
+ - Form helper compatibility
140
+
141
+ ### String Key Support
142
+
143
+ Parameters can be passed with either symbol or string keys:
144
+
145
+ ```ruby
146
+ # Both work identically
147
+ UserParams.new(name: "john", age: 30)
148
+ UserParams.new("name" => "john", "age" => 30)
149
+ ```
150
+
151
+ ### Error Handling
152
+
153
+ PlainParams provides clear error messages for common issues:
154
+
155
+ ```ruby
156
+ # Missing field definitions
157
+ class EmptyParams < PlainParams
158
+ end
159
+ EmptyParams.new # => RuntimeError: No @real_fields or @virtual_fields provided
160
+
161
+ # Duplicate fields
162
+ class DuplicateParams < PlainParams
163
+ @real_fields = %i[name]
164
+ @virtual_fields = %i[name]
165
+ end
166
+ DuplicateParams.new # => RuntimeError: Duplicated field(s) 'name'
167
+
168
+ # Invalid fields
169
+ UserParams.new(invalid: "value") # => RuntimeError: field 'invalid' is not in @real_fields or @virtual_fields
170
+ ```
171
+
172
+ ## API Reference
173
+
174
+ ### Class Methods
175
+
176
+ - `real_fields` - Returns array of real field names
177
+ - `virtual_fields` - Returns array of virtual field names
178
+
179
+ ### Instance Methods
180
+
181
+ - `values` - Returns hash with all real and virtual values
182
+ - `real_values` - Returns hash with only real field values
183
+ - `virtual_values` - Returns hash with only virtual field values
184
+ - `valid?` - Validates the instance according to defined validations
185
+ - `persisted?` - Always returns `false` (ActiveModel compatibility)
186
+
187
+ ## Best Practices
188
+
189
+ 1. **Keep virtual fields simple** - They should contain display logic or simple calculations
190
+ 2. **Use real fields for validation** - Put your validation rules on real fields
191
+ 3. **Leverage ActiveModel validations** - Use the full power of ActiveModel validations
192
+ 4. **Virtual fields for computed values** - Use virtual fields for values derived from real fields
193
+
194
+ ## Requirements
195
+
196
+ - Ruby >= 3.0
197
+ - ActiveModel >= 7.0, < 9.0
198
+
199
+ ## Contributing
200
+
201
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gedean/plain_params.
202
+
203
+ ## License
204
+
205
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/examples/usage.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'uri'
2
+ require_relative '../lib/plain_params'
3
+
4
+ class ContactParams < PlainParams
5
+ @real_fields = %i[name email phone]
6
+ @virtual_fields = %i[formatted_phone email_domain]
7
+
8
+ validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
9
+ validates :phone, format: { with: /\A\d{10,11}\z/ }
10
+
11
+ def formatted_phone
12
+ return unless phone
13
+ if phone.length == 11
14
+ "(#{phone[0..1]}) #{phone[2..6]}-#{phone[7..10]}"
15
+ else
16
+ "(#{phone[0..1]}) #{phone[2..5]}-#{phone[6..9]}"
17
+ end
18
+ end
19
+
20
+ def email_domain
21
+ email.split('@').last if email
22
+ end
23
+ end
24
+
25
+ puts "=== Valid Contact ==="
26
+ contact = ContactParams.new(
27
+ name: "Alice Smith",
28
+ email: "alice@example.com",
29
+ phone: "11987654321"
30
+ )
31
+
32
+ if contact.valid?
33
+ puts "Name: #{contact.name}"
34
+ puts "Email: #{contact.email}"
35
+ puts "Phone: #{contact.phone}"
36
+ puts "Formatted Phone: #{contact.formatted_phone}"
37
+ puts "Email Domain: #{contact.email_domain}"
38
+ puts "\nAll values:"
39
+ p contact.values
40
+ else
41
+ puts "Errors: #{contact.errors.full_messages.join(', ')}"
42
+ end
43
+
44
+ puts "\n=== Invalid Contact ==="
45
+ invalid_contact = ContactParams.new(
46
+ name: "Bob",
47
+ email: "invalid-email",
48
+ phone: "123"
49
+ )
50
+
51
+ if invalid_contact.valid?
52
+ puts "Valid!"
53
+ else
54
+ puts "Errors: #{invalid_contact.errors.full_messages.join(', ')}"
55
+ end
data/lib/plain_params.rb CHANGED
@@ -6,63 +6,68 @@ class PlainParams
6
6
  include ActiveModel::Validations
7
7
 
8
8
  def initialize(params_attributes = {})
9
- raise 'No @real_fields or @virtual_fields provided in the class definition' if (self.class.real_fields.nil? and self.class.virtual_fields.nil?)
9
+ raise 'No @real_fields or @virtual_fields provided in the class definition' if self.class.real_fields.nil? && self.class.virtual_fields.nil?
10
10
 
11
11
  self.class.real_fields ||= []
12
12
  self.class.virtual_fields ||= []
13
13
 
14
+ setup_accessors
14
15
  self.attributes = params_attributes
15
16
  end
16
17
 
17
- def self.real_fields = @real_fields
18
- def self.virtual_fields = @virtual_fields
18
+ class << self
19
+ attr_accessor :real_fields, :virtual_fields
20
+ end
19
21
 
20
22
  def values
21
- fields_values = {}
22
-
23
- fields_values[:real] = self.class.real_fields.map { |afield| [afield, send(afield)] }.to_h
24
- fields_values[:virtual] = self.class.virtual_fields.map { |afield| [afield, send(afield)] }.to_h
25
-
26
- fields_values
23
+ {
24
+ real: self.class.real_fields.to_h { [it, send(it)] },
25
+ virtual: self.class.virtual_fields.to_h { [it, send(it)] }
26
+ }
27
27
  end
28
28
 
29
29
  def real_values = values[:real]
30
30
  def virtual_values = values[:virtual]
31
-
32
31
  def persisted? = false
33
32
 
34
33
  private
34
+
35
+ def setup_accessors
36
+ self.class.real_fields.each do |field|
37
+ self.class.send(:attr_accessor, field)
38
+ self.class.send(:validates_presence_of, field)
39
+ end
40
+
41
+ self.class.virtual_fields.each do |field|
42
+ self.class.send(:attr_writer, field) unless self.class.method_defined?("#{field}=")
43
+ self.class.send(:attr_reader, field) unless self.class.method_defined?(field)
44
+ end
45
+ end
35
46
 
36
47
  def attributes=(attributes)
37
- duplicated_fields = self.class.real_fields.intersection(self.class.virtual_fields)
48
+ duplicated_fields = self.class.real_fields & self.class.virtual_fields
38
49
  raise "Duplicated field(s) '#{duplicated_fields.join(', ')}' in @real_fields and @virtual_fields" if duplicated_fields.any?
39
50
 
40
51
  symbolized_attributes = attributes.symbolize_keys
41
-
42
- (symbolized_attributes.keys - (self.class.real_fields + self.class.virtual_fields)).each do |invalid_field|
43
- raise "field '#{invalid_field.to_s}' is not in @real_fields or @virtual_fields"
44
- end
45
-
46
- # initialized @real_fields first
47
- self.class.real_fields.each do |available_field|
48
- (instance_variable_set"@#{available_field.to_s}", symbolized_attributes[available_field])
49
- self.class.send(:attr_accessor, available_field)
50
- self.class.send(:validates_presence_of, available_field)
52
+ invalid_fields = symbolized_attributes.keys - (self.class.real_fields + self.class.virtual_fields)
53
+
54
+ invalid_fields.each do |invalid_field|
55
+ raise "field '#{invalid_field}' is not in @real_fields or @virtual_fields"
51
56
  end
52
57
 
53
- # @virtual_fields comes sencondly for use already initialized @available fields
54
- self.class.virtual_fields.each do |virtual_field|
55
- unless symbolized_attributes[virtual_field].nil?
56
- self.send("#{virtual_field.to_s}=", symbolized_attributes[virtual_field])
57
- end
58
- end
58
+ initialize_real_fields(symbolized_attributes)
59
+ initialize_virtual_fields(symbolized_attributes)
59
60
  end
60
61
 
61
- def self.real_fields=(value)
62
- @real_fields = value
62
+ def initialize_real_fields(attributes)
63
+ self.class.real_fields.each do |field|
64
+ instance_variable_set("@#{field}", attributes[field])
65
+ end
63
66
  end
64
67
 
65
- def self.virtual_fields=(value)
66
- @virtual_fields = value
68
+ def initialize_virtual_fields(attributes)
69
+ self.class.virtual_fields.each do |field|
70
+ send("#{field}=", attributes[field]) if attributes.key?(field)
71
+ end
67
72
  end
68
73
  end
metadata CHANGED
@@ -1,42 +1,81 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plain_params
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gedean Dias
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-04-25 00:00:00.000000000 Z
10
+ date: 2025-06-28 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activemodel
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - "~>"
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ - - "<"
18
20
  - !ruby/object:Gem::Version
19
- version: '7.1'
21
+ version: '9.0'
20
22
  type: :runtime
21
23
  prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '7.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: rspec
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '3.13'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '3.13'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '13.0'
53
+ type: :development
54
+ prerelease: false
22
55
  version_requirements: !ruby/object:Gem::Requirement
23
56
  requirements:
24
57
  - - "~>"
25
58
  - !ruby/object:Gem::Version
26
- version: '7.1'
27
- description: No ActiveRecord or Dry-Validation, just plain parameters.
59
+ version: '13.0'
60
+ description: A simple and lightweight parameter validation library that leverages
61
+ ActiveModel to provide a clean interface for handling parameters with real and virtual
62
+ fields.
28
63
  email: gedean.dias@gmail.com
29
64
  executables: []
30
65
  extensions: []
31
66
  extra_rdoc_files: []
32
67
  files:
68
+ - CHANGELOG.md
69
+ - LICENSE
33
70
  - README.md
71
+ - examples/usage.rb
34
72
  - lib/plain_params.rb
35
73
  homepage: https://github.com/gedean/plain_params
36
74
  licenses:
37
75
  - MIT
38
76
  metadata: {}
39
- post_install_message: Please check readme file for use instructions.
77
+ post_install_message: Thank you for installing PlainParams! Check out the README for
78
+ usage examples.
40
79
  rdoc_options: []
41
80
  require_paths:
42
81
  - lib
@@ -44,15 +83,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
44
83
  requirements:
45
84
  - - ">="
46
85
  - !ruby/object:Gem::Version
47
- version: '3'
86
+ version: '3.0'
48
87
  required_rubygems_version: !ruby/object:Gem::Requirement
49
88
  requirements:
50
89
  - - ">="
51
90
  - !ruby/object:Gem::Version
52
91
  version: '0'
53
92
  requirements: []
54
- rubygems_version: 3.5.9
55
- signing_key:
93
+ rubygems_version: 3.6.9
56
94
  specification_version: 4
57
- summary: No ActiveRecord or Dry-Validation, just plain parameters.
95
+ summary: Lightweight parameter validation using ActiveModel
58
96
  test_files: []