structure 0.22.0 → 0.22.1
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.
- data/lib/structure.rb +36 -37
- data/lib/structure/json.rb +22 -1
- data/lib/structure/version.rb +1 -1
- data/test/structure_test.rb +15 -4
- metadata +2 -8
data/lib/structure.rb
CHANGED
@@ -7,7 +7,6 @@
|
|
7
7
|
# end
|
8
8
|
#
|
9
9
|
class Structure
|
10
|
-
# Summon a Basic Object.
|
11
10
|
unless defined? BasicObject
|
12
11
|
if defined? BlankSlate
|
13
12
|
BasicObject = BlankSlate
|
@@ -22,12 +21,12 @@ class Structure
|
|
22
21
|
|
23
22
|
# A wrapper for lazy-evaluating undeclared classes
|
24
23
|
#
|
25
|
-
# @note
|
24
|
+
# @note Somewhat borrowed from the same-named class in Ohm
|
26
25
|
class Wrapper < BasicObject
|
27
26
|
# Wraps specified class in a wrapper if it is not already wrapped
|
28
27
|
#
|
29
|
-
# @param [Class] klass
|
30
|
-
# @return [Wrapper]
|
28
|
+
# @param [Class] klass a class, which may already be wrapped
|
29
|
+
# @return [Wrapper] the wrapped class
|
31
30
|
def self.wrap(klass)
|
32
31
|
klass.class == self ? klass : new(klass.to_s)
|
33
32
|
end
|
@@ -53,8 +52,6 @@ class Structure
|
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
|
-
private
|
57
|
-
|
58
55
|
def method_missing(mth, *args, &block)
|
59
56
|
@unwrapped ? super : @unwrapped = true
|
60
57
|
::Kernel.const_get(@name).send(mth, *args, &block)
|
@@ -71,7 +68,7 @@ class Structure
|
|
71
68
|
# @param [Object] default an optional default value
|
72
69
|
def initialize(type, default = nil)
|
73
70
|
@wrapper = Wrapper.wrap(type)
|
74
|
-
@default = default
|
71
|
+
@default = typecast(default)
|
75
72
|
end
|
76
73
|
|
77
74
|
# @return the default value for the key
|
@@ -88,7 +85,9 @@ class Structure
|
|
88
85
|
# @raise [TypeError] value isn't a type
|
89
86
|
# @return [Object] a typecast value
|
90
87
|
def typecast(val)
|
91
|
-
if val.nil?
|
88
|
+
if val.nil?
|
89
|
+
nil
|
90
|
+
elsif val.is_a?(type)
|
92
91
|
val.dup rescue val
|
93
92
|
elsif Kernel.respond_to?(type.to_s)
|
94
93
|
Kernel.send(type.to_s, val)
|
@@ -113,32 +112,17 @@ class Structure
|
|
113
112
|
# @param [Object] default an optional default value
|
114
113
|
# @raise [NameError] name is already taken
|
115
114
|
def key(name, type = String, default = nil)
|
116
|
-
name = name.to_sym
|
117
|
-
|
118
|
-
if method_defined?(name)
|
115
|
+
if method_defined?(name = name.to_sym)
|
119
116
|
raise NameError, "#{name} is taken"
|
120
117
|
end
|
121
118
|
|
122
|
-
if default && !default.is_a?(type)
|
123
|
-
raise TypeError, "#{default} isn't a #{type}"
|
124
|
-
end
|
125
|
-
|
126
|
-
# Add key to blueprint.
|
127
119
|
blueprint[name] = Definition.new(type, default)
|
128
|
-
|
129
|
-
# Define getter.
|
130
|
-
define_method(name) do
|
131
|
-
@attributes[name]
|
132
|
-
end
|
133
|
-
|
134
|
-
# Define setter.
|
120
|
+
define_method(name) { @attributes[name] }
|
135
121
|
define_method("#{name}=") do |val|
|
136
|
-
|
122
|
+
modifiable[name] = blueprint[name].typecast(val)
|
137
123
|
end
|
138
124
|
end
|
139
125
|
|
140
|
-
private
|
141
|
-
|
142
126
|
def const_missing(name)
|
143
127
|
Wrapper.new(name)
|
144
128
|
end
|
@@ -158,18 +142,19 @@ class Structure
|
|
158
142
|
|
159
143
|
# @return [Hash] a hash representation of the structure
|
160
144
|
def to_hash
|
161
|
-
@attributes.inject({})
|
162
|
-
|
163
|
-
v.to_hash
|
164
|
-
elsif v.is_a?(Array)
|
165
|
-
v.map { |e| e.respond_to?(:to_hash) ? e.to_hash : e }
|
166
|
-
else
|
167
|
-
v
|
168
|
-
end
|
145
|
+
@attributes.inject({}) { |a, (k, v)| a.merge k => hashify(v) }
|
146
|
+
end
|
169
147
|
|
170
|
-
|
148
|
+
def hashify(obj)
|
149
|
+
if obj.respond_to? :to_hash
|
150
|
+
obj.to_hash
|
151
|
+
elsif obj.is_a? Array
|
152
|
+
obj.map { |e| hashify(e) }
|
153
|
+
else
|
154
|
+
obj
|
171
155
|
end
|
172
156
|
end
|
157
|
+
private :hashify
|
173
158
|
|
174
159
|
# Compares this object with another object for equality
|
175
160
|
#
|
@@ -182,9 +167,23 @@ class Structure
|
|
182
167
|
other.is_a?(self.class) && attributes == other.attributes
|
183
168
|
end
|
184
169
|
|
185
|
-
private
|
186
|
-
|
187
170
|
def blueprint
|
188
171
|
self.class.blueprint
|
189
172
|
end
|
173
|
+
private :blueprint
|
174
|
+
|
175
|
+
# Used internally to check if the structure is frozen or not before
|
176
|
+
# updating a value
|
177
|
+
#
|
178
|
+
# @note Borrowed from OpenStruct
|
179
|
+
def modifiable
|
180
|
+
begin
|
181
|
+
@modifiable = true
|
182
|
+
rescue
|
183
|
+
raise TypeError, "can't modify frozen #{self.class}", caller(3)
|
184
|
+
end
|
185
|
+
|
186
|
+
@attributes
|
187
|
+
end
|
188
|
+
private :modifiable
|
190
189
|
end
|
data/lib/structure/json.rb
CHANGED
@@ -5,6 +5,27 @@ rescue NameError
|
|
5
5
|
end
|
6
6
|
|
7
7
|
class Structure
|
8
|
+
# JSON methods for a structure
|
9
|
+
#
|
10
|
+
# Include this in your structure if you need to cast it to JSON and
|
11
|
+
# vice versa.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# class Point < Structure
|
15
|
+
# include JSON
|
16
|
+
#
|
17
|
+
# key :x, Integer
|
18
|
+
# key :y, Integer
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Alternatively, include it in the parent class if you have more than
|
22
|
+
# one structure.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# class Structure
|
26
|
+
# include JSON
|
27
|
+
# end
|
28
|
+
#
|
8
29
|
module JSON
|
9
30
|
def self.included(base)
|
10
31
|
base.extend ClassMethods
|
@@ -46,7 +67,7 @@ class Structure
|
|
46
67
|
module ClassMethods
|
47
68
|
# Builds a structure out of its JSON representation
|
48
69
|
#
|
49
|
-
# @param [Hash] hsh a
|
70
|
+
# @param [Hash] hsh a hash representation of a JSON
|
50
71
|
# @return [Structure] a structure
|
51
72
|
def json_create(hsh)
|
52
73
|
hsh.delete('json_class')
|
data/lib/structure/version.rb
CHANGED
data/test/structure_test.rb
CHANGED
@@ -70,13 +70,24 @@ class TestStructure < MiniTest::Unit::TestCase
|
|
70
70
|
assert_equal 'John', other.friends.first.name
|
71
71
|
end
|
72
72
|
|
73
|
+
def test_cant_change_sex_when_frozen
|
74
|
+
person = Person.new(:name => 'John')
|
75
|
+
person.freeze
|
76
|
+
assert_raises(TypeError) { person.name = 'Jane' }
|
77
|
+
end
|
78
|
+
|
73
79
|
def test_to_hash
|
74
80
|
person = Person.new(:name => 'John')
|
75
|
-
|
76
|
-
|
81
|
+
friend = Person.new(:name => 'Jane')
|
82
|
+
person.friends << friend
|
83
|
+
hsh = person.to_hash
|
84
|
+
|
85
|
+
assert_equal person.name, hsh[:name]
|
86
|
+
assert_equal friend.name, hsh[:friends].first[:name]
|
77
87
|
|
78
|
-
|
79
|
-
|
88
|
+
person.friends = [[friend]]
|
89
|
+
hsh = person.to_hash
|
90
|
+
assert_equal friend.name, hsh[:friends].first.first[:name]
|
80
91
|
end
|
81
92
|
|
82
93
|
def test_json
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: structure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.22.
|
4
|
+
version: 0.22.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-27 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Structure is a typed key/value container.
|
15
15
|
email:
|
@@ -41,18 +41,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
41
|
- - ! '>='
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '0'
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
hash: 2031698207987898845
|
47
44
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
45
|
none: false
|
49
46
|
requirements:
|
50
47
|
- - ! '>='
|
51
48
|
- !ruby/object:Gem::Version
|
52
49
|
version: '0'
|
53
|
-
segments:
|
54
|
-
- 0
|
55
|
-
hash: 2031698207987898845
|
56
50
|
requirements: []
|
57
51
|
rubyforge_project: structure
|
58
52
|
rubygems_version: 1.8.10
|