blobject 0.2.3 → 0.3.2

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.
@@ -1,3 +1,3 @@
1
1
  class Blobject
2
- VERSION = '0.2.3'
2
+ VERSION = '0.3.2'
3
3
  end
data/makefile ADDED
@@ -0,0 +1,4 @@
1
+ test:
2
+ ./spec/exec
3
+
4
+ .PHONY: test
@@ -1,155 +1,277 @@
1
- require 'spec_helper'
1
+ require 'spec/env'
2
2
  require 'blobject'
3
3
 
4
4
  describe Blobject do
5
-
6
- let(:blobject){Blobject.new}
7
-
8
- it 'should not be able to assign to a blobject outside of a modify block using =' do
9
- expect {
10
- blobject.name = 'hello'
11
-
12
- }.to raise_error
5
+
6
+ def b
7
+ @b ||= Blobject.new
8
+ end
9
+
10
+ it 'raises an error if the call is not a reader, writer or checker' do
11
+
12
+ proc do
13
+ b.fish_for :salmon, :sole
14
+ end.must_raise NoMethodError
15
+
16
+ proc do
17
+ b.checker_with_an_arg? :this_should_not_be_here
18
+ end.must_raise NoMethodError
19
+
20
+ end
21
+
22
+ it 'provides access tot he internal hash with #hash' do
23
+ assert b.hash.equal?(b.instance_variable_get :@hash)
24
+ end
25
+
26
+ it 'sets values when calling a writer' do
27
+ b.number = 123
28
+ assert_equal b.number, 123
29
+ end
30
+
31
+ it 'sets objects n levels deep' do
32
+ b.name.nickname.back_at_school = 'Richard Head'
33
+ assert_equal b.name.nickname.back_at_school, 'Richard Head'
34
+ end
35
+
36
+ it 'does not result in a graph containing empty blobjects' do
37
+ b.fish.food
38
+ assert !b.fish?, 'should not have assigned an empty blobject'
13
39
  end
14
-
15
- it 'should not be able to assign to a blobject outside of a modify block without =' do
16
- expect {
17
- blobject.name 'hello'
18
- }.to raise_error
19
- end
40
+
41
+ it 'turns hashes into blobjects when assigning' do
20
42
 
21
- it 'should raise an error when call members that have not been assign' do
43
+ b.name = { christian: "Vinnie", surname: "Jones" }
22
44
 
23
- expect {
24
- blobject.meow
25
- }.to raise_error(NoMethodError)
45
+ assert_instance_of Blobject, b.name
46
+ assert_equal b.name.christian, "Vinnie"
47
+ assert_equal b.name.surname, "Jones"
26
48
  end
27
-
28
- describe '#empty?' do
29
- it 'should return true on an empty blobject' do
30
- blobject.should be_empty
31
- end
49
+
50
+ describe 'respond_to?' do
32
51
 
33
- it 'should return false on an non-empty blobject' do
34
- blobject.modify { name "Barry" }
35
- blobject.should_not be_empty
52
+ it 'returns true if the blobject has the corresponding member' do
53
+ b.name = 'jim'
54
+ assert b.respond_to?(:name)
55
+ assert b.respond_to?(:name=)
56
+ assert b.respond_to?(:name?)
36
57
  end
37
- end
38
-
39
- describe '#modify' do
40
-
41
- it 'should store assignment of variables to any depth in the object graph' do
42
-
43
- b = blobject.modify do
44
- variable 'hello'
45
- deep.object.graph.member 123
58
+
59
+ it 'should return true if a prohibited attribute name is a defined method' do
60
+ def b.to_ary
61
+ 123
46
62
  end
47
-
48
- blobject.variable.should == 'hello'
49
- blobject.deep.object.graph.member.should == 123
63
+ assert b.respond_to? :to_ary
50
64
  end
51
-
52
- it 'should not handle more than one parameter' do
53
- expect {
54
- blobject.modify do
55
- name(1, 2)
56
- end
57
- }.to raise_error(NoMethodError)
65
+
66
+ it 'should return false if the methods ends with a !' do
67
+ refute b.respond_to? :hello!
58
68
  end
59
-
60
- it 'should return an empty blobject when a non-existing member is called' do
61
- blobject.modify do
62
- b = name
63
- name.should be_instance_of Blobject
64
- name.should be_empty
65
- end
69
+
70
+ it 'returns true if the blobject has no corresponding member' do
71
+ b.name = 'jim'
72
+ assert b.respond_to?(:name)
73
+ assert b.respond_to?(:name=)
74
+ assert b.respond_to?(:name?)
66
75
  end
67
-
68
- it 'should allow assignment chaining' do
69
- blobject.modify do
70
- name.first('Barry').last('Watkins')
71
- end
72
-
73
- blobject.name.first.should == 'Barry'
74
- blobject.name.last.should == 'Watkins'
76
+
77
+ it 'should return true if the blobject has the corresponding member but the accessor has not been memoized' do
78
+ b = Blobject.new :name => 'barry'
79
+ assert b.respond_to?(:name)
80
+ assert b.respond_to?(:name=)
81
+ assert b.respond_to?(:name?)
75
82
  end
76
-
77
- it 'should return self' do
78
- rtn = blobject.modify {}
79
- rtn.should equal blobject
83
+
84
+ it 'returns false for :to_ary because that method is not allowed' do
85
+ refute Blobject.new.respond_to? :to_ary
80
86
  end
87
+ end
88
+
89
+ describe 'to_hash' do
90
+
91
+ it 'should recursively reify the blobject into a hash' do
92
+ b.name.first = 'barry'
93
+ b.number = 123456
94
+
95
+ h = b.to_hash
96
+
97
+ assert_instance_of Hash, h
98
+ assert_instance_of Hash, h[:name]
99
+ assert_equal h[:number], 123456
100
+ assert_equal h[:name][:first], 'barry'
101
+ end
102
+ end
103
+
104
+ describe 'from_json' do
81
105
 
82
- it 'should allow for nested modification blocks' do
106
+ describe 'array' do
83
107
 
84
- blobject.modify do
85
-
86
- name.middle = 'mittens'
87
-
88
- # makes sure that previously assigned members can be modified with a block
89
-
90
- name do
91
- first 'barry'
92
- last 'mcdoodle'
93
- end
108
+ it 'returns an array with blobjects not hashes' do
109
+ json = '[1, true, {"meaning": false}]'
110
+ array = Blobject.from_json json
111
+
112
+ assert_instance_of Array, array
113
+ assert_equal array[0], 1
114
+ assert_equal array[1], true
115
+ assert_instance_of Blobject, array[2]
94
116
  end
117
+ end
118
+
119
+ describe 'json object' do
95
120
 
96
- blobject.name.first.should == 'barry'
97
- blobject.name.last.should == 'mcdoodle'
98
-
121
+ it 'returns a blobject which' do
122
+ json = '{"name": {"first": "doogle"}}'
123
+ b = Blobject.from_json json
124
+ assert_equal b.name.first, 'doogle'
125
+ assert b.name?
126
+
127
+ end
99
128
  end
100
129
  end
101
130
 
102
- describe '#has_<name>' do
103
-
104
- it 'should return true when we have a member of name' do
105
- blobject.modify {name 'barry'}
106
- blobject.should have_name
131
+ describe 'initialize' do
132
+ it 'takes an initial value as a hash' do
133
+ b = Blobject.new :inner => {:nested => :hash}, :value => 12345
134
+ assert_equal b.value, 12345
135
+ assert b.inner?
136
+ assert_equal b.inner.nested, :hash
107
137
  end
108
-
109
- it 'should return false otherwise' do
110
- blobject.should_not have_name
138
+
139
+ it 'recurses through the initial hash turning hashes into blobjects' do
140
+ b = Blobject.new :name => {:first => 'doogle', :last => 'mcfoogle'}
141
+ assert_instance_of Blobject, b.name
142
+ assert_equal b.name.first, 'doogle'
143
+ end
144
+
145
+ it 'yields to a block with self as a parameter' do
146
+ b = Blobject.new do |b|
147
+ b.name = 'yield'
148
+ end
149
+
150
+ assert_equal 'yield', b.name
111
151
  end
112
152
  end
113
-
114
- describe '#[]' do
115
- it 'allows get via hash like dereferencing' do
116
- blobject.modify {name 'rod'}
117
- blobject[:name].should == 'rod'
153
+
154
+ describe 'checking' do
155
+
156
+ it 'returns true if the attribute exists' do
157
+ b.fish = 'bass'
158
+ assert b.fish?
118
159
  end
119
-
120
- it 'nil should be returned for a key miss' do
121
- blobject[:name].should == nil
160
+
161
+ it 'returns false if the attribute does not exist' do
162
+ assert !b.fish?
122
163
  end
123
- end
124
-
125
- describe '#[]=' do
126
- it 'should relay to send' do
127
- blobject.should_receive(:send).with(:name, 'barry')
128
- blobject[:name] = 'barry'
164
+
165
+ it 'does not indicate the boolean status of the value' do
166
+ b.fish = false
167
+ assert b.fish?
129
168
  end
130
169
  end
131
170
 
132
- describe '#merge' do
133
- let(:data) do
134
- { :one => 1,
135
- :rest => [2,3,4,5,6,{:number => 7},8,9,0],
136
- :nested => {
137
- :structure => 'of values'
138
- } }
171
+ describe 'memoization' do
172
+
173
+ it 'creates checker, reader and writer memoize methods after they are called the first time' do
174
+
175
+ b.method_unlikely_already_to_be_defined = 123
176
+
177
+ b.methods.must_include :method_unlikely_already_to_be_defined
178
+ b.methods.must_include :method_unlikely_already_to_be_defined=
179
+ b.methods.must_include :method_unlikely_already_to_be_defined?
180
+
181
+ b.another_method_unlikely_already_to_be_defined
182
+
183
+ b.methods.must_include :another_method_unlikely_already_to_be_defined
184
+ b.methods.must_include :another_method_unlikely_already_to_be_defined=
185
+ b.methods.must_include :another_method_unlikely_already_to_be_defined?
186
+
187
+ b.yet_another_method_unlikely_already_to_be_defined?
188
+
189
+ b.methods.must_include :another_method_unlikely_already_to_be_defined
190
+ b.methods.must_include :another_method_unlikely_already_to_be_defined=
191
+ b.methods.must_include :another_method_unlikely_already_to_be_defined?
139
192
  end
140
-
141
- it 'should return self' do
142
- blobject.merge({}).should equal blobject
193
+
194
+ it 'does not redefine existing members' do
195
+
196
+ def b.hello
197
+ 123
198
+ end
199
+
200
+ b.hello=456
201
+
202
+ assert_equal 123, b.hello
203
+
204
+ def b.oink= v
205
+ @oink = v
206
+ end
207
+
208
+ b.oink = 123
209
+
210
+ b.hash[:oink] = 456
211
+
212
+ def b.oink_value
213
+ @oink
214
+ end
215
+
216
+ assert_equal 123, b.oink_value
217
+
143
218
  end
144
-
145
- it 'should populate itself with the hash' do
146
- blobject.merge data
147
219
 
148
- blobject.one.should == 1
149
- blobject.rest[5].should be_instance_of(Blobject)
150
- blobject.rest[5].number.should == 7
151
- blobject.nested.structure.should == 'of values'
220
+ describe 'memoized reader' do
221
+ it 'returns an empty blobject' do
222
+
223
+ b.name.first = 'Harry'
224
+
225
+ b = Blobject.new
226
+
227
+ assert !b.name.nil?
228
+
229
+ b.name.first = 'Barry'
230
+
231
+ assert_equal b.name.first, 'Barry'
232
+ end
152
233
  end
153
234
  end
154
235
 
155
- end
236
+ describe 'frozen blobject' do
237
+
238
+ before :each do
239
+ list_element = Blobject.new
240
+
241
+ b.name.first = 'barry'
242
+ b.data.list = [1, 2, 3, list_element]
243
+ b.data.inner_hash = {:inner => {:one => 1}}
244
+
245
+ b.freeze
246
+ end
247
+
248
+ it 'still provides access' do
249
+ refute_nil b.name.first
250
+ end
251
+
252
+ it 'freezes the internal hash' do
253
+ assert b.hash.frozen?
254
+ end
255
+
256
+ it 'allows access to existing attributes' do
257
+ assert_equal b.name.first, 'barry'
258
+ end
259
+
260
+ it 'recursively freezes nested Blobjects' do
261
+ assert b.frozen?
262
+ assert b.name.frozen?
263
+ assert b.data.list[3].frozen?
264
+ assert b.data.inner_hash.frozen?
265
+ end
266
+
267
+ it 'raises an error when trying to set an attribute' do
268
+ proc { b.hello = 123 }.must_raise RuntimeError
269
+ end
270
+
271
+ it 'returns nil when trying to get an attribute' do
272
+ assert b.meow_face.nil?, 'method missing returned something'
273
+ # check again to test memoized method
274
+ assert b.meow_face.nil?, 'memoized method returned something'
275
+ end
276
+ end
277
+ end
data/spec/env.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ $:.push 'lib', 'spec'
5
+
6
+ require 'minitest/autorun'
7
+ require 'minitest/spec'
8
+
9
+ require 'minitest/reporters'
10
+
11
+ MiniTest::Unit.runner = MiniTest::SuiteRunner.new
12
+ MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
data/spec/exec ADDED
@@ -0,0 +1,7 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'ruby-debug' if ARGV.include? 'debug'
4
+
5
+ $:.push '.'
6
+
7
+ Dir['./spec/**/*_spec.rb'].each { |test_file| require test_file }
@@ -0,0 +1 @@
1
+ ["one","two",{"three":{"a":1,"b":2,"c":3}}]
@@ -0,0 +1,6 @@
1
+ - one
2
+ - two
3
+ - three:
4
+ a: 1
5
+ b: 2
6
+ c: 3
@@ -0,0 +1 @@
1
+ {"root":{"nested":[32,64,128,256,512]},"oink":false}
@@ -0,0 +1,8 @@
1
+ root:
2
+ nested:
3
+ - 32
4
+ - 64
5
+ - 128
6
+ - 256
7
+ - 512
8
+ oink: false