hash_cast 0.5.0 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/lib/hash_cast/caster.rb +13 -8
- data/lib/hash_cast/casters/string_caster.rb +13 -0
- data/lib/hash_cast/config.rb +7 -1
- data/lib/hash_cast/{attributes_caster.rb → recursive_caster_applicator.rb} +3 -3
- data/lib/hash_cast/version.rb +1 -1
- data/lib/hash_cast.rb +11 -1
- data/spec/hash_cast/caster_spec.rb +125 -69
- data/spec/hash_cast/hash_cast_spec.rb +4 -4
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 899228916d29fc1b30a38151b850f6734ea57cb7390561b6a3aaecd0e3e1a7e6
|
4
|
+
data.tar.gz: b055bcb17b4863fea30a1f5b3a6e739514aaff9ca853fcefb3ee702bed3ece5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ebd62549257464c42f8d9550b5a3a592e9b84fc430619821818497d74bc1c95946effc1c21188a619834f6420b3c78fef2aac6f9859289da0ec7719188e9726
|
7
|
+
data.tar.gz: 0134f1c7695a0c205e0b9f57fa6ed995fc568e55e1e999b37d80973ac102c0450d2bde94eaf20c7c72f74f534d9da6d9432a017224d1bac4eb4b05e8b04e9f2c
|
data/CHANGELOG.md
ADDED
data/Gemfile.lock
CHANGED
data/lib/hash_cast/caster.rb
CHANGED
@@ -99,21 +99,26 @@ module HashCast::Caster
|
|
99
99
|
# @param hash [Hash] hash for casting
|
100
100
|
# @param options [Hash] options, input_keys: :string, output_key: :symbol
|
101
101
|
def cast(hash, options = {})
|
102
|
-
|
103
|
-
check_hash_given!(hash)
|
104
|
-
check_options!(options)
|
105
|
-
set_default_options(options)
|
106
|
-
|
107
|
-
attributes_caster = HashCast::AttributesCaster.new(class_variable_get(:@@attributes), options)
|
108
|
-
attributes_caster.cast(hash)
|
102
|
+
cast_with_attributes(hash, get_attributes, options)
|
109
103
|
end
|
110
104
|
|
111
105
|
private
|
112
106
|
|
113
|
-
def
|
107
|
+
def get_attributes
|
114
108
|
unless class_variable_defined?(:@@attributes)
|
115
109
|
raise HashCast::Errors::ArgumentError, "Attributes block should be defined"
|
116
110
|
end
|
111
|
+
|
112
|
+
class_variable_get(:@@attributes)
|
113
|
+
end
|
114
|
+
|
115
|
+
def cast_with_attributes(hash, attributes, options = {})
|
116
|
+
check_hash_given!(hash)
|
117
|
+
check_options!(options)
|
118
|
+
set_default_options(options)
|
119
|
+
|
120
|
+
caster_applicator = HashCast::RecursiveCasterApplicator.new(attributes, options)
|
121
|
+
caster_applicator.cast(hash)
|
117
122
|
end
|
118
123
|
|
119
124
|
def check_options!(options)
|
@@ -1,6 +1,19 @@
|
|
1
1
|
class HashCast::Casters::StringCaster
|
2
|
+
NULL_BYTE_CHARACTER = "\u0000".freeze
|
2
3
|
|
3
4
|
def self.cast(value, attr_name, options = {})
|
5
|
+
casted_value = cast_string(value, attr_name, options)
|
6
|
+
|
7
|
+
if HashCast.config.validate_string_null_byte && casted_value.match?(NULL_BYTE_CHARACTER)
|
8
|
+
raise HashCast::Errors::CastingError, 'contains invalid characters'
|
9
|
+
end
|
10
|
+
|
11
|
+
casted_value
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.cast_string(value, attr_name, options = {})
|
4
17
|
if value.is_a?(String)
|
5
18
|
value
|
6
19
|
elsif value.is_a?(Symbol)
|
data/lib/hash_cast/config.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class HashCast::Config
|
2
|
-
attr_accessor :input_keys, :output_keys
|
2
|
+
attr_accessor :input_keys, :output_keys, :validate_string_null_byte
|
3
3
|
|
4
4
|
def input_keys
|
5
5
|
@input_keys || :symbol
|
@@ -8,4 +8,10 @@ class HashCast::Config
|
|
8
8
|
def output_keys
|
9
9
|
@output_keys || :symbol
|
10
10
|
end
|
11
|
+
|
12
|
+
def validate_string_null_byte
|
13
|
+
return true if @validate_string_null_byte.nil?
|
14
|
+
|
15
|
+
@validate_string_null_byte
|
16
|
+
end
|
11
17
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class HashCast::
|
1
|
+
class HashCast::RecursiveCasterApplicator
|
2
2
|
attr_reader :attributes, :options
|
3
3
|
|
4
4
|
def initialize(attributes, options)
|
@@ -19,11 +19,11 @@ class HashCast::AttributesCaster
|
|
19
19
|
handle_attribute_error(e, attribute)
|
20
20
|
end
|
21
21
|
else
|
22
|
-
raise HashCast::Errors::MissingAttributeError.new("should be given", attribute.name)if attribute.required?
|
22
|
+
raise HashCast::Errors::MissingAttributeError.new("should be given", attribute.name) if attribute.required?
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
if !options[:skip_unexpected_attributes]
|
26
|
+
if options.has_key?(:skip_unexpected_attributes) && !options[:skip_unexpected_attributes]
|
27
27
|
check_unexpected_attributes_not_given!(hash_keys, casted_hash.keys)
|
28
28
|
end
|
29
29
|
|
data/lib/hash_cast/version.rb
CHANGED
data/lib/hash_cast.rb
CHANGED
@@ -5,7 +5,7 @@ require 'hash_cast/casters'
|
|
5
5
|
require 'hash_cast/concern.rb'
|
6
6
|
require 'hash_cast/metadata/attribute'
|
7
7
|
require 'hash_cast/attributes_parser'
|
8
|
-
require 'hash_cast/
|
8
|
+
require 'hash_cast/recursive_caster_applicator'
|
9
9
|
require 'hash_cast/caster'
|
10
10
|
|
11
11
|
module HashCast
|
@@ -32,6 +32,16 @@ module HashCast
|
|
32
32
|
def self.config
|
33
33
|
@@config ||= HashCast::Config.new
|
34
34
|
end
|
35
|
+
|
36
|
+
def self.create(&block)
|
37
|
+
Class.new do
|
38
|
+
include HashCast::Caster
|
39
|
+
|
40
|
+
attributes do
|
41
|
+
instance_exec(&block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
35
45
|
end
|
36
46
|
|
37
47
|
HashCast.add_caster(:array, HashCast::Casters::ArrayCaster)
|
@@ -54,7 +54,7 @@ describe HashCast::Caster do
|
|
54
54
|
|
55
55
|
casted_hash = ContactCaster.cast(input_hash)
|
56
56
|
|
57
|
-
casted_hash.
|
57
|
+
expect(casted_hash).to eq({
|
58
58
|
contact: {
|
59
59
|
name: "John Smith",
|
60
60
|
age: 22,
|
@@ -77,7 +77,7 @@ describe HashCast::Caster do
|
|
77
77
|
},
|
78
78
|
]
|
79
79
|
}
|
80
|
-
}
|
80
|
+
})
|
81
81
|
end
|
82
82
|
|
83
83
|
describe "Custom casters" do
|
@@ -119,14 +119,14 @@ describe HashCast::Caster do
|
|
119
119
|
]
|
120
120
|
)
|
121
121
|
|
122
|
-
casted_hash.
|
122
|
+
expect(casted_hash).to eq({
|
123
123
|
name: "Might & Magic",
|
124
124
|
settings: { account: "migthy_lord" },
|
125
125
|
emails: [
|
126
126
|
{ address: "test1@example.com" },
|
127
127
|
{ address: "test2@example.com" }
|
128
128
|
]
|
129
|
-
}
|
129
|
+
})
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
@@ -221,69 +221,6 @@ describe HashCast::Caster do
|
|
221
221
|
end.to_not raise_error
|
222
222
|
end
|
223
223
|
|
224
|
-
it "should raise error if unexpected attribute was given" do
|
225
|
-
input_hash = {
|
226
|
-
contact: {
|
227
|
-
wrong_attribute: 'foo',
|
228
|
-
name: "Jim",
|
229
|
-
weight: 65.5,
|
230
|
-
birthday: Date.today,
|
231
|
-
last_logged_in: DateTime.now,
|
232
|
-
last_visited_at: Time.now,
|
233
|
-
company: {
|
234
|
-
name: "MyCo",
|
235
|
-
},
|
236
|
-
emails: [ "test@example.com", "test2@example.com" ],
|
237
|
-
social_accounts: [
|
238
|
-
{
|
239
|
-
name: "john_smith",
|
240
|
-
type: :twitter,
|
241
|
-
},
|
242
|
-
{
|
243
|
-
name: "John",
|
244
|
-
type: :facebook,
|
245
|
-
},
|
246
|
-
]
|
247
|
-
}
|
248
|
-
}
|
249
|
-
|
250
|
-
expect do
|
251
|
-
ContactCaster.cast(input_hash)
|
252
|
-
end.to raise_error(HashCast::Errors::UnexpectedAttributeError, "contact[wrong_attribute] is not valid attribute name")
|
253
|
-
end
|
254
|
-
|
255
|
-
it "shouldn't unexpected attributes error if skip_unexpected_attributes flag is set to true" do
|
256
|
-
input_hash = {
|
257
|
-
contact: {
|
258
|
-
wrong_attribute: 'foo',
|
259
|
-
name: "Jim",
|
260
|
-
weight: 65.5,
|
261
|
-
birthday: Date.today,
|
262
|
-
last_logged_in: DateTime.now,
|
263
|
-
last_visited_at: Time.now,
|
264
|
-
company: {
|
265
|
-
name: "MyCo",
|
266
|
-
},
|
267
|
-
emails: [ "test@example.com", "test2@example.com" ],
|
268
|
-
social_accounts: [
|
269
|
-
{
|
270
|
-
name: "john_smith",
|
271
|
-
type: :twitter,
|
272
|
-
},
|
273
|
-
{
|
274
|
-
name: "John",
|
275
|
-
type: :facebook,
|
276
|
-
},
|
277
|
-
]
|
278
|
-
}
|
279
|
-
}
|
280
|
-
|
281
|
-
expect do
|
282
|
-
ContactCaster.cast(input_hash, skip_unexpected_attributes: true)
|
283
|
-
end.not_to raise_error(HashCast::Errors::UnexpectedAttributeError)
|
284
|
-
|
285
|
-
end
|
286
|
-
|
287
224
|
it "should convert accept hash with string keys and cast them to symbol keys" do
|
288
225
|
input_hash = {
|
289
226
|
'contact' => {
|
@@ -312,7 +249,7 @@ describe HashCast::Caster do
|
|
312
249
|
|
313
250
|
casted_hash = ContactCaster.cast(input_hash, input_keys: :string, output_keys: :symbol)
|
314
251
|
|
315
|
-
casted_hash.
|
252
|
+
expect(casted_hash).to eq({
|
316
253
|
contact: {
|
317
254
|
name: "John Smith",
|
318
255
|
age: 22,
|
@@ -335,12 +272,106 @@ describe HashCast::Caster do
|
|
335
272
|
},
|
336
273
|
]
|
337
274
|
}
|
275
|
+
})
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
context "checking unexpected attributes" do
|
280
|
+
before(:all) do
|
281
|
+
class BillingDetailsCaster
|
282
|
+
include HashCast::Caster
|
283
|
+
|
284
|
+
attributes do
|
285
|
+
string :name, optional: true
|
286
|
+
|
287
|
+
array :contacts, each: :hash, optional: true do
|
288
|
+
string :email
|
289
|
+
end
|
290
|
+
|
291
|
+
hash :address, optional: true do
|
292
|
+
string :city
|
293
|
+
string :country
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
it "doesn't raise unexpected attributes error by default" do
|
300
|
+
input_hash = {
|
301
|
+
wrong_attribute: 'foo',
|
302
|
+
name: "Jim",
|
303
|
+
address: {
|
304
|
+
city: "New York",
|
305
|
+
country: "USA",
|
306
|
+
street: "Random street"
|
307
|
+
},
|
308
|
+
contacts: [
|
309
|
+
{
|
310
|
+
name: "john_smith",
|
311
|
+
email: "john@example.com",
|
312
|
+
}
|
313
|
+
]
|
314
|
+
}
|
315
|
+
|
316
|
+
output_hash = BillingDetailsCaster.cast(input_hash)
|
317
|
+
|
318
|
+
expect(output_hash).to eq({
|
319
|
+
name: "Jim",
|
320
|
+
address: {
|
321
|
+
city: "New York",
|
322
|
+
country: "USA"
|
323
|
+
},
|
324
|
+
contacts: [
|
325
|
+
{
|
326
|
+
email: "john@example.com",
|
327
|
+
}
|
328
|
+
]
|
329
|
+
})
|
330
|
+
end
|
331
|
+
|
332
|
+
it "raise error for unexpected root attribute if skip_unexpected_attributes=false" do
|
333
|
+
input_hash = {
|
334
|
+
wrong_attribute: 'foo',
|
335
|
+
name: "Jim"
|
338
336
|
}
|
337
|
+
|
338
|
+
expect do
|
339
|
+
BillingDetailsCaster.cast(input_hash, skip_unexpected_attributes: false)
|
340
|
+
end.to raise_error(HashCast::Errors::UnexpectedAttributeError, "wrong_attribute is not valid attribute name")
|
341
|
+
end
|
342
|
+
|
343
|
+
it "raise error for unexpected root attribute if skip_unexpected_attributes=false" do
|
344
|
+
input_hash = {
|
345
|
+
name: "Jim",
|
346
|
+
address: {
|
347
|
+
city: "New York",
|
348
|
+
country: "USA",
|
349
|
+
street: "Random street"
|
350
|
+
}
|
351
|
+
}
|
352
|
+
|
353
|
+
expect do
|
354
|
+
BillingDetailsCaster.cast(input_hash, skip_unexpected_attributes: false)
|
355
|
+
end.to raise_error(HashCast::Errors::UnexpectedAttributeError, "address[street] is not valid attribute name")
|
356
|
+
end
|
357
|
+
|
358
|
+
it "raise error for unexpected root attribute if skip_unexpected_attributes=false" do
|
359
|
+
input_hash = {
|
360
|
+
name: "Jim",
|
361
|
+
contacts: [{
|
362
|
+
name: "john_smith",
|
363
|
+
email: "john@example.com",
|
364
|
+
}]
|
365
|
+
}
|
366
|
+
|
367
|
+
expect do
|
368
|
+
BillingDetailsCaster.cast(input_hash, skip_unexpected_attributes: false)
|
369
|
+
end.to raise_error(HashCast::Errors::UnexpectedAttributeError, "contacts[name] is not valid attribute name")
|
339
370
|
end
|
340
371
|
end
|
341
372
|
|
342
373
|
context "checking invalid parameters" do
|
343
|
-
it "should raise
|
374
|
+
it "should raise CasterNotFound exception if caster name is invalid" do
|
344
375
|
expect do
|
345
376
|
class WrongCaster
|
346
377
|
include HashCast::Caster
|
@@ -381,4 +412,29 @@ describe HashCast::Caster do
|
|
381
412
|
end.to raise_error(HashCast::Errors::CastingError, "city should be a string")
|
382
413
|
end
|
383
414
|
end
|
415
|
+
|
416
|
+
context "string caster" do
|
417
|
+
before(:all) do
|
418
|
+
class HomeCaster
|
419
|
+
include HashCast::Caster
|
420
|
+
|
421
|
+
attributes do
|
422
|
+
string :city
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
after{ HashCast.config.validate_string_null_byte = nil }
|
428
|
+
|
429
|
+
it "should allow null byte if validate_string_null_byte config is set to false" do
|
430
|
+
HashCast.config.validate_string_null_byte = false
|
431
|
+
HomeCaster.cast(city: "\u0000")
|
432
|
+
end
|
433
|
+
|
434
|
+
it "should not allow null byte if validate_string_null_byte config by default" do
|
435
|
+
expect do
|
436
|
+
HomeCaster.cast(city: "\u0000")
|
437
|
+
end.to raise_error(HashCast::Errors::CastingError, "city contains invalid characters")
|
438
|
+
end
|
439
|
+
end
|
384
440
|
end
|
@@ -4,11 +4,10 @@ describe HashCast do
|
|
4
4
|
|
5
5
|
describe ".create" do
|
6
6
|
it "should cast hash attributes" do
|
7
|
-
pending "NOT YET IMPLEMENTED"
|
8
7
|
input_hash = {
|
9
8
|
contact: {
|
10
9
|
name: "John Smith",
|
11
|
-
age:
|
10
|
+
age: 22,
|
12
11
|
company: {
|
13
12
|
name: "MyCo",
|
14
13
|
}
|
@@ -30,8 +29,9 @@ describe HashCast do
|
|
30
29
|
end
|
31
30
|
|
32
31
|
casted_hash = caster.cast(input_hash)
|
33
|
-
|
34
|
-
|
32
|
+
|
33
|
+
expect(casted_hash.object_id).to_not eq(input_hash.object_id)
|
34
|
+
expect(casted_hash).to eq(input_hash)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hash_cast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Albert Gazizov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- ".rspec"
|
51
51
|
- ".ruby-gemset"
|
52
52
|
- ".ruby-version"
|
53
|
+
- CHANGELOG.md
|
53
54
|
- Gemfile
|
54
55
|
- Gemfile.lock
|
55
56
|
- LICENSE.txt
|
@@ -57,7 +58,6 @@ files:
|
|
57
58
|
- Rakefile
|
58
59
|
- hash_cast.gemspec
|
59
60
|
- lib/hash_cast.rb
|
60
|
-
- lib/hash_cast/attributes_caster.rb
|
61
61
|
- lib/hash_cast/attributes_parser.rb
|
62
62
|
- lib/hash_cast/caster.rb
|
63
63
|
- lib/hash_cast/casters.rb
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- lib/hash_cast/config.rb
|
76
76
|
- lib/hash_cast/errors.rb
|
77
77
|
- lib/hash_cast/metadata/attribute.rb
|
78
|
+
- lib/hash_cast/recursive_caster_applicator.rb
|
78
79
|
- lib/hash_cast/version.rb
|
79
80
|
- spec/hash_cast/caster_spec.rb
|
80
81
|
- spec/hash_cast/hash_cast_spec.rb
|