summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorVladimir Kochnev <hashtable@yandex.ru>2015-11-15 16:57:51 +0300
committerVladimir Kochnev <hashtable@yandex.ru>2019-01-28 21:14:08 +0300
commit250f174f48a7115c832690fd052dcaf63b6debc9 (patch)
tree84c55562cdd1281072bcd84114d11859a7f5e528 /spec
parentcd30488f9e4f4cbdfe0ccada40297e6f6d7e5610 (diff)
downloadhashie-250f174f48a7115c832690fd052dcaf63b6debc9.tar.gz
Add Hashie::Extensions::Mash::DefineAccessors.
This patch adds an extension for Mash that makes it behave like `OpenStruct`. It reduces overhead of `method_missing?` magic which is a good thing! It's inspired by the recent @sferik's work on `OpenStruct` — https://github.com/ruby/ruby/pull/1033. When using it in `Mash` subclasses it makes them *remember* methods so then it's more like `ActiveModel` than `OpenStruct` in this case. To use it like `OpenStruct` one could use this shortcut: ```ruby { foo: 1, bar: 2 }.to_mash.with_accessors! ``` Implementation details: It injects to class an anonymous module that stores accessor method definitions. This is inspired by `ActiveModel` / `ActiveRecord`. It allows to override accessors in subclass and call them via `super` if this is intended.
Diffstat (limited to 'spec')
-rw-r--r--spec/hashie/extensions/mash/define_accessors_spec.rb90
1 files changed, 90 insertions, 0 deletions
diff --git a/spec/hashie/extensions/mash/define_accessors_spec.rb b/spec/hashie/extensions/mash/define_accessors_spec.rb
new file mode 100644
index 0000000..de7abe7
--- /dev/null
+++ b/spec/hashie/extensions/mash/define_accessors_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+describe Hashie::Extensions::Mash::DefineAccessors do
+ let(:args) { [] }
+
+ shared_examples 'class with dynamically defined accessors' do
+ it 'defines reader on demand' do
+ expect(subject.method_defined?(:foo)).to be_falsey
+ instance.foo
+ expect(subject.method_defined?(:foo)).to be_truthy
+ end
+
+ it 'defines writer on demand' do
+ expect(subject.method_defined?(:foo=)).to be_falsey
+ instance.foo = :bar
+ expect(subject.method_defined?(:foo=)).to be_truthy
+ end
+
+ it 'defines predicate on demand' do
+ expect(subject.method_defined?(:foo?)).to be_falsey
+ instance.foo?
+ expect(subject.method_defined?(:foo?)).to be_truthy
+ end
+
+ it 'defines initializing reader on demand' do
+ expect(subject.method_defined?(:foo!)).to be_falsey
+ instance.foo!
+ expect(subject.method_defined?(:foo!)).to be_truthy
+ end
+
+ it 'defines underbang reader on demand' do
+ expect(subject.method_defined?(:foo_)).to be_falsey
+ instance.foo_
+ expect(subject.method_defined?(:foo_)).to be_truthy
+ end
+
+ context 'when initializing from another hash' do
+ let(:args) { [{ foo: :bar }] }
+
+ it 'does not define any accessors' do
+ expect(subject.method_defined?(:foo)).to be_falsey
+ expect(subject.method_defined?(:foo=)).to be_falsey
+ expect(subject.method_defined?(:foo?)).to be_falsey
+ expect(subject.method_defined?(:foo!)).to be_falsey
+ expect(subject.method_defined?(:foo_)).to be_falsey
+ expect(instance.foo).to eq :bar
+ end
+ end
+ end
+
+ context 'when included in Mash subclass' do
+ subject { Class.new(Hashie::Mash) { include Hashie::Extensions::Mash::DefineAccessors } }
+ let(:instance) { subject.new(*args) }
+
+ describe 'this subclass' do
+ it_behaves_like 'class with dynamically defined accessors'
+
+ describe 'when accessors are overrided in class' do
+ before do
+ subject.class_eval do
+ def foo
+ if self[:foo] != 1
+ :bar
+ else
+ super
+ end
+ end
+ end
+ end
+
+ it 'allows to call super' do
+ expect(instance.foo).to eq :bar
+ instance.foo = 2
+ expect(instance.foo).to eq :bar
+ instance.foo = 1
+ expect(instance.foo).to eq 1
+ end
+ end
+ end
+ end
+
+ context 'when Mash instance is extended' do
+ let(:instance) { Hashie::Mash.new(*args).with_accessors! }
+ subject { instance.singleton_class }
+
+ describe 'its singleton class' do
+ it_behaves_like 'class with dynamically defined accessors'
+ end
+ end
+end