hashie 2.1.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -111,7 +111,7 @@ module Hashie
111
111
 
112
112
  def optimize_if_necessary!
113
113
  if (@lookups += 1) >= @optimize_every
114
- @regexes = @regex_counts.sort_by { |regex, count| -count }.map { |regex, count| regex }
114
+ @regexes = @regex_counts.sort_by { |_, count| -count }.map { |regex, _| regex }
115
115
  @lookups = 0
116
116
  end
117
117
  end
@@ -19,23 +19,22 @@ module Hashie
19
19
  def self.property(property_name, options = {})
20
20
  super
21
21
 
22
- options[:from] = options[:from].to_sym if options[:from]
23
- property_name = property_name.to_sym
22
+ options[:from] = options[:from] if options[:from]
24
23
 
25
24
  if options[:from]
26
25
  if property_name == options[:from]
27
26
  fail ArgumentError, "Property name (#{property_name}) and :from option must not be the same"
28
27
  end
29
28
 
30
- translations[options[:from].to_sym] = property_name.to_sym
29
+ translations[options[:from]] = property_name
31
30
 
32
31
  define_method "#{options[:from]}=" do |val|
33
32
  with = options[:with] || options[:transform_with]
34
- self[property_name.to_sym] = with.respond_to?(:call) ? with.call(val) : val
33
+ self[property_name] = with.respond_to?(:call) ? with.call(val) : val
35
34
  end
36
35
  else
37
36
  if options[:transform_with].respond_to? :call
38
- transforms[property_name.to_sym] = options[:transform_with]
37
+ transforms[property_name] = options[:transform_with]
39
38
  end
40
39
  end
41
40
  end
@@ -43,10 +42,10 @@ module Hashie
43
42
  # Set a value on the Dash in a Hash-like way. Only works
44
43
  # on pre-existing properties.
45
44
  def []=(property, value)
46
- if self.class.translations.key? property.to_sym
45
+ if self.class.translations.key? property
47
46
  send("#{property}=", value)
48
- elsif self.class.transforms.key? property.to_sym
49
- super property, self.class.transforms[property.to_sym].call(value)
47
+ elsif self.class.transforms.key? property
48
+ super property, self.class.transforms[property].call(value)
50
49
  elsif property_exists? property
51
50
  super
52
51
  end
@@ -77,7 +76,7 @@ module Hashie
77
76
  # Raises an NoMethodError if the property doesn't exist
78
77
  #
79
78
  def property_exists?(property)
80
- unless self.class.property?(property.to_sym)
79
+ unless self.class.property?(property)
81
80
  fail NoMethodError, "The property '#{property}' is not defined for this Trash."
82
81
  end
83
82
  true
@@ -89,7 +88,7 @@ module Hashie
89
88
  def initialize_attributes(attributes)
90
89
  return unless attributes
91
90
  attributes_copy = attributes.dup.delete_if do |k, v|
92
- if self.class.translations.include?(k.to_sym)
91
+ if self.class.translations.include?(k)
93
92
  self[k] = v
94
93
  true
95
94
  end
@@ -1,3 +1,3 @@
1
1
  module Hashie
2
- VERSION = '2.1.2'
2
+ VERSION = '3.0.0'
3
3
  end
@@ -22,7 +22,7 @@ class PropertyBangTest < Hashie::Dash
22
22
  property :important!
23
23
  end
24
24
 
25
- class Subclassed < DashTest
25
+ class SubclassedTest < DashTest
26
26
  property :last_name, required: true
27
27
  end
28
28
 
@@ -35,7 +35,6 @@ class DeferredTest < Hashie::Dash
35
35
  end
36
36
 
37
37
  describe DashTest do
38
-
39
38
  subject { DashTest.new(first_name: 'Bob', email: 'bob@example.com') }
40
39
 
41
40
  it('subclasses Hashie::Hash') { should respond_to(:to_mash) }
@@ -74,7 +73,7 @@ describe DashTest do
74
73
  end
75
74
 
76
75
  it 'fails writing a required property to nil using []=' do
77
- expect { subject['first_name'] = nil }.to raise_error(ArgumentError)
76
+ expect { subject[:first_name] = nil }.to raise_error(ArgumentError)
78
77
  end
79
78
 
80
79
  it 'fails writing to a non-existent property using []=' do
@@ -82,9 +81,9 @@ describe DashTest do
82
81
  end
83
82
 
84
83
  it 'works for an existing property using []=' do
85
- subject['first_name'] = 'Bob'
86
- expect(subject['first_name']).to eq 'Bob'
84
+ subject[:first_name] = 'Bob'
87
85
  expect(subject[:first_name]).to eq 'Bob'
86
+ expect { subject['first_name'] }.to raise_error(NoMethodError)
88
87
  end
89
88
 
90
89
  it 'works for an existing property using a method call' do
@@ -99,14 +98,14 @@ describe DashTest do
99
98
  end
100
99
 
101
100
  it 'is able to retrieve properties through blocks' do
102
- subject['first_name'] = 'Aiden'
101
+ subject[:first_name] = 'Aiden'
103
102
  value = nil
104
- subject.[]('first_name') { |v| value = v }
103
+ subject.[](:first_name) { |v| value = v }
105
104
  expect(value).to eq 'Aiden'
106
105
  end
107
106
 
108
107
  it 'is able to retrieve properties through blocks with method calls' do
109
- subject['first_name'] = 'Frodo'
108
+ subject[:first_name] = 'Frodo'
110
109
  value = nil
111
110
  subject.first_name { |v| value = v }
112
111
  expect(value).to eq 'Frodo'
@@ -115,12 +114,12 @@ describe DashTest do
115
114
 
116
115
  context 'reading from deferred properties' do
117
116
  it 'evaluates proc after initial read' do
118
- expect(DeferredTest.new['created_at']).to be_instance_of(Time)
117
+ expect(DeferredTest.new[:created_at]).to be_instance_of(Time)
119
118
  end
120
119
 
121
120
  it 'does not evalute proc after subsequent reads' do
122
121
  deferred = DeferredTest.new
123
- expect(deferred['created_at'].object_id).to eq deferred['created_at'].object_id
122
+ expect(deferred[:created_at].object_id).to eq deferred[:created_at].object_id
124
123
  end
125
124
  end
126
125
 
@@ -139,7 +138,7 @@ describe DashTest do
139
138
  end
140
139
 
141
140
  it 'accepts block to define a global default' do
142
- obj = described_class.new { |hash, key| key.to_s.upcase }
141
+ obj = described_class.new { |_, key| key.to_s.upcase }
143
142
  expect(obj.first_name).to eq 'FIRST_NAME'
144
143
  expect(obj.count).to be_zero
145
144
  end
@@ -220,17 +219,17 @@ describe DashTest do
220
219
  end
221
220
 
222
221
  it 'checks if a property exists' do
223
- expect(described_class.property?('first_name')).to be true
224
- expect(described_class.property?(:first_name)).to be true
222
+ expect(described_class.property?(:first_name)).to be_truthy
223
+ expect(described_class.property?('first_name')).to be_falsy
225
224
  end
226
225
 
227
226
  it 'checks if a property is required' do
228
- expect(described_class.required?('first_name')).to be true
229
- expect(described_class.required?(:first_name)).to be true
227
+ expect(described_class.required?(:first_name)).to be_truthy
228
+ expect(described_class.required?('first_name')).to be_falsy
230
229
  end
231
230
 
232
231
  it 'doesnt include property from subclass' do
233
- expect(described_class.property?(:last_name)).to be false
232
+ expect(described_class.property?(:last_name)).to be_falsy
234
233
  end
235
234
 
236
235
  it 'lists declared defaults' do
@@ -238,7 +237,7 @@ describe DashTest do
238
237
  end
239
238
 
240
239
  it 'allows properties that end in bang' do
241
- expect(PropertyBangTest.property?(:important!)).to be true
240
+ expect(PropertyBangTest.property?(:important!)).to be_truthy
242
241
  end
243
242
  end
244
243
 
@@ -246,7 +245,7 @@ describe DashTest do
246
245
  before { subject.replace(first_name: 'Cain') }
247
246
 
248
247
  it 'return self' do
249
- expect(subject.replace(email: 'bar').to_hash).to eq('email' => 'bar', 'count' => 0)
248
+ expect(subject.replace(email: 'bar').to_hash).to eq(email: 'bar', count: 0)
250
249
  end
251
250
 
252
251
  it 'sets all specified keys to their corresponding values' do
@@ -254,7 +253,7 @@ describe DashTest do
254
253
  end
255
254
 
256
255
  it 'leaves only specified keys and keys with default values' do
257
- expect(subject.keys.sort).to eq %w(count first_name)
256
+ expect(subject.keys.sort_by { |key| key.to_s }).to eq [:count, :first_name]
258
257
  expect(subject.email).to be_nil
259
258
  expect(subject.count).to eq 0
260
259
  end
@@ -310,13 +309,14 @@ describe Hashie::Dash, 'inheritance' do
310
309
 
311
310
  it 'allows nil defaults' do
312
311
  @bottom.property :echo, default: nil
313
- expect(@bottom.new).to have_key('echo')
312
+ expect(@bottom.new).to have_key(:echo)
313
+ expect(@bottom.new).to_not have_key('echo')
314
314
  end
315
315
 
316
316
  end
317
317
 
318
- describe Subclassed do
319
- subject { Subclassed.new(first_name: 'Bob', last_name: 'McNob', email: 'bob@example.com') }
318
+ describe SubclassedTest do
319
+ subject { SubclassedTest.new(first_name: 'Bob', last_name: 'McNob', email: 'bob@example.com') }
320
320
 
321
321
  describe '#count' do
322
322
  subject { super().count }
@@ -329,10 +329,55 @@ describe Subclassed do
329
329
  it { should respond_to(:last_name=) }
330
330
 
331
331
  it 'has one additional property' do
332
- expect(described_class.property?(:last_name)).to be true
332
+ expect(described_class.property?(:last_name)).to be_truthy
333
333
  end
334
334
 
335
335
  it "didn't override superclass inheritance logic" do
336
- expect(described_class.instance_variable_get('@inheritance_test')).to be true
336
+ expect(described_class.instance_variable_get('@inheritance_test')).to be_truthy
337
+ end
338
+ end
339
+
340
+ class MixedPropertiesTest < Hashie::Dash
341
+ property :symbol
342
+ property 'string'
343
+ end
344
+
345
+ describe MixedPropertiesTest do
346
+ subject { MixedPropertiesTest.new('string' => 'string', symbol: 'symbol') }
347
+
348
+ it { should respond_to('string') }
349
+ it { should respond_to(:symbol) }
350
+
351
+ it 'property?' do
352
+ expect(described_class.property?('string')).to be_truthy
353
+ expect(described_class.property?(:symbol)).to be_truthy
354
+ end
355
+
356
+ it 'fetch' do
357
+ expect(subject['string']).to eq('string')
358
+ expect { subject[:string] }.to raise_error(NoMethodError)
359
+ expect(subject[:symbol]).to eq('symbol')
360
+ expect { subject['symbol'] }.to raise_error(NoMethodError)
361
+ end
362
+
363
+ it 'double define' do
364
+ klass = Class.new(MixedPropertiesTest) do
365
+ property 'symbol'
366
+ end
367
+ instance = klass.new(symbol: 'one', 'symbol' => 'two')
368
+ expect(instance[:symbol]).to eq('one')
369
+ expect(instance['symbol']).to eq('two')
370
+ end
371
+
372
+ it 'assign' do
373
+ subject['string'] = 'updated'
374
+ expect(subject['string']).to eq('updated')
375
+
376
+ expect { subject[:string] = 'updated' }.to raise_error(NoMethodError)
377
+
378
+ subject[:symbol] = 'updated'
379
+ expect(subject[:symbol]).to eq('updated')
380
+
381
+ expect { subject['symbol'] = 'updated' }.to raise_error(NoMethodError)
337
382
  end
338
383
  end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashie::Extensions::Dash::IndifferentAccess do
4
+ class DashWithIndifferentAccess < Hashie::Dash
5
+ include Hashie::Extensions::Dash::IndifferentAccess
6
+ property :name
7
+ end
8
+
9
+ context 'initialized with' do
10
+ it 'string' do
11
+ instance = DashWithIndifferentAccess.new('name' => 'Name')
12
+ expect(instance.name).to eq('Name')
13
+ expect(instance['name']).to eq('Name')
14
+ expect(instance[:name]).to eq('Name')
15
+ expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Name">')
16
+ expect(instance.to_hash).to eq('name' => 'Name')
17
+ end
18
+
19
+ it 'key' do
20
+ instance = DashWithIndifferentAccess.new(name: 'Name')
21
+ expect(instance.name).to eq('Name')
22
+ expect(instance['name']).to eq('Name')
23
+ expect(instance[:name]).to eq('Name')
24
+ expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Name">')
25
+ expect(instance.to_hash).to eq('name' => 'Name')
26
+ end
27
+ end
28
+
29
+ it 'updates' do
30
+ instance = DashWithIndifferentAccess.new
31
+ instance['name'] = 'Updated String'
32
+ expect(instance.name).to eq('Updated String')
33
+ instance[:name] = 'Updated Symbol'
34
+ expect(instance.name).to eq('Updated Symbol')
35
+ instance.name = 'Updated Method'
36
+ expect(instance.name).to eq('Updated Method')
37
+ end
38
+
39
+ context 'initialized with both prefers last assignment' do
40
+ it 'string, then symbol' do
41
+ instance = DashWithIndifferentAccess.new('name' => 'First', name: 'Last')
42
+ expect(instance.name).to eq('Last')
43
+ expect(instance['name']).to eq('Last')
44
+ expect(instance[:name]).to eq('Last')
45
+ expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="Last">')
46
+ expect(instance.to_hash).to eq('name' => 'Last')
47
+ end
48
+
49
+ it 'symbol then string' do
50
+ instance = DashWithIndifferentAccess.new(name: 'Last', 'name' => 'First')
51
+ expect(instance.name).to eq('First')
52
+ expect(instance['name']).to eq('First')
53
+ expect(instance[:name]).to eq('First')
54
+ expect(instance.inspect).to eq('#<DashWithIndifferentAccess name="First">')
55
+ expect(instance.to_hash).to eq('name' => 'First')
56
+ end
57
+ end
58
+ end
@@ -11,6 +11,7 @@ module Hashie
11
11
  { title: 'Call of the Wild' },
12
12
  { title: 'Moby Dick' }
13
13
  ],
14
+ shelves: nil,
14
15
  location: {
15
16
  address: '123 Library St.'
16
17
  }
@@ -62,6 +63,32 @@ module Hashie
62
63
  )
63
64
  end
64
65
  end
66
+
67
+ context 'when the nested object is missing' do
68
+ it 'raises an UndefinedPathError' do
69
+ expect do
70
+ instance.deep_fetch(:library, :unknown_key, :books)
71
+ end.to(
72
+ raise_error(
73
+ DeepFetch::UndefinedPathError,
74
+ 'Could not fetch path (library > unknown_key > books) at unknown_key'
75
+ )
76
+ )
77
+ end
78
+ end
79
+
80
+ context 'when the nested object is nil' do
81
+ it 'raises an UndefinedPathError' do
82
+ expect do
83
+ instance.deep_fetch(:library, :shelves, :address)
84
+ end.to(
85
+ raise_error(
86
+ DeepFetch::UndefinedPathError,
87
+ 'Could not fetch path (library > shelves > address) at address'
88
+ )
89
+ )
90
+ end
91
+ end
65
92
  end
66
93
  end
67
94
  end
@@ -1,23 +1,46 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Hashie::Extensions::IgnoreUndeclared do
4
- class ForgivingTrash < Hashie::Trash
5
- include Hashie::Extensions::IgnoreUndeclared
6
- property :city
7
- property :state, from: :provence
8
- end
4
+ context 'included in Trash' do
5
+ class ForgivingTrash < Hashie::Trash
6
+ include Hashie::Extensions::IgnoreUndeclared
7
+ property :city
8
+ property :state, from: :provence
9
+ end
9
10
 
10
- subject { ForgivingTrash }
11
+ subject { ForgivingTrash }
11
12
 
12
- it 'silently ignores undeclared properties on initialization' do
13
- expect { subject.new(city: 'Toronto', provence: 'ON', country: 'Canada') }.to_not raise_error
14
- end
13
+ it 'silently ignores undeclared properties on initialization' do
14
+ expect { subject.new(city: 'Toronto', provence: 'ON', country: 'Canada') }.to_not raise_error
15
+ end
16
+
17
+ it 'works with translated properties (with symbol keys)' do
18
+ expect(subject.new(provence: 'Ontario').state).to eq('Ontario')
19
+ end
15
20
 
16
- it 'works with translated properties (with symbol keys)' do
17
- expect(subject.new(provence: 'Ontario').state).to eq('Ontario')
21
+ it 'works with translated properties (with string keys)' do
22
+ expect(subject.new(provence: 'Ontario').state).to eq('Ontario')
23
+ end
24
+
25
+ it 'requires properties to be declared on assignment' do
26
+ hash = subject.new(city: 'Toronto')
27
+ expect { hash.country = 'Canada' }.to raise_error(NoMethodError)
28
+ end
18
29
  end
19
30
 
20
- it 'works with translated properties (with string keys)' do
21
- expect(subject.new(provence: 'Ontario').state).to eq('Ontario')
31
+ context 'combined with DeepMerge' do
32
+ class ForgivingTrashWithMerge < Hashie::Trash
33
+ include Hashie::Extensions::DeepMerge
34
+ include Hashie::Extensions::IgnoreUndeclared
35
+ property :some_key
36
+ end
37
+
38
+ it 'deep merges' do
39
+ class ForgivingTrashWithMergeAndProperty < ForgivingTrashWithMerge
40
+ property :some_other_key
41
+ end
42
+ hash = ForgivingTrashWithMergeAndProperty.new(some_ignored_key: 17, some_key: 12)
43
+ expect(hash.deep_merge(some_other_key: 55, some_ignored_key: 18)).to eq(some_key: 12, some_other_key: 55)
44
+ end
22
45
  end
23
46
  end
@@ -103,12 +103,12 @@ describe Hashie::Extensions::IndifferentAccess do
103
103
  it 'removes old keys' do
104
104
  [:foo, 'foo'].each do |k|
105
105
  expect(h[k]).to be_nil
106
- expect(h.key?(k)).to be false
106
+ expect(h.key?(k)).to be_falsy
107
107
  end
108
108
  end
109
109
 
110
110
  it 'creates new keys with indifferent access' do
111
- [:bar, 'bar', :hi, 'hi'].each { |k| expect(h.key?(k)).to be true }
111
+ [:bar, 'bar', :hi, 'hi'].each { |k| expect(h.key?(k)).to be_truthy }
112
112
  expect(h[:bar]).to eq 'baz'
113
113
  expect(h['bar']).to eq 'baz'
114
114
  expect(h[:hi]).to eq 'bye'