blobject 0.3.3 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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