ribbon 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ class Ribbon < BasicObject
2
+
3
+ # Extensions to the standard library.
4
+ #
5
+ # @author Matheus Afonso Martins Moreira
6
+ # @since 0.6.0
7
+ module CoreExtensions; end
8
+
9
+ end
10
+
11
+ %w(array basic_object hash object).each do |file|
12
+ require file.prepend 'ribbon/core_extensions/'
13
+ end
@@ -1,13 +1,18 @@
1
1
  require 'ribbon'
2
2
 
3
3
  class Ribbon < BasicObject
4
- module CoreExt
4
+ module CoreExtensions
5
5
 
6
6
  # Methods to work with ribbons in arrays.
7
+ #
8
+ # @author Matheus Afonso Martins Moreira
9
+ # @since 0.6.0
7
10
  module Array
8
11
 
9
12
  # If the last argument is a hash, removes and converts it to a ribbon,
10
13
  # otherwise returns an empty ribbon.
14
+ #
15
+ # @return [Ribbon] the Ribbon at the end of this array
11
16
  def extract_ribbon!
12
17
  case last
13
18
  when Hash then Ribbon.new pop
@@ -19,19 +24,26 @@ class Ribbon < BasicObject
19
24
 
20
25
  # Extracts the last argument as a wrapped ribbon, or returns an empty one.
21
26
  # See #extract_ribbon! for details.
27
+ #
28
+ # @return [Ribbon::Wrapper] the wrapped Ribbon at the end of this array
22
29
  def extract_wrapped_ribbon!
23
- Ribbon.wrap extract_options_as_ribbon!
30
+ Ribbon.wrap extract_ribbon!
24
31
  end
25
32
 
26
33
  # Same as #extract_ribbon!
34
+ #
35
+ #
36
+ # @return [Ribbon] the Ribbon at the end of this array
27
37
  alias extract_options_as_ribbon! extract_ribbon!
28
38
 
29
39
  # Same as #extract_wrapped_ribbon!
40
+ #
41
+ # @return [Ribbon::Wrapper] the wrapped Ribbon at the end of this array
30
42
  alias extract_options_as_wrapped_ribbon! extract_wrapped_ribbon!
31
43
 
32
44
  end
33
45
 
34
- ::Array.send :include, ::Ribbon::CoreExt::Array
46
+ ::Array.send :include, ::Ribbon::CoreExtensions::Array
35
47
 
36
48
  end
37
49
  end
@@ -0,0 +1,24 @@
1
+ class Ribbon < BasicObject
2
+ module CoreExtensions
3
+
4
+ # Some useful methods.
5
+ #
6
+ # @author Matheus Afonso Martins Moreira
7
+ # @since 0.6.0
8
+ module BasicObject
9
+
10
+ # Evaluates the block using +instance_eval+ if it takes no arguments;
11
+ # yields +self+ to it otherwise.
12
+ #
13
+ # @yieldparam [self] object this instance
14
+ # @return [Object, nil] the result of the block or nil if not given one
15
+ def __yield_or_eval__(&block)
16
+ if block.arity.zero? then instance_eval &block else block.call self end if block
17
+ end
18
+
19
+ end
20
+
21
+ ::BasicObject.send :include, ::Ribbon::CoreExtensions::BasicObject
22
+
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ require 'ribbon'
2
+
3
+ class Ribbon < BasicObject
4
+ module CoreExtensions
5
+
6
+ # Includes methods to convert hashes to ribbons.
7
+ #
8
+ # @author Matheus Afonso Martins Moreira
9
+ # @since 0.6.0
10
+ module Hash
11
+
12
+ # Converts this hash to a Ribbon.
13
+ #
14
+ # @return a new Ribbon with the contents of this hash
15
+ def to_ribbon
16
+ Ribbon.new self
17
+ end
18
+
19
+ # Converts this hash to a Ribbon::Wrapper.
20
+ #
21
+ # @return a new wrapped Ribbon with the contents of this hash
22
+ def to_ribbon_wrapper
23
+ Ribbon.wrap self
24
+ end
25
+
26
+ # Same as #to_ribbon.
27
+ #
28
+ # @return a new Ribbon with the contents of this hash
29
+ alias to_rbon to_ribbon
30
+
31
+ # Same as #to_ribbon_wrapper.
32
+ #
33
+ # @return a new wrapped Ribbon with the contents of this hash
34
+ alias to_wrapped_ribbon to_ribbon_wrapper
35
+
36
+ end
37
+
38
+ ::Hash.send :include, ::Ribbon::CoreExtensions::Hash
39
+
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ class Ribbon < BasicObject
2
+ module CoreExtensions
3
+
4
+ # Methods available to all objects.
5
+ #
6
+ # @author Matheus Afonso Martins Moreira
7
+ # @since 0.6.0
8
+ module Object
9
+ end
10
+
11
+ ::Object.send :include, ::Ribbon::CoreExtensions::Object
12
+
13
+ end
14
+ end
15
+
16
+ %w(option_scope yield_or_eval).each do |file|
17
+ require file.prepend 'ribbon/core_extensions/object/'
18
+ end
@@ -0,0 +1,21 @@
1
+ require 'ribbon/options'
2
+
3
+ class Ribbon < BasicObject
4
+ module CoreExtensions
5
+
6
+ module Object
7
+
8
+ # Applies an option scope to this object, where all methods called in the
9
+ # block receive the specified options.
10
+ #
11
+ # @see Ribbon::Options
12
+ def option_scope(options = {}, &block)
13
+ Ribbon::Options.apply_to self, options, &block
14
+ end
15
+
16
+ end
17
+
18
+ ::Object.send :include, ::Ribbon::CoreExtensions::Object
19
+
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ require 'ribbon/core_extensions/basic_object'
2
+
3
+ class Ribbon < BasicObject
4
+ module CoreExtensions
5
+
6
+ module Object
7
+
8
+ alias yield_or_eval __yield_or_eval__
9
+
10
+ end
11
+
12
+ ::Object.send :include, ::Ribbon::CoreExtensions::Object
13
+
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ require 'ribbon'
2
+ require 'ribbon/core_extensions/array'
3
+
4
+ class Ribbon < BasicObject
5
+
6
+ # Applies options to all method calls.
7
+ #
8
+ # Ribbon::Options.new(object, option: :value) do
9
+ # method_with some: :settings # equivalent to { some: :settings, option: :value }
10
+ # overrides option: { with: :another_value }
11
+ # end
12
+ #
13
+ # Ribbon::Options.apply_to(Ribbon.new, separator: '->') do |ribbon|
14
+ # ribbon.to_s
15
+ # ribbon.inspect
16
+ # end
17
+ #
18
+ # @author Matheus Afonso Martins Moreira
19
+ # @since 0.6.0
20
+ class Options < BasicObject
21
+
22
+ # Applies the given options to all methods sent to the receiver. Will apply
23
+ # the block immediately, if given one.
24
+ #
25
+ # @param receiver the object that will be receiving the methods
26
+ # @param [Ribbon, Ribbon::Wrapper, #to_hash] options the options that will
27
+ # be applied to all
28
+ # methods
29
+ # @see CoreExt::BasicObject#__yield_or_eval__
30
+ def initialize(receiver, options = {}, &block)
31
+ @receiver, @options = receiver, options
32
+ __yield_or_eval__ &block
33
+ end
34
+
35
+ # Merges the options given to the method with the options associated with
36
+ # this instance and sends the method to the receiver as normal.
37
+ def method_missing(method, *arguments, &block)
38
+ options = arguments.extract_options_as_ribbon!
39
+ arguments << ::Ribbon.deep_merge(@options, options)
40
+ @receiver.__send__ method, *arguments, &block
41
+ end
42
+
43
+ end
44
+
45
+ class << Options
46
+
47
+ # Applies options to all method calls.
48
+ #
49
+ # @see #initialize
50
+ alias apply_to new
51
+
52
+ end
53
+
54
+ end
@@ -11,7 +11,7 @@ class Ribbon < BasicObject
11
11
  # Minor version.
12
12
  #
13
13
  # Increments denote backward-compatible changes and additions.
14
- MINOR = 5
14
+ MINOR = 6
15
15
 
16
16
  # Patch version.
17
17
  #
@@ -2,68 +2,75 @@ require 'ribbon'
2
2
 
3
3
  class Ribbon < BasicObject
4
4
 
5
- # Wraps around a Ribbon in order to provide general-purpose methods.
5
+ # Wraps a Ribbon in order to provide general-purpose methods.
6
6
  #
7
- # Ribbons are designed to use methods as hash keys. In order to maximize
8
- # possibilities, many useful methods were left out of the ribbon class and
9
- # implemented in this wrapper class instead.
7
+ # Ribbons are designed to use methods as hash keys. In order to maximize the
8
+ # number of possibilities, many useful methods, including methods from Object,
9
+ # were left out and included in this class instead.
10
10
  #
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.
11
+ # You can use wrapped ribbons like an ordinary hash. Any undefined methods
12
+ # will be sent to the ribbon's hash. If the hash doesn't respond to the
13
+ # method, it will be sent to the ribbon itself.
14
14
  #
15
- # r = Ribbon.new
16
- # w = Ribbon::Wrapper.new r
15
+ # wrapper = Ribbon::Wrapper.new
17
16
  #
18
- # w.a.b.c
19
- # w[:a][:b][:c]
17
+ # wrapper.a.b.c
18
+ # => {}
19
+ # wrapper.keys
20
+ # => [:a]
20
21
  #
21
- # Wrapped ribbons talk directly to their ribbon's hash:
22
+ # Keep in mind that nested ribbons may or may not be wrapped:
22
23
  #
23
- # w[:k]
24
- # => nil
24
+ # wrapper.a.b.c.keys
25
+ # => {}
26
+ # wrapper
27
+ # => {a: {b: {c: {keys: {}}}}}
25
28
  #
26
- # However, keep in mind that the wrapped hash may contain other ribbons,
27
- # which may not be wrapped:
29
+ # You can wrap and unwrap all ribbons inside:
28
30
  #
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:
38
- #
39
- # w.x = 10
40
- # w.ribbon.x
41
- # => 10
31
+ # wrapper.wrap_all!
32
+ # wrapper.unwrap_all!
33
+ # @author Matheus Afonso Martins Moreira
34
+ # @since 0.2.0
35
+ # @see Ribbon
42
36
  class Wrapper
43
37
 
44
- # The wrapped Ribbon object.
45
- attr :ribbon
38
+ # The wrapped Ribbon.
39
+ #
40
+ # @return [Ribbon] the ribbon wrapped by this instance
41
+ def ribbon
42
+ @ribbon ||= Ribbon.new
43
+ end
46
44
 
47
- # Wraps +ribbon+. If it is already wrapped, uses the wrapped ribbon as this
48
- # wrapper's ribbon. If it is a hash, creates a new Ribbon with its data. If
49
- # it is something else, an ArgumentError will be raised.
45
+ # Wraps a Ribbon, another Wrapper's Ribbon or a hash.
46
+ #
47
+ # @param [Ribbon, Ribbon::Wrapper, #to_hash] ribbon the ribbon that will be
48
+ # wrapped
49
+ # @return [Ribbon] the wrapped Ribbon
50
50
  def ribbon=(ribbon)
51
51
  @ribbon = case ribbon
52
52
  when Wrapper then ribbon.ribbon
53
- when Hash then Ribbon.new ribbon
54
53
  when Ribbon then ribbon
55
- else raise ArgumentError, "Can't wrap #{ribbon.class}"
54
+ else Ribbon.new ribbon.to_hash
56
55
  end
57
56
  end
58
57
 
59
- # Wraps a Ribbon object, providing many general-purpose methods that were
60
- # not defined in the Ribbon itself.
58
+ # Wraps the given Ribbon, another Wrapper's Ribbon or a hash.
59
+ #
60
+ # If given a block, the wrapper will be yielded to it. If the block doesn't
61
+ # take any arguments, it will be evaluated in the context of the wrapper.
62
+ #
63
+ # @see #ribbon=
64
+ # @see Ribbon#initialize
61
65
  def initialize(ribbon = Ribbon.new, &block)
62
66
  self.ribbon = ribbon
63
- if block.arity.zero? then instance_eval &block else block.call self end if block
67
+ __yield_or_eval__ &block
64
68
  end
65
69
 
66
- # Returns the hash of the wrapped ribbon.
70
+ # The hash used by the wrapped Ribbon.
71
+ #
72
+ # @return [Hash] the internal hash of the Ribbon wrapped by this instance
73
+ # @since 0.5.0
67
74
  def internal_hash
68
75
  ribbon.__hash__
69
76
  end
@@ -75,67 +82,85 @@ class Ribbon < BasicObject
75
82
  else ribbon end.__send__ method, *args, &block
76
83
  end
77
84
 
78
- def deep_merge(ribbon)
79
- Ribbon.deep_merge self, ribbon
85
+ # Merges the contents of this wrapped Ribbon with the contents of the given
86
+ # Ribbon into a new Ribbon::Wrapper instance.
87
+ #
88
+ # @param [Ribbon, Ribbon::Wrapper, #to_hash] ribbon the ribbon with new
89
+ # values
90
+ # @return [Ribbon::Wrapper] a new wrapped ribbon containing the results of
91
+ # the merge
92
+ # @yieldparam key the key which identifies both values
93
+ # @yieldparam old_value the value from this wrapped Ribbon
94
+ # @yieldparam new_value the value from the given ribbon
95
+ # @yieldreturn the object that will be used as the new value
96
+ # @since 0.4.5
97
+ # @see #deep_merge!
98
+ # @see Ribbon.deep_merge
99
+ def deep_merge(ribbon, &block)
100
+ Ribbon.wrap Ribbon.deep_merge(self, ribbon, &block)
80
101
  end
81
102
 
82
- def deep_merge!(ribbon)
83
- Ribbon.deep_merge! self, ribbon
103
+ # Merges this wrapped Ribbon with the given Ribbon.
104
+ #
105
+ # @param [Ribbon, Ribbon::Wrapper, #to_hash] ribbon the ribbon with new
106
+ # values
107
+ # @return [self] this Ribbon::Wrapper instance
108
+ # @yieldparam key the key which identifies both values
109
+ # @yieldparam old_value the value from this wrapped Ribbon
110
+ # @yieldparam new_value the value from the given ribbon
111
+ # @yieldreturn the object that will be used as the new value
112
+ # @since 0.4.5
113
+ # @see #deep_merge
114
+ # @see Ribbon.deep_merge!
115
+ def deep_merge!(ribbon, &block)
116
+ Ribbon.deep_merge! self, ribbon, &block
84
117
  end
85
118
 
86
119
  # Wraps all ribbons contained by this wrapper's ribbon.
120
+ #
121
+ # @return [self] this Ribbon::Wrapper instance
122
+ # @since 0.3.0
87
123
  def wrap_all!
88
124
  wrap_all_recursive!
89
125
  end
90
126
 
91
127
  # Unwraps all ribbons contained by this wrapper's ribbon.
128
+ #
129
+ # @return [Ribbon] the Ribbon wrapped by this instance
130
+ # @since 0.3.0
92
131
  def unwrap_all!
93
132
  unwrap_all_recursive!
94
133
  end
95
134
 
96
135
  # Converts the wrapped Ribbon and all ribbons inside into hashes.
136
+ #
137
+ # @return [Hash] the converted contents of this wrapped Ribbon
97
138
  def to_hash
98
139
  to_hash_recursive
99
140
  end
100
141
 
101
- # Converts the wrapped ribbon to a hash and serializes it with YAML. To get
102
- # a Ribbon back from the serialized hash, you can simply load the hash and
103
- # pass it to the Ribbon constructor:
142
+ # Converts the wrapped ribbon to a hash and serializes it with YAML.
104
143
  #
105
- # ribbon = Ribbon.new YAML.load(str)
106
- #
107
- # Alternatively, you can pass a YAML string to the Wrapper::from_yaml
108
- # method.
144
+ # @return [String] the YAML string that represents this Ribbon
145
+ # @see from_yaml
109
146
  def to_yaml
110
147
  to_hash.to_yaml
111
148
  end
112
149
 
113
- # Delegates to Ribbon#to_s.
150
+ # Delegates to the wrapped Ribbon.
151
+ #
152
+ # @return [String] the string representation of this Ribbon::Wrapper
153
+ # @see Ribbon#to_s
114
154
  def to_s
115
155
  ribbon.to_s
116
156
  end
117
157
 
118
- # The class methods.
119
- class << self
120
-
121
- # Wraps a Ribbon instance.
122
- #
123
- # Ribbon::Wrapper[ribbon]
124
- alias [] new
125
-
126
- # Deserializes the hash from the +string+ using YAML and uses it to
127
- # construct a new wrapped ribbon.
128
- def from_yaml(string)
129
- Ribbon::Wrapper.new YAML.load(string)
130
- end
131
-
132
- end
133
-
134
158
  private
135
159
 
136
160
  # Converts the wrapped ribbon and all ribbons inside into hashes using
137
- # recursion. This implementation avoids the creation of additional ribbon or
138
- # wrapper objects.
161
+ # recursion.
162
+ #
163
+ # @return [Hash] the converted contents of the wrapped Ribbon
139
164
  def to_hash_recursive(ribbon = self.ribbon)
140
165
  {}.tap do |hash|
141
166
  ribbon.__hash__.each do |key, value|
@@ -148,21 +173,25 @@ class Ribbon < BasicObject
148
173
  end
149
174
  end
150
175
 
151
- # Recursively wraps all ribbons inside. This implementation avoids the
152
- # creation of additional ribbon or wrapper objects.
176
+ # Recursively wraps all ribbons and hashes inside.
177
+ #
178
+ # @return [self] this Ribbon::Wrapper instance
179
+ # @since 0.3.0
153
180
  def wrap_all_recursive!(wrapper = self)
154
- wrapper.internal_hash.each do |key, value|
155
- wrapper.internal_hash[key] = case value
156
- when Ribbon then wrap_all_recursive! Ribbon::Wrapper[value]
181
+ (hash = wrapper.internal_hash).each do |key, value|
182
+ hash[key] = case value
183
+ when Ribbon, Hash then wrap_all_recursive! Ribbon::Wrapper[value]
157
184
  else value
158
185
  end
159
186
  end
160
187
  wrapper
161
188
  end
162
189
 
163
- # Recursively unwraps all ribbons inside. This implementation avoids the
164
- # creation of additional ribbon or wrapper objects.
165
- def unwrap_all_recursive!(ribbon = self)
190
+ # Recursively unwraps all wrapped ribbons inside.
191
+ #
192
+ # @return [Ribbon] the Ribbon wrapped by this instance
193
+ # @since 0.3.0
194
+ def unwrap_all_recursive!(ribbon = self.ribbon)
166
195
  ribbon.__hash__.each do |key, value|
167
196
  ribbon[key] = case value
168
197
  when Ribbon::Wrapper then unwrap_all_recursive! value.ribbon
@@ -173,4 +202,23 @@ class Ribbon < BasicObject
173
202
  end
174
203
 
175
204
  end
205
+
206
+ class << Wrapper
207
+
208
+ # Wraps a Ribbon instance.
209
+ #
210
+ # @see #initialize
211
+ alias [] new
212
+
213
+ # Deserializes the hash from the +string+ using YAML and uses it to
214
+ # construct a new wrapped ribbon.
215
+ #
216
+ # @return [Ribbon::Wrapper] a new wrapped Ribbon
217
+ # @since 0.4.4
218
+ # @see #to_yaml
219
+ def from_yaml(string)
220
+ ::Ribbon::Wrapper.new YAML.load(string)
221
+ end
222
+
223
+ end
176
224
  end