ribbon 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ribbon.rb +93 -18
- data/lib/ribbon/version.rb +2 -2
- data/lib/ribbon/wrapper.rb +64 -57
- metadata +5 -5
data/lib/ribbon.rb
CHANGED
@@ -10,10 +10,35 @@ require 'ribbon/wrapper'
|
|
10
10
|
# general-purpose hash, since the <tt>[key]</tt> and <tt>[key] = value</tt>
|
11
11
|
# methods are defined.
|
12
12
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
13
|
+
# Ribbons support cascading references seamlessly. If you access a property that
|
14
|
+
# hasn't been set, a new ribbon is created and returned, allowing you to
|
15
|
+
# continue your calls:
|
16
|
+
#
|
17
|
+
# r = Ribbon.new
|
18
|
+
# r.a.b.c = 10
|
19
|
+
#
|
20
|
+
# Appending a <tt>!</tt> to the end of the property sets the value and returns
|
21
|
+
# the receiver:
|
22
|
+
#
|
23
|
+
# r.x!(10).y!(20).z!(30) # Equivalent to: r.x = 10; r.y = 20; r.z = 30
|
24
|
+
# => {x: 10, y: 20, z: 30}
|
25
|
+
#
|
26
|
+
# Appending a <tt>?</tt> to the end of the property allows you to peek at the
|
27
|
+
# contents of the property without creating a new ribbon if it is missing:
|
28
|
+
#
|
29
|
+
# r.p?
|
30
|
+
# => nil
|
31
|
+
#
|
32
|
+
# Seamless reference cascade using arbitrary keys are also supported via the
|
33
|
+
# <tt>[key]</tt> and <tt>[key] = value</tt> operators, which allow you to
|
34
|
+
# directly manipulate the internal hash:
|
35
|
+
#
|
36
|
+
# r[:j][:k][:l]
|
37
|
+
#
|
38
|
+
# Keep in mind that the <tt>[key]</tt> operator will always create new ribbons
|
39
|
+
# for missing properties, which is something that may not be desirable; consider
|
40
|
+
# wrapping the ribbon with a Ribbon::Wrapper in order to have better access to
|
41
|
+
# the underlying hash.
|
17
42
|
class Ribbon < BasicObject
|
18
43
|
|
19
44
|
# The internal Hash.
|
@@ -21,7 +46,7 @@ class Ribbon < BasicObject
|
|
21
46
|
@hash ||= {}
|
22
47
|
end
|
23
48
|
|
24
|
-
# Initializes the new
|
49
|
+
# Initializes the new ribbon, merging the internal hash with the given one and
|
25
50
|
# converting all internal objects. See Ribbon::convert_all! for details.
|
26
51
|
def initialize(hash = {}, &block)
|
27
52
|
__hash__.merge! hash, &block
|
@@ -44,23 +69,25 @@ class Ribbon < BasicObject
|
|
44
69
|
|
45
70
|
# Handles the following cases:
|
46
71
|
#
|
47
|
-
# ribbon.method = value => ribbon[method] = value
|
48
|
-
# ribbon.method! value => ribbon[method] = value
|
49
|
-
# ribbon.method? => ribbon[method] ? true : false
|
50
72
|
# ribbon.method => ribbon[method]
|
73
|
+
# ribbon.method = value => ribbon[method] = value
|
74
|
+
# ribbon.method! value => ribbon[method] = value; self
|
75
|
+
# ribbon.method? => ribbon.__hash__[method]
|
51
76
|
def method_missing(method, *args, &block)
|
52
|
-
m = method.to_s.strip.
|
77
|
+
m = method.to_s.strip.gsub(/[=?!]$/, '').strip.to_sym
|
53
78
|
case method.to_s[-1]
|
54
|
-
when '='
|
79
|
+
when '='
|
55
80
|
self[m] = args.first
|
81
|
+
when '!'
|
82
|
+
self[m] = args.first; self
|
56
83
|
when '?'
|
57
|
-
__hash__[m]
|
84
|
+
self.__hash__[m]
|
58
85
|
else
|
59
86
|
self[method]
|
60
87
|
end
|
61
88
|
end
|
62
89
|
|
63
|
-
# If <tt>object</tt> is a
|
90
|
+
# If <tt>object</tt> is a hash, converts it to a ribbon. If it is an array,
|
64
91
|
# converts any hashes inside.
|
65
92
|
def self.convert(object)
|
66
93
|
case object
|
@@ -70,7 +97,7 @@ class Ribbon < BasicObject
|
|
70
97
|
end
|
71
98
|
end
|
72
99
|
|
73
|
-
# Converts all values in the given
|
100
|
+
# Converts all values in the given ribbon.
|
74
101
|
def self.convert_all!(ribbon)
|
75
102
|
ribbon.__hash__.each do |key, value|
|
76
103
|
ribbon[key] = case value
|
@@ -81,22 +108,51 @@ class Ribbon < BasicObject
|
|
81
108
|
ribbon
|
82
109
|
end
|
83
110
|
|
84
|
-
#
|
111
|
+
# Computes a simple key: value string for easy visualization of this ribbon.
|
112
|
+
#
|
113
|
+
# In +opts+ can be specified several options that customize how the string
|
114
|
+
# is generated. Among those options:
|
115
|
+
#
|
116
|
+
# [:separator] Used to separate a key/value pair. Default is <tt>': '</tt>.
|
117
|
+
# [:key] Symbol that will be sent to the key in order to obtain its
|
118
|
+
# string representation. Defaults to <tt>:to_s</tt>.
|
119
|
+
# [:value] Symbol that will be sent to the value in order to obtain its
|
120
|
+
# string representation. Defaults to <tt>:inspect</tt>.
|
121
|
+
def to_s(opts = {})
|
122
|
+
to_s_recursive opts
|
123
|
+
end
|
124
|
+
|
125
|
+
# Same as #to_s.
|
126
|
+
alias inspect to_s
|
127
|
+
|
128
|
+
# Merges the hash of +new+ with the hash of +old+, creating a new ribbon in
|
129
|
+
# the process.
|
130
|
+
def self.merge(old, new, &block)
|
131
|
+
new extract_hash_from(old).merge(extract_hash_from(ribbon), &block)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Merges the hash of +new+ with the hash of +old+, modifying +old+'s hash in
|
135
|
+
# the process.
|
136
|
+
def self.merge!(old, new, &block)
|
137
|
+
extract_hash_from(old).merge! extract_hash_from(ribbon), &block
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns +true+ if the given +object+ is a ribbon.
|
85
141
|
def self.instance?(object)
|
86
142
|
self === object
|
87
143
|
end
|
88
144
|
|
89
|
-
# Returns +true+ if the given
|
145
|
+
# Returns +true+ if the given ribbon is wrapped.
|
90
146
|
def self.wrapped?(ribbon)
|
91
147
|
Wrapper === ribbon
|
92
148
|
end
|
93
149
|
|
94
|
-
# Wraps a
|
150
|
+
# Wraps a ribbon instance in a Ribbon::Wrapper.
|
95
151
|
def self.wrap(ribbon)
|
96
152
|
Wrapper.new ribbon
|
97
153
|
end
|
98
154
|
|
99
|
-
# Unwraps the +ribbon+ if it is wrapped and returns its hash. Returns nil in
|
155
|
+
# Unwraps the +ribbon+ if it is wrapped and returns its hash. Returns +nil+ in
|
100
156
|
# any other case.
|
101
157
|
def self.extract_hash_from(ribbon)
|
102
158
|
ribbon = ribbon.ribbon if ::Ribbon.wrapped? ribbon
|
@@ -105,11 +161,30 @@ class Ribbon < BasicObject
|
|
105
161
|
|
106
162
|
class << self
|
107
163
|
|
108
|
-
# Wraps a
|
164
|
+
# Wraps a ribbon instance in a Ribbon::Wrapper.
|
109
165
|
#
|
110
166
|
# Ribbon[ribbon].keys
|
111
167
|
alias [] wrap
|
112
168
|
|
113
169
|
end
|
114
170
|
|
171
|
+
private
|
172
|
+
|
173
|
+
# Computes a string value recursively for the given ribbon and all ribbons
|
174
|
+
# inside it. This implementation avoids creating additional ribbon or
|
175
|
+
# Ribbon::Wrapper objects.
|
176
|
+
def to_s_recursive(opts, ribbon = self)
|
177
|
+
ksym = opts.fetch(:key, :to_s).to_sym
|
178
|
+
vsym = opts.fetch(:value, :inspect).to_sym
|
179
|
+
separator = opts.fetch(:separator, ': ').to_s
|
180
|
+
values = ribbon.__hash__.map do |k, v|
|
181
|
+
k = k.ribbon if ::Ribbon.wrapped? k
|
182
|
+
v = v.ribbon if ::Ribbon.wrapped? v
|
183
|
+
k = if ::Ribbon.instance? k then to_s_recursive opts, k else k.send ksym end
|
184
|
+
v = if ::Ribbon.instance? v then to_s_recursive opts, v else v.send vsym end
|
185
|
+
"#{k}#{separator}#{v}"
|
186
|
+
end.join ', '
|
187
|
+
"{#{values}}"
|
188
|
+
end
|
189
|
+
|
115
190
|
end
|
data/lib/ribbon/version.rb
CHANGED
@@ -11,12 +11,12 @@ class Ribbon < BasicObject
|
|
11
11
|
# Minor version.
|
12
12
|
#
|
13
13
|
# Increments denote backward-compatible changes and additions.
|
14
|
-
MINOR =
|
14
|
+
MINOR = 3
|
15
15
|
|
16
16
|
# Patch version.
|
17
17
|
#
|
18
18
|
# Increments denote changes in implementation.
|
19
|
-
PATCH =
|
19
|
+
PATCH = 0
|
20
20
|
|
21
21
|
# Build version.
|
22
22
|
#
|
data/lib/ribbon/wrapper.rb
CHANGED
@@ -5,18 +5,37 @@ class Ribbon < BasicObject
|
|
5
5
|
# Wraps around a Ribbon in order to provide general-purpose methods.
|
6
6
|
#
|
7
7
|
# Ribbons are designed to use methods as hash keys. In order to maximize
|
8
|
-
# possibilities, many useful methods were left out of the
|
8
|
+
# possibilities, many useful methods were left out of the ribbon class and
|
9
9
|
# implemented in this wrapper class instead.
|
10
10
|
#
|
11
|
-
#
|
11
|
+
# This class enables you to use ribbons like an ordinary hash. Any undefined
|
12
|
+
# methods called on a wrapped ribbon will be sent to its hash, or to the
|
13
|
+
# ribbon itself if the hash doesn't respond to the method.
|
12
14
|
#
|
13
15
|
# r = Ribbon.new
|
14
|
-
# Ribbon
|
16
|
+
# w = Ribbon::Wrapper.new r
|
15
17
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
+
# w.a.b.c
|
19
|
+
# w[:a][:b][:c]
|
20
|
+
#
|
21
|
+
# Wrapped ribbons talk directly to their ribbon's hash:
|
22
|
+
#
|
23
|
+
# w[:k]
|
24
|
+
# => nil
|
25
|
+
#
|
26
|
+
# However, keep in mind that the wrapped hash may contain other ribbons,
|
27
|
+
# which may not be wrapped:
|
28
|
+
#
|
29
|
+
# w.a.b.c[:d]
|
30
|
+
# => {}
|
31
|
+
#
|
32
|
+
# You can automatically wrap and unwrap all ribbons inside the wrapped one:
|
33
|
+
#
|
34
|
+
# w.wrap_all!
|
35
|
+
# w.unwrap_all!
|
36
|
+
#
|
37
|
+
# The wrapped ribbon receives all undefined methods that hashes won't take:
|
18
38
|
#
|
19
|
-
# w = Ribbon[r]
|
20
39
|
# w.x = 10
|
21
40
|
# w.ribbon.x
|
22
41
|
# => 10
|
@@ -52,7 +71,7 @@ class Ribbon < BasicObject
|
|
52
71
|
self.ribbon = ribbon
|
53
72
|
end
|
54
73
|
|
55
|
-
# Returns the hash of the wrapped
|
74
|
+
# Returns the hash of the wrapped ribbon.
|
56
75
|
def hash
|
57
76
|
ribbon.__hash__
|
58
77
|
end
|
@@ -64,28 +83,22 @@ class Ribbon < BasicObject
|
|
64
83
|
else ribbon end.__send__ method, *args, &block
|
65
84
|
end
|
66
85
|
|
67
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
# This method returns a new hash.
|
71
|
-
def merge(ribbon, &block)
|
72
|
-
hash.merge Ribbon.extract_hash_from(ribbon), &block
|
86
|
+
# Wraps all ribbons contained by this wrapper's ribbon.
|
87
|
+
def wrap_all!
|
88
|
+
wrap_all_recursive!
|
73
89
|
end
|
74
90
|
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
# This method modifies the hash of this wrapped Ribbon.
|
79
|
-
def merge!(ribbon, &block)
|
80
|
-
hash.merge! Ribbon.extract_hash_from(ribbon), &block
|
91
|
+
# Unwraps all ribbons contained by this wrapper's ribbon.
|
92
|
+
def unwrap_all!
|
93
|
+
unwrap_all_recursive!
|
81
94
|
end
|
82
95
|
|
83
|
-
# Converts the wrapped Ribbon and all
|
96
|
+
# Converts the wrapped Ribbon and all ribbons inside into hashes.
|
84
97
|
def to_hash
|
85
98
|
to_hash_recursive
|
86
99
|
end
|
87
100
|
|
88
|
-
# Converts the wrapped
|
101
|
+
# Converts the wrapped ribbon to a hash and serializes it with YAML. To get
|
89
102
|
# a Ribbon back from the serialized hash, you can simply load the hash and
|
90
103
|
# pass it to the Ribbon constructor:
|
91
104
|
#
|
@@ -94,46 +107,16 @@ class Ribbon < BasicObject
|
|
94
107
|
to_hash.to_yaml
|
95
108
|
end
|
96
109
|
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
# In +opts+ can be specified several options that customize how the string
|
101
|
-
# is generated. Among those options:
|
102
|
-
#
|
103
|
-
# [:separator] Used to separate a key/value pair. Default is <tt>': '</tt>.
|
104
|
-
# [:key] Symbol that will be sent to the key in order to obtain its
|
105
|
-
# string representation. Defaults to <tt>:to_s</tt>.
|
106
|
-
# [:value] Symbol that will be sent to the value in order to obtain its
|
107
|
-
# string representation. Defaults to <tt>:inspect</tt>.
|
108
|
-
def to_s(opts = {})
|
109
|
-
to_s_recursive opts, ribbon
|
110
|
+
# Delegates to Ribbon#to_s.
|
111
|
+
def to_s
|
112
|
+
ribbon.to_s
|
110
113
|
end
|
111
114
|
|
112
|
-
# Same as #to_s.
|
113
|
-
alias :inspect :to_s
|
114
|
-
|
115
115
|
private
|
116
116
|
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
def to_s_recursive(opts, ribbon)
|
121
|
-
ksym = opts.fetch(:key, :to_s).to_sym
|
122
|
-
vsym = opts.fetch(:value, :inspect).to_sym
|
123
|
-
separator = opts.fetch(:separator, ': ').to_s
|
124
|
-
values = ribbon.__hash__.map do |k, v|
|
125
|
-
k = k.ribbon if Ribbon.wrapped? k
|
126
|
-
v = v.ribbon if Ribbon.wrapped? v
|
127
|
-
k = if Ribbon.instance? k then to_s_recursive opts, k else k.send ksym end
|
128
|
-
v = if Ribbon.instance? v then to_s_recursive opts, v else v.send vsym end
|
129
|
-
"#{k}#{separator}#{v}"
|
130
|
-
end.join ', '
|
131
|
-
"{#{values}}"
|
132
|
-
end
|
133
|
-
|
134
|
-
# Converts the wrapped Ribbon and all Ribbons inside into hashes using
|
135
|
-
# recursion. This implementation avoids the creation of additional Ribbon or
|
136
|
-
# Ribbon::Wrapper objects.
|
117
|
+
# Converts the wrapped ribbon and all ribbons inside into hashes using
|
118
|
+
# recursion. This implementation avoids the creation of additional ribbon or
|
119
|
+
# wrapper objects.
|
137
120
|
def to_hash_recursive(ribbon = self.ribbon)
|
138
121
|
{}.tap do |hash|
|
139
122
|
ribbon.__hash__.each do |key, value|
|
@@ -146,5 +129,29 @@ class Ribbon < BasicObject
|
|
146
129
|
end
|
147
130
|
end
|
148
131
|
|
132
|
+
# Recursively wraps all ribbons inside. This implementation avoids the
|
133
|
+
# creation of additional ribbon or wrapper objects.
|
134
|
+
def wrap_all_recursive!(wrapper = self)
|
135
|
+
wrapper.hash.each do |key, value|
|
136
|
+
wrapper.hash[key] = case value
|
137
|
+
when ::Ribbon then wrap_all_recursive! ::Ribbon::Wrapper[value]
|
138
|
+
else value
|
139
|
+
end
|
140
|
+
end
|
141
|
+
wrapper
|
142
|
+
end
|
143
|
+
|
144
|
+
# Recursively unwraps all ribbons inside. This implementation avoids the
|
145
|
+
# creation of additional ribbon or wrapper objects.
|
146
|
+
def unwrap_all_recursive!(ribbon = self)
|
147
|
+
ribbon.__hash__.each do |key, value|
|
148
|
+
ribbon[key] = case value
|
149
|
+
when ::Ribbon::Wrapper then unwrap_all_recursive! value.ribbon
|
150
|
+
else value
|
151
|
+
end
|
152
|
+
end
|
153
|
+
ribbon
|
154
|
+
end
|
155
|
+
|
149
156
|
end
|
150
157
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ribbon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2011-12-30 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rookie
|
16
|
-
requirement: &
|
16
|
+
requirement: &15659380 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *15659380
|
25
25
|
description: Ruby Object Notation. Inspired by JSON and OpenStruct.
|
26
26
|
email: matheus.a.m.moreira@gmail.com
|
27
27
|
executables: []
|
@@ -54,7 +54,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
54
|
version: '0'
|
55
55
|
segments:
|
56
56
|
- 0
|
57
|
-
hash:
|
57
|
+
hash: 2946347091620539582
|
58
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
59
|
none: false
|
60
60
|
requirements:
|
@@ -63,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
63
|
version: '0'
|
64
64
|
segments:
|
65
65
|
- 0
|
66
|
-
hash:
|
66
|
+
hash: 2946347091620539582
|
67
67
|
requirements: []
|
68
68
|
rubyforge_project:
|
69
69
|
rubygems_version: 1.8.10
|