hashie 2.1.2 → 3.0.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.
@@ -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'