ribbon 0.2.4 → 0.3.0
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/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
|