blobject 0.3.3 → 0.3.7

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.
@@ -0,0 +1,172 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html>
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+
7
+ <link rel="stylesheet" href="css/full_list.css" type="text/css" media="screen" charset="utf-8" />
8
+
9
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
10
+
11
+
12
+
13
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
14
+
15
+ <script type="text/javascript" charset="utf-8" src="js/full_list.js"></script>
16
+
17
+
18
+ <base id="base_target" target="_parent" />
19
+ </head>
20
+ <body>
21
+ <script type="text/javascript" charset="utf-8">
22
+ if (window.top.frames.main) {
23
+ document.getElementById('base_target').target = 'main';
24
+ document.body.className = 'frames';
25
+ }
26
+ </script>
27
+ <div id="content">
28
+ <h1 id="full_list_header">Method List</h1>
29
+ <div id="nav">
30
+
31
+ <span><a target="_self" href="class_list.html">
32
+ Classes
33
+ </a></span>
34
+
35
+ <span><a target="_self" href="method_list.html">
36
+ Methods
37
+ </a></span>
38
+
39
+ <span><a target="_self" href="file_list.html">
40
+ Files
41
+ </a></span>
42
+
43
+ </div>
44
+ <div id="search">Search: <input type="text" /></div>
45
+
46
+ <ul id="full_list" class="method">
47
+
48
+
49
+ <li class="r1 ">
50
+ <span class='object_link'><a href="Blobject.html#%3D%3D-instance_method" title="Blobject#== (method)">#==</a></span>
51
+
52
+ <small>Blobject</small>
53
+
54
+ </li>
55
+
56
+
57
+ <li class="r2 ">
58
+ <span class='object_link'><a href="Blobject.html#%5B%5D-instance_method" title="Blobject#[] (method)">#[]</a></span>
59
+
60
+ <small>Blobject</small>
61
+
62
+ </li>
63
+
64
+
65
+ <li class="r1 ">
66
+ <span class='object_link'><a href="Blobject.html#%5B%5D%3D-instance_method" title="Blobject#[]= (method)">#[]=</a></span>
67
+
68
+ <small>Blobject</small>
69
+
70
+ </li>
71
+
72
+
73
+ <li class="r2 ">
74
+ <span class='object_link'><a href="Blobject.html#as_json-instance_method" title="Blobject#as_json (method)">#as_json</a></span>
75
+
76
+ <small>Blobject</small>
77
+
78
+ </li>
79
+
80
+
81
+ <li class="r1 ">
82
+ <span class='object_link'><a href="Blobject.html#freeze-instance_method" title="Blobject#freeze (method)">#freeze</a></span>
83
+
84
+ <small>Blobject</small>
85
+
86
+ </li>
87
+
88
+
89
+ <li class="r2 ">
90
+ <span class='object_link'><a href="Blobject.html#from_json-class_method" title="Blobject.from_json (method)">from_json</a></span>
91
+
92
+ <small>Blobject</small>
93
+
94
+ </li>
95
+
96
+
97
+ <li class="r1 ">
98
+ <span class='object_link'><a href="Blobject.html#from_yaml-class_method" title="Blobject.from_yaml (method)">from_yaml</a></span>
99
+
100
+ <small>Blobject</small>
101
+
102
+ </li>
103
+
104
+
105
+ <li class="r2 ">
106
+ <span class='object_link'><a href="Blobject.html#hash-instance_method" title="Blobject#hash (method)">#hash</a></span>
107
+
108
+ <small>Blobject</small>
109
+
110
+ </li>
111
+
112
+
113
+ <li class="r1 ">
114
+ <span class='object_link'><a href="Blobject.html#initialize-instance_method" title="Blobject#initialize (method)">#initialize</a></span>
115
+
116
+ <small>Blobject</small>
117
+
118
+ </li>
119
+
120
+
121
+ <li class="r2 ">
122
+ <span class='object_link'><a href="Blobject.html#inspect-instance_method" title="Blobject#inspect (method)">#inspect</a></span>
123
+
124
+ <small>Blobject</small>
125
+
126
+ </li>
127
+
128
+
129
+ <li class="r1 ">
130
+ <span class='object_link'><a href="Blobject.html#method_missing-instance_method" title="Blobject#method_missing (method)">#method_missing</a></span>
131
+
132
+ <small>Blobject</small>
133
+
134
+ </li>
135
+
136
+
137
+ <li class="r2 ">
138
+ <span class='object_link'><a href="Blobject.html#respond_to%3F-instance_method" title="Blobject#respond_to? (method)">#respond_to?</a></span>
139
+
140
+ <small>Blobject</small>
141
+
142
+ </li>
143
+
144
+
145
+ <li class="r1 ">
146
+ <span class='object_link'><a href="Blobject.html#to_hash-instance_method" title="Blobject#to_hash (method)">#to_hash</a></span>
147
+
148
+ <small>Blobject</small>
149
+
150
+ </li>
151
+
152
+
153
+ <li class="r2 ">
154
+ <span class='object_link'><a href="Blobject.html#to_json-instance_method" title="Blobject#to_json (method)">#to_json</a></span>
155
+
156
+ <small>Blobject</small>
157
+
158
+ </li>
159
+
160
+
161
+ <li class="r1 ">
162
+ <span class='object_link'><a href="Blobject.html#to_yaml-instance_method" title="Blobject#to_yaml (method)">#to_yaml</a></span>
163
+
164
+ <small>Blobject</small>
165
+
166
+ </li>
167
+
168
+
169
+ </ul>
170
+ </div>
171
+ </body>
172
+ </html>
@@ -0,0 +1,112 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.8.1
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
19
+ relpath = '';
20
+ framesUrl = "frames.html#!" + escape(window.location.href);
21
+ </script>
22
+
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
25
+
26
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
27
+
28
+
29
+ </head>
30
+ <body>
31
+ <div id="header">
32
+ <div id="menu">
33
+
34
+ <a href="_index.html">Index</a> &raquo;
35
+
36
+
37
+ <span class="title">Top Level Namespace</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a class="full_list_link" id="class_list_link"
46
+ href="class_list.html">
47
+ Class List
48
+ </a>
49
+
50
+ <a class="full_list_link" id="method_list_link"
51
+ href="method_list.html">
52
+ Method List
53
+ </a>
54
+
55
+ <a class="full_list_link" id="file_list_link"
56
+ href="file_list.html">
57
+ File List
58
+ </a>
59
+
60
+ </div>
61
+ <div class="clear"></div>
62
+ </div>
63
+
64
+ <iframe id="search_frame"></iframe>
65
+
66
+ <div id="content"><h1>Top Level Namespace
67
+
68
+
69
+
70
+ </h1>
71
+
72
+ <dl class="box">
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ </dl>
82
+ <div class="clear"></div>
83
+
84
+ <h2>Defined Under Namespace</h2>
85
+ <p class="children">
86
+
87
+
88
+
89
+
90
+ <strong class="classes">Classes:</strong> <span class='object_link'><a href="Blobject.html" title="Blobject (class)">Blobject</a></span>
91
+
92
+
93
+ </p>
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ </div>
104
+
105
+ <div id="footer">
106
+ Generated on Thu Jun 7 00:25:31 2012 by
107
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
+ 0.8.1 (ruby-1.9.3).
109
+ </div>
110
+
111
+ </body>
112
+ </html>
@@ -1,3 +1,3 @@
1
1
  class Blobject
2
- VERSION = '0.3.3'
3
- end
2
+ VERSION = '0.3.7'
3
+ end
data/lib/blobject.rb CHANGED
@@ -2,49 +2,52 @@ require 'json'
2
2
  require 'yaml'
3
3
  require_relative 'blobject/version'
4
4
 
5
+ # Wraps a hash to provide arbitrarily nested object-style attribute access
5
6
  class Blobject
6
7
 
7
8
  # filter :to_ary else Blobject#to_ary returns a
8
9
  # blobject which is not cool, especially if you are puts.
9
10
  ProhibitedNames = [:to_ary]
10
11
 
11
- module Error; end
12
-
12
+ # pass an optional hash of values to preload
13
+ # you can also pass a block, the new Blobject will be yield
13
14
  def initialize hash = {}
14
15
 
15
- @hash = hash
16
+ @hash = Hash.new
16
17
 
17
- @hash.keys.each do |key|
18
- unless key.class <= Symbol
19
- value = @hash.delete key
20
- key = key.to_sym
21
- @hash[key] = value
22
- end
18
+ hash.each do |key, value|
19
+ key = key.to_sym unless key.is_a? Symbol
20
+ @hash[key] = value
23
21
  end
24
22
 
25
- __visit_subtree__ do |name, node|
26
- if node.class <= Hash
27
- @hash[name] = Blobject.new node
28
- end
23
+ @hash.each do |name, node|
24
+ @hash[name] = self.class.send(:__blobjectify__, node)
29
25
  end
30
26
 
31
27
  yield self if block_given?
32
28
  end
33
29
 
30
+ def empty?
31
+ @hash.empty?
32
+ end
33
+
34
+ # delegates to the internal Hash
34
35
  def inspect
35
36
 
36
37
  @hash.inspect
37
38
  end
38
39
 
40
+ # access the internal hash. be careful, this is _not_ a copy
39
41
  def hash
40
42
 
41
43
  @hash
42
44
  end
43
45
 
46
+ # creates a recursive copy of the internal hash
44
47
  def to_hash
45
48
 
46
49
  h = hash.dup
47
- __visit_subtree__ do |name, node|
50
+ @hash.each do |name, node|
48
51
  h[name] = node.to_hash if node.respond_to? :to_hash
49
52
  end
50
53
  h
@@ -60,12 +63,13 @@ class Blobject
60
63
  # assignment in conditionals is usually a bad smell, here it helps minimize regex matching
61
64
  when (name = method[/^\w+$/, 0]) && params.length == 0
62
65
  # the call is an attribute reader
63
- return nil if frozen? and not @hash.has_key?(method)
64
-
66
+
67
+ return self.class.new.freeze if frozen? and not @hash.has_key?(method)
65
68
  self.class.send :__define_attribute__, name
66
69
 
67
70
  return send(method) if @hash.has_key? method
68
71
 
72
+ # close the scope for storing call chain
69
73
  parent = self
70
74
  nested_blobject = self.class.new
71
75
 
@@ -106,85 +110,77 @@ class Blobject
106
110
  end
107
111
  end
108
112
 
113
+ # compares Blobjects to Blobjects or Hashes
109
114
  def == other
110
115
  return @hash == other.hash if other.class <= Blobject
111
116
  return @hash == other if other.class <= Hash
112
117
  super
113
118
  end
114
119
 
120
+ # hash-like access to the Blobject's attributes
115
121
  def [] name
116
122
 
117
123
  send name
118
124
  end
119
125
 
126
+ # hash-like attribtue setter
120
127
  def []= name, value
121
128
 
122
129
  send "#{name.to_s}=", value
123
130
  end
124
131
 
132
+ # freeze a Blobject to prevent it being modified
125
133
  def freeze
126
- __visit_subtree__ { |name, node| node.freeze }
127
134
  @hash.freeze
128
135
  super
129
136
  end
130
137
 
138
+ def freeze_r
139
+ self.class.send(:__freeze_r__, self)
140
+ freeze
141
+ end
142
+
143
+
144
+ # returns a hash which can be serialized as json.
145
+ # this is for use in rails controllers: `render json: blobject`
131
146
  def as_json *args
132
147
  return hash.as_json(*args) if hash.respond_to? :as_json
133
148
  to_hash
134
149
  end
135
150
 
151
+ # serialize the Blobject as a json string
136
152
  def to_json *args
137
153
  as_json.to_json *args
138
154
  end
139
155
 
140
- def as_yaml
141
-
142
- to_hash
143
- end
144
-
156
+ # serialize the Blobject as a yaml string
145
157
  def to_yaml
146
158
 
147
159
  as_yaml.to_yaml
148
160
  end
149
161
 
162
+ # get a Blobject from a json string
163
+ # if the yaml string describes an array, an array will be returned
150
164
  def self.from_json json
151
165
 
152
- from_json!(json).freeze
153
- end
154
-
155
- def self.from_json! json
156
-
157
- __from_hash_or_array__(JSON.parse(json))
166
+ __blobjectify__(JSON.parse(json))
158
167
  end
159
168
 
169
+ # get a Blobject from a yaml string
170
+ # if the yaml string describes an array, an array will be returned
160
171
  def self.from_yaml yaml
161
172
 
162
- from_yaml!(yaml).freeze
163
- end
164
-
165
- def self.from_yaml! yaml
166
-
167
- __from_hash_or_array__(YAML.load(yaml))
173
+ __blobjectify__(YAML.load(yaml))
168
174
  end
169
175
 
170
176
  private
171
177
  # to avoid naming collisions private method names are prefixed and suffix with double unerscores (__)
172
178
 
173
- def __visit_subtree__ &block
174
-
175
- @hash.each do |name, node|
176
-
177
- if node.class <= Array
178
- node.flatten.each do |node_node|
179
- block.call(nil, node_node, &block)
180
- end
181
- end
182
-
183
- block.call name, node, &block
184
- end
185
- end
186
-
179
+ # Used to tag and reraise errors from a Blobject
180
+ # Refer to "Tagging exceptions with modules" on p97 in Exceptional Ruby by Avdi Grimm
187
181
  # errors from this library can be handled with rescue Blobject::Error
182
+ module Error; end
183
+
188
184
  def __tag_and_raise__ e
189
185
  raise e
190
186
  rescue
@@ -196,19 +192,35 @@ private
196
192
 
197
193
  private
198
194
 
199
- def __from_hash_or_array__ hash_or_array
200
-
201
- if hash_or_array.class <= Array
202
- return hash_or_array.map do |e|
203
- if e.class <= Hash
204
- Blobject.new e
205
- else
206
- e
207
- end
195
+ def __freeze_r__ object
196
+
197
+ case object
198
+ when Array
199
+ return object.each do |e|
200
+ e.freeze
201
+ __freeze_r__(e)
208
202
  end
203
+ when Hash
204
+ return object.each do |k, v|
205
+ v.freeze
206
+ __freeze_r__(v)
207
+ end
208
+ when Blobject
209
+ object.freeze
210
+ __freeze_r__ object.hash
211
+ else
212
+ object.freeze
209
213
  end
214
+ end
215
+
216
+ def __blobjectify__ object
210
217
 
211
- Blobject.new hash_or_array
218
+ array = object if object.is_a? Array
219
+ hash = object if object.is_a? Hash
220
+
221
+ return array.map{|a| __blobjectify__(a)} if array
222
+ return Blobject.new(hash) if hash
223
+ return object
212
224
  end
213
225
 
214
226
  def __define_attribute__ name
@@ -222,7 +234,7 @@ private
222
234
  unless methods.include? setter_name
223
235
  self.send :define_method, setter_name do |value|
224
236
  begin
225
- value = self.class.new(value) if value.class <= Hash
237
+ value = self.class.send(:__blobjectify__, value) if value.is_a?(Hash) or value.is_a?(Array)
226
238
  @hash[name] = value
227
239
  rescue ex
228
240
  __tag_and_raise__(ex)
@@ -236,9 +248,9 @@ private
236
248
 
237
249
  value = @hash[name]
238
250
 
239
- if value.nil? && !frozen?
251
+ if value.nil?
240
252
  value = self.class.new
241
- @hash[name] = value
253
+ @hash[name] = value unless frozen?
242
254
  end
243
255
 
244
256
  value
@@ -47,6 +47,43 @@ describe Blobject do
47
47
  assert_equal b.name.surname, "Jones"
48
48
  end
49
49
 
50
+ it 'recursively blobjectifies assigned values' do
51
+ b.data = {name: 'jim', number: 144}
52
+ b.data.must_be_instance_of Blobject
53
+ end
54
+
55
+ it 'recursively blobjectifies assigned arrays' do
56
+ b.data = [{name: 'jim', number: 144}, [{name: 'jam', number: 147}]]
57
+ b.data[0].must_be_instance_of Blobject
58
+ b.data[1][0].must_be_instance_of Blobject
59
+ end
60
+
61
+ it 'can indicate whether it is empty' do
62
+ b = Blobject.new
63
+ b.must_be :empty?
64
+ b.name = "something"
65
+ b.wont_be :empty?
66
+ end
67
+
68
+ describe 'hash-like access' do
69
+
70
+ it 'allows hash-style setters' do
71
+
72
+ b[:foo] = 123
73
+ b['bar'] = 456
74
+
75
+ assert_equal "#{b.foo}#{b.bar}", '123456'
76
+ end
77
+
78
+ it 'allows hash-style getters' do
79
+
80
+ b.name = "Jimmy"
81
+
82
+ assert_equal b[:name] , "Jimmy"
83
+ assert_equal b['name'], "Jimmy"
84
+ end
85
+ end
86
+
50
87
  describe 'respond_to?' do
51
88
 
52
89
  it 'returns true if the blobject has the corresponding member' do
@@ -233,7 +270,14 @@ describe Blobject do
233
270
  end
234
271
  end
235
272
 
236
- describe 'frozen blobject' do
273
+ describe 'freeze' do
274
+ it 'freezes the internal hash' do
275
+ b.freeze
276
+ b.hash.must_be :frozen?
277
+ end
278
+ end
279
+
280
+ describe 'freeze_r' do
237
281
 
238
282
  before :each do
239
283
  list_element = Blobject.new
@@ -242,7 +286,7 @@ describe Blobject do
242
286
  b.data.list = [1, 2, 3, list_element]
243
287
  b.data.inner_hash = {:inner => {:one => 1}}
244
288
 
245
- b.freeze
289
+ b.freeze_r
246
290
  end
247
291
 
248
292
  it 'still provides access' do
@@ -268,10 +312,10 @@ describe Blobject do
268
312
  proc { b.hello = 123 }.must_raise RuntimeError
269
313
  end
270
314
 
271
- it 'returns nil when trying to get an attribute' do
272
- assert b.meow_face.nil?, 'method missing returned something'
315
+ it 'still returns a blobject when trying to get an attribute' do
316
+ b.meow_face.must_be_instance_of Blobject
273
317
  # check again to test memoized method
274
- assert b.meow_face.nil?, 'memoized method returned something'
275
- end
318
+ b.meow_face.must_be_instance_of Blobject
319
+ end
276
320
  end
277
321
  end