attribeauty 0.4.8 → 0.4.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53aa2dd41b3c190851af0cac11f16ae9ccbebe82d8f83b45f6544fd438a2c612
4
- data.tar.gz: fa8f0e2d4a8c7b642255938eb828efd98a264c199c3e63c9d9315c8a620352ef
3
+ metadata.gz: 2b6b12bbaf3cc5bbef6bd67f9d8c69ad35ba2608bd3c90798ef6510884940cb7
4
+ data.tar.gz: 2f86805ee0ca65397f78eab60e83cd2740e4eef03879f130679d062d996f0467
5
5
  SHA512:
6
- metadata.gz: 7647ebd7f97f78f9d160436a5a2df37ece3dc5c52f9281f31dc8ec0c9d769ec470d78a2f04214723c2785ca38397110023210d01f9d519c5d73262f4b4eefcef
7
- data.tar.gz: 877730b270379af03c9480f24db221790676f8da285156f718147be21295154a79a58f616e67ef0b337eac422749ec9ba224e3d4bd634e4b826fa4ee31f40b0b
6
+ metadata.gz: 68bc689b886c754315b5b41203e67fb4ed9e46818eebc8b7e4cf336c4cae0fba92b56c57ff78a2611814db61c170980ba025bcf8ff5d9b65cb22b75a59389218
7
+ data.tar.gz: 8b023ec357bd89dabfcb17ecd317bd70e322ae822a10dc17ac0aacee49465ea33b47b2efee5cf44575fc78b87fb2444cece241f49e6a320e84feb363eac33f89
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.9] - 2024-07-02
4
+
5
+ - Updated README
6
+
3
7
  ## [0.4.8] - 2024-07-01
4
8
 
5
9
  - allow args with base, so `required` and `default` can be used
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- attribeauty (0.4.8)
4
+ attribeauty (0.4.11)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,152 +1,170 @@
1
1
  # Attribeauty
2
2
 
3
- I just wanted a quick, simple way to initialize mutable objects. This is it.
4
- There are so many of these (notably rails Attributes api), but none were what I wanted.
3
+ ## Params
5
4
 
6
- ## Installation
5
+ `Attribeauty::Params` casts your params and removes elements you want to exclude if they are nil or empty.
7
6
 
8
- Install the gem and add to the application's Gemfile by executing:
7
+ #### Why is this needed?
9
8
 
10
- $ bundle add attribeauty
9
+ `params` arrive in your controllers as strings—whether they represent integers, nil, dates, or anything else. Rails handles coercion at the Model level when these params are assigned as attributes. However, there are often many steps before your params are assigned. `Attribeauty::Params` elegantly ensures that your attributes start in their expected state before continuing their journey to their final destination.
11
10
 
12
- If bundler is not being used to manage dependencies, install the gem by executing:
11
+ #### Directions
13
12
 
14
- $ gem install attribeauty
15
-
16
- ## Base
17
-
18
- Inherit from `Attribeauty::Base` and then add attribute with the type you want.
19
- Initialize with these and they will be cast to that attribute.
20
- Use `assign_attributes` to update the object.
13
+ First, let's set a `params_filter` object to accept rails `params.to_unsafe_h` in the `ApplicationController`
21
14
 
15
+ ```ruby
16
+ # app/controllers/application_controller.rb
17
+ class ApplicationController
18
+ private
22
19
 
23
- ```
24
- class MyClass < Attribeauty::Base
25
- attribute :first, :string
26
- attribute :second, :integer
27
- attribute :third, :float
28
- attribute :forth, :boolean
29
- attribute :fifth, :time
30
- attribute :sixth, :koala
31
- attribute :seventh, :string, default: "Kangaroo"
20
+ def params_filter
21
+ Attribeauty::Params.with(params.to_unsafe_h)
22
+ end
32
23
  end
33
-
34
- instance = MyClass.new(first: 456)
35
- instance.first # => "456"
36
- instance.assign_attributes(second: "456")
37
- instance.second # => 456
38
- instance.first = 9000
39
- instance.first # => "9000"
40
- instance.seventh # => "Kangaroo"
41
24
  ```
42
25
 
43
- To add your own types, simply have a class that handles `MyClassName.new.cast(value)`:
44
-
26
+ The `params_filter` object here will take any ruby hash, `symbolize` the keys, and is now ready for the structure you want to provide.
27
+
28
+ If a users controller receives the following params:
29
+
30
+ ```ruby
31
+ {
32
+ 'user' => {
33
+ 'username' => 'js_bach',
34
+ 'full_name' => 'Johann Sebastian Bach',
35
+ 'job_title' => 'Composer',
36
+ 'age' => '43',
37
+ 'salary' => nil,
38
+ 'email' => {
39
+ 'address' => 'js@bach.music'
40
+ }
41
+ }
42
+ }
45
43
  ```
46
- Attribeauty.configure do |config|
47
- config.types[:koala] = MyTypes::Koala
48
- end
49
44
 
50
- module MyTypes
51
- class Koala
52
- def cast(value)
53
- value.inspect.to_s << "_koalas"
54
- end
55
- end
56
- end
45
+ We can coerce them with into `create_params` with the following:
57
46
 
58
- class MyClass < Attribeauty::Base
59
- attribute :wild_animal, :koala
60
- end
61
47
 
62
- instance = MyClass.new(wild_animal: "the_wildest_animals_are")
63
- instance.wild_animal # => "the_wildest_animals_are_koalas"
48
+ ```ruby
49
+ # app/controllers/my_controller.rb
50
+ class UsersController < ApplicationController
51
+ def edit; end
64
52
 
65
- ```
53
+ def update
54
+ @user = Users::Creator.call(create_params)
66
55
 
67
- To use rails types add to your config:
68
- ```
69
- # config/initializers/attribeauty.rb
56
+ if @user.valid?
57
+ redirect_to index_path, notice: 'Welcome to the app'
58
+ else
59
+ flash[:alert] = @user.errors.full_messages
60
+ render :edit
61
+ end
62
+ end
70
63
 
71
- Rails.application.reloader.to_prepare do
72
- Attribeauty.configure do |config|
73
- config.types[:string] = ActiveModel::Type::String
74
- config.types[:boolean] = ActiveModel::Type::Boolean
75
- config.types[:date] = ActiveModel::Type::Date
76
- config.types[:time] = ActiveModel::Type::Time
77
- config.types[:datetime] = ActiveModel::Type::DateTime
78
- config.types[:float] = ActiveModel::Type::Float
79
- config.types[:integer] = ActiveModel::Type::Integer
64
+ private
65
+
66
+ def create_params
67
+ params_filter.accept do
68
+ root :user do
69
+ attribute :username, :string, required: true
70
+ attribute :full_name, :string
71
+ attribute :job_title, :string, exclude_if: [:nil?, :empty?]
72
+ attribute :age, :integer
73
+ attribute :salary, :integer, exclude_if: :nil?
74
+ attribute :email do
75
+ attribute :address, :string, required: true
76
+ attribute :receive_updates, :boolean, default: false
77
+ end
78
+ end
79
+ end
80
80
  end
81
81
  end
82
-
83
82
  ```
84
83
 
85
- ## Params
84
+ The above will return a hash with the `age` integer cast to integer, the `salary` removed, and a `receive_updates` defaulted to `false`. The `root` `user` node will be removed too. If you wish to keep the root node, simply using `attribute` with a `block` will suffice. Below is the output from this:
86
85
 
87
- Experimental params sanitizer is now available. This will cast your params, and remove elements you want to exclude if `nil` or `empty`
88
86
 
89
- Why is this needed? Params arrive into the controller in a messy state. Booleans are not ready for caparison, integers are often strings, empty strings, and nils abound. Rails does the casting of params at the model, which is simple and elegant, but in many cases, these params are used for a multitude of things before hitting the database. I truly believe we need to cast them before they do anything.
87
+ ```ruby
88
+ {
89
+ 'username' => 'js_bach',
90
+ 'full_name' => 'Johann Sebastian Bach',
91
+ 'job_title' => 'Composer',
92
+ 'age' => 43,
93
+ 'email' => {
94
+ 'address' => 'js@bach.music',
95
+ 'receive_updates' => false
96
+ }
97
+ }
90
98
 
91
99
  ```
92
- # app/controllers/my_controller.rb
93
- class MyController
94
- def update
95
- MyRecord.update(update_params)
96
-
97
- redirect_to index_path
98
- end
99
100
 
100
- private
101
-
102
- def params_filter
103
- Attribeauty::Params.with(request.params)
104
- end
105
-
106
- def update_params
101
+ `Attribeauty::Params` can handle nested arrays and nested hashes with the same `accept`:
102
+
103
+ ```ruby
104
+ # {
105
+ # "username" => "js_bach",
106
+ # "full_name" => "Johann Sebastian Bach",
107
+ # "job_title" => "Composer",
108
+ # "age" => 43,
109
+ # "email" => [
110
+ # { "address" => "js@bach.music", "secondary" => false },
111
+ # { "address" => "papa@bach.music", "secondary" => true }
112
+ # ]
113
+ # }
114
+ #
115
+ # or
116
+ #
117
+ # {
118
+ # "username" => "js_bach",
119
+ # "full_name" => "Johann Sebastian Bach",
120
+ # "job_title" => "Composer",
121
+ # "age" => 43,
122
+ # "email" => { "address" => "js@bach.music", "secondary" => false }
123
+ # }
124
+ def create_params
107
125
  params_filter.accept do
108
- attribute :title, :string, required: true
126
+ attribute :username, :string, required: true
127
+ attribute :full_name, :string
128
+ attribute :job_title, :string, exclude_if: [:nil?, :empty?]
129
+ attribute :age, :integer
130
+ attribute :salary, :integer, exclude_if: :nil?
109
131
  attribute :email do
110
- attribute :address, :string
111
- attribute :valid, :boolean
112
- attribute :ip_address, :string, exclude_if: :empty?
132
+ attribute :address, :string, required: true
133
+ attribute :secondary, :boolean, default: false
113
134
  end
114
135
  end
115
136
  end
116
137
  ```
117
138
 
118
- If you have a "head" param, like in rails, you can exclude it, also note the `exclude_if` option, this will exclude the value completely, if it evaluates to true.
119
- `exclude_if` will accept a single method call (`:nil?`) or an array (`[:nil?, :empty?]`)
139
+ #### Error handling
120
140
 
121
- ```
141
+ `Attribeauty::Params` has rudimentary error handling, and will return an errors array when `required: true` values are missing:
142
+
143
+ ```ruby
122
144
  class MyController
145
+ def edit; end
146
+
123
147
  def update
124
- MyRecord.update(update_params)
125
-
126
- redirect_to index_path
148
+ if params_filter.errors.any?
149
+ flash[:alert] = params.errors.join(', ')
150
+ render :edit
151
+ else
152
+ MyRecord::Updater.call(update_params)
153
+ redirect_to index_path
154
+ end
127
155
  end
128
156
 
129
157
  private
130
158
 
131
- # your params look like this:
132
- # { user: { title: "woo", email: { address: "hmm@yep.com", ip_address: "" } } }
133
- #
134
- def params_filter
135
- Attribeauty::Params.with(request.params)
136
- end
159
+ # with the following params:
160
+ # { user: { username: nil } }
161
+
162
+ # update_params.errors => ["username required"]
137
163
 
138
- # using root will exclude the value and yield:
139
- # { title: "woo", email: { address: "hmm@yep.com" } }
140
- #
141
164
  def update_params
142
165
  params_filter.accept do
143
166
  root :user do
144
- attribute :title, :string, required: true
145
- attribute :email do
146
- attribute :address, :string, exclude_if: [:empty?, :nil?]
147
- attribute :valid, :boolean
148
- attribute :ip_address, :string, exclude_if: :empty?
149
- end
167
+ attribute :username, :string, required: true
150
168
  end
151
169
  end
152
170
  end
@@ -154,38 +172,30 @@ end
154
172
 
155
173
  ```
156
174
 
175
+ #### Raising Errors
176
+
157
177
  If you want to raise an error, rather than just return the errors in an array, use the `accept!` method. Will raise `Attribeauty::MissingAttributeError` with the required elements:
158
178
 
159
179
 
160
- ```
180
+ ```ruby
161
181
  class MyController
162
182
  def update
163
- MyRecord.update(update_params)
164
-
183
+ MyRecord::Updater.call(update_params)
184
+ # calling update_params
185
+ # will raise: Attribeauty::MissingAttributeError, "username required"
186
+
165
187
  redirect_to index_path
166
188
  end
167
189
 
168
190
  private
169
191
 
170
- # your params look like this:
171
- # { user: { profile: [{ address: { street_name: "Main St" } }] } }
172
- #
173
- def params_filter
174
- Attribeauty::Params.with(request.params)
175
- end
192
+ # with the following params:
193
+ # { user: { username: nil } }
176
194
 
177
- # This following with the accept! method
178
- # will raise: Attribeauty::MissingAttributeError, "title required, email required"
179
- #
180
195
  def update_params
181
196
  params_filter.accept! do
182
197
  root :user do
183
- attribute :title, :string, exclude_if: :nil?, required: true
184
- attribute :email do
185
- attribute :address, :string
186
- attribute :valid, :boolean
187
- attribute :ip_address, :string
188
- end
198
+ attribute :username, :string, required: true
189
199
  end
190
200
  end
191
201
  end
@@ -193,10 +203,12 @@ end
193
203
 
194
204
  ```
195
205
 
196
- what if you want to require all attributes? If you pass the `required: true` or `exclude_if: :nil?` with the `accept`, it will be applied to all attributes.
197
- You can also exclude a value from this by useing the `allows` option.
206
+ #### Require all
198
207
 
199
- ```
208
+ What if you want to require all attributes? If you pass the `required: true` or `exclude_if: :nil?` with the `accept`, it will be applied to all attributes.
209
+ You can also exclude a value from this by using the `allows` option.
210
+
211
+ ```ruby
200
212
  class MyController
201
213
  def update
202
214
  MyRecord.update(update_params)
@@ -206,17 +218,13 @@ class MyController
206
218
 
207
219
  private
208
220
 
209
- # your params look like this:
221
+ # with the following params:
210
222
  # { user: { profile: [{ address: { street_name: "Main St" } }] } }
211
- #
212
- def params_filter
213
- Attribeauty::Params.with(request.params)
214
- end
215
223
 
216
- # exclude_if and required will be passed onto all attributes
217
- #
224
+ # required: true will be passed onto all attributes, except ip_address
225
+
218
226
  def update_params
219
- params_filter.accept exclude_if: :nil?, required: true do
227
+ params_filter.accept required: true do
220
228
  root :user do
221
229
  attribute :title, :string,
222
230
  attribute :email do
@@ -234,6 +242,102 @@ end
234
242
  See `test/test_params.rb` for more examples
235
243
 
236
244
 
245
+ ## Base
246
+
247
+ I needed a straightforward way to initialize mutable objects, and this solution provides exactly that. While there are many existing options (notably the Rails Attributes API), I opted to build my own.
248
+
249
+
250
+ Inherit from `Attribeauty::Base` and then add attribute with the type you want.
251
+ Initialize with these and they will be cast to that attribute.
252
+ Use `assign_attributes` to update the object.
253
+
254
+
255
+ ```ruby
256
+ class MyClass < Attribeauty::Base
257
+ attribute :first, :string
258
+ attribute :second, :integer
259
+ attribute :third, :float
260
+ attribute :forth, :boolean
261
+ attribute :fifth, :time
262
+ attribute :sixth, :koala
263
+ attribute :seventh, :string, default: "Kangaroo"
264
+ end
265
+
266
+ instance = MyClass.new(first: 456)
267
+ instance.first # => "456"
268
+ instance.assign_attributes(second: "456")
269
+ instance.second # => 456
270
+ instance.first = 9000
271
+ instance.first # => "9000"
272
+ instance.seventh # => "Kangaroo"
273
+ ```
274
+
275
+ To add your own types, simply have a class that handles `MyClassName.new.cast(value)`:
276
+
277
+ ```ruby
278
+ Attribeauty.configure do |config|
279
+ config.types[:koala] = MyTypes::Koala
280
+ end
281
+
282
+ module MyTypes
283
+ class Koala
284
+ def cast(value)
285
+ value.inspect.to_s << "_koalas"
286
+ end
287
+ end
288
+ end
289
+
290
+ class MyClass < Attribeauty::Base
291
+ attribute :wild_animal, :koala
292
+ end
293
+
294
+ instance = MyClass.new(wild_animal: "the_wildest_animals_are")
295
+ instance.wild_animal # => "the_wildest_animals_are_koalas"
296
+
297
+ ```
298
+
299
+ To use rails types add to your config:
300
+ ```ruby
301
+ # config/initializers/attribeauty.rb
302
+
303
+ Rails.application.reloader.to_prepare do
304
+ Attribeauty.configure do |config|
305
+ config.types[:string] = ActiveModel::Type::String
306
+ config.types[:boolean] = ActiveModel::Type::Boolean
307
+ config.types[:date] = ActiveModel::Type::Date
308
+ config.types[:time] = ActiveModel::Type::Time
309
+ config.types[:datetime] = ActiveModel::Type::DateTime
310
+ config.types[:float] = ActiveModel::Type::Float
311
+ config.types[:integer] = ActiveModel::Type::Integer
312
+ end
313
+ end
314
+
315
+ ```
316
+
317
+ #### Is this for rails only?
318
+ Nope, any ruby program will work with this.
319
+
320
+ ## Installation
321
+
322
+ Add `attribeauty` to your application's Gemfile and `bundle install` the gem:
323
+
324
+ ```ruby
325
+ # Gemfile
326
+ gem 'attribeauty'
327
+ ```
328
+
329
+ Use bundle to automatically install the gem and add to the application's Gemfile by executing:
330
+
331
+ ```bash
332
+ $ bundle add attribeauty
333
+ ```
334
+
335
+ If bundler is not being used to manage dependencies, install the gem by executing:
336
+
337
+ ```bash
338
+ $ gem install attribeauty
339
+ ```
340
+
237
341
  ## Development
238
342
 
239
343
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/attribeauty/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "attribeauty"
7
+ spec.version = Attribeauty::VERSION
8
+ spec.authors = ["Toby"]
9
+ spec.email = ["toby@darkroom.tech"]
10
+
11
+ spec.summary = "Attributes simply done"
12
+ spec.description = "There are so many of these, I just needed this one."
13
+ spec.homepage = "https://github.com/tobyond/attribeauty"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/tobyond/attribeauty"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(__dir__) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
25
+ end
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ # Uncomment to register a new dependency of your gem
32
+ # spec.add_dependency "example-gem", "~> 1.0"
33
+
34
+ # For more information and examples about making a new gem, check out our
35
+ # guide at: https://bundler.io/guides/creating_gem.html
36
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Attribeauty
4
- VERSION = "0.4.8"
4
+ VERSION = "0.4.11"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attribeauty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.8
4
+ version: 0.4.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toby
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-01 00:00:00.000000000 Z
11
+ date: 2024-08-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: There are so many of these, I just needed this one.
14
14
  email:
@@ -30,6 +30,7 @@ files:
30
30
  - LICENSE.txt
31
31
  - README.md
32
32
  - Rakefile
33
+ - attribeauty.gemspec
33
34
  - lib/attribeauty.rb
34
35
  - lib/attribeauty/base.rb
35
36
  - lib/attribeauty/configuration.rb