uber 0.0.15 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d35993c8fa8e0378bda6e3207c7a34c79fcaf901
4
- data.tar.gz: 57e3c7e6251134b7c38f4e946768f795d842da1e
3
+ metadata.gz: 8e46b835cbac5bfe2249537db1825b8cc2402fd8
4
+ data.tar.gz: ca87915c839fab9bcdbcb011e88fccfecf6c589f
5
5
  SHA512:
6
- metadata.gz: 02482631941669e1ce796a484b4876e3b9e82dc1a163935579b6ce46acba7a6533ad0b3655df7cb455bec8ac723108ca256c0effb6895a759a584b2e9dd559b4
7
- data.tar.gz: 47e7927839f0d30ba4ef9ea11e400b114744d65ade257036d8f018476c17a4ada0bfdaed5c2667d4cd7336987c424d2d298b55717ca5fe8b751ff751b8127023
6
+ metadata.gz: 1c570b323ea4e9064d50d1d5c9681ce3071e03368b0d094e15d2ab0de838d148c7ccd0f87bd863364b1754743e8f97a9c6c83dff7c4357da4e9d3696e1e0c985
7
+ data.tar.gz: 33b188c6b3665ca9d0ec98b79c337ae1e5916a624e279aaaab7f0bc2c2320626df876af2837c9106602584ffeaeb8e52cfcef1a69e0013b57b421fe1d78f1e58
@@ -3,4 +3,8 @@ rvm:
3
3
  - 2.0.0
4
4
  - 2.1
5
5
  - 2.2
6
+ - 2.3.1
6
7
  - jruby-19mode
8
+ - jruby
9
+ before_install:
10
+ - gem install bundler
data/CHANGES.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.1.0
2
+
3
+ * Introduce `Uber::Option` as a replacement for `Uber::Options::Value`. It is still there, but deprecated.
4
+ * New API for `Uber::Builder`: You now add builders to `Uber::Builder::Builders` and simply call that instance while passing in context and args. This allows very simple reusable builders that can be used anywhere.
5
+ * `Uber::Options` now uses `Uber::Option`.
6
+ * Removing `Uber::Version` as this is done nicely by `Gem::Version`.
7
+
1
8
  # 0.0.15
2
9
 
3
10
  * `Value#evaluate` is now `#call`. This will make it easier to introduce Null objects.
@@ -55,4 +62,4 @@
55
62
 
56
63
  # 0.0.2
57
64
 
58
- * Add `::inheritable_attr`.
65
+ * Add `::inheritable_attr`.
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in uber.gemspec
4
4
  gemspec
5
5
 
6
- gem "activesupport"
6
+ gem "benchmark-ips"
data/README.md CHANGED
@@ -12,7 +12,7 @@ Add this line to your application's Gemfile:
12
12
  gem 'uber'
13
13
  ```
14
14
 
15
- Ready?
15
+ Uber runs with Ruby >= 1.9.3.
16
16
 
17
17
  # Inheritable Class Attributes
18
18
 
@@ -147,39 +147,61 @@ Note how you simply pass an instance of the callable object into the hash instea
147
147
  options = Uber::Options.new(tags: Tags.new)
148
148
  ```
149
149
 
150
- ## Evaluating Elements
150
+ ## Option
151
151
 
152
- If you want to evaluate a single option element, use `#eval`.
152
+ `Uber::Option` implements the pattern of taking an option, such as a proc, instance method name, or static value, and evaluate it at runtime without knowing the option's implementation.
153
+
154
+ Creating `Option` instances via `::[]` usually happens on class-level in DSL methods.
153
155
 
154
156
  ```ruby
155
- options.eval(:ttl, user) #=> "n/a"
157
+ with_proc = Uber::Option[ ->(options) { "proc: #{options.inspect}" } ]
158
+ with_static = Uber::Option[ "Static value" ]
159
+ with_method = Uber::Option[ :name_of_method ]
160
+
161
+ def name_of_method(options)
162
+ "method: #{options.inspect}"
163
+ end
156
164
  ```
157
165
 
158
- ## Single Values
166
+ Use `#call` to evaluate the options at runtime.
159
167
 
160
- Sometimes you don't need an entire hash but a dynamic value, only.
168
+ ```ruby
169
+ with_proc.(1, 2) #=> "proc: [1, 2]"
170
+ with_static.(1, 2) #=> "Static value" # arguments are ignored
171
+ with_method.(self, 1, 2) #=> "method: [1, 2]" # first arg is context
172
+ ```
173
+
174
+ It's also possible to evaluate a callable object. It has to be marked with `Uber::Callable` beforehand.
161
175
 
162
176
  ```ruby
163
- value = Uber::Options::Value.new(lambda { |volume| volume < 0 ? 0 : volume })
177
+ class MyCallable
178
+ include Uber::Callable
164
179
 
165
- value.evaluate(context, -122.18) #=> 0
166
- ```
180
+ def call(context, *args)
181
+ "callable: #{args.inspect}, #{context}"
182
+ end
183
+ end
167
184
 
168
- Use `Options::Value#evaluate` to handle single values.
185
+ with_callable = Uber::Option[ MyCallable.new ]
186
+ ```
169
187
 
170
- If the `Value` represents a lambda and is `evaluate`d with `nil` as context, the block is called in the original context.
188
+ The context is passed as first argument.
171
189
 
172
190
  ```ruby
173
- volume = 99
174
- value = Uber::Options::Value.new(lambda { volume })
175
-
176
- value.evaluate(nil) #=> 99
191
+ with_callable.(Object, 1, 2) #=> "callable: [1, 2] Object"
177
192
  ```
178
193
 
194
+ You can also make blocks being `instance_exec`ed on the context, giving a unique API to all option types.
179
195
 
180
- ## Performance
196
+ ```ruby
197
+ with_instance_proc = Uber::Option[ ->(options) { "proc: #{options.inspect} #{self}" }, instance_exec: true ]
198
+ ```
199
+
200
+ The first argument now becomes the context, exactly the way it works for the method and callable type.
181
201
 
182
- Evaluating an options hash can be time-consuming. When `Options` contains static elements only, it behaves *and performs* like an ordinary hash.
202
+ ```ruby
203
+ with_instance_proc.(Object, 1, 2) #=> "proc [1, 2] Object"
204
+ ```
183
205
 
184
206
 
185
207
  # Delegates
@@ -216,129 +238,115 @@ song.title #=> "helloween!"
216
238
 
217
239
  Note how `#title` calls the original title and then downcases the string.
218
240
 
219
-
220
241
  # Builder
221
242
 
222
- When included, `Builder` allows to add builder instructions on the class level. These can then be evaluated when instantiating
223
- the class to conditionally build (sub-)classes based on the incoming parameters.
243
+ Builders are good for polymorphically creating objects without having to know where that happens. You define a builder with conditions in one class, and that class takes care of creating the actual desired class.
224
244
 
225
- Builders can be defined in three different ways.
245
+ ## Declarative Interface
246
+
247
+ Include `Uber::Builder` to leverage the `::builds` method for adding builders, and `::build!` to run those builders in a given context and with arbitrary options.
226
248
 
227
- ## Block Syntax
228
249
 
229
250
  ```ruby
230
- class Listener
251
+ require "uber/builder"
252
+
253
+ class User
231
254
  include Uber::Builder
232
255
 
233
- builds do |params|
234
- SignedIn if params[:current_user]
256
+ builds do |options|
257
+ Admin if params[:admin]
235
258
  end
236
259
  end
237
260
 
238
- class SignedIn
261
+ class Admin
239
262
  end
240
263
  ```
241
264
 
242
- The class then has to use the builder to compute a class name using the build blocks you defined.
265
+ Note that you can call `builds` as many times as you want per class.
243
266
 
244
- ```ruby
245
- class Listener
246
- def self.build(params)
247
- class_builder.call(params).
248
- new(params)
249
- end
250
- end
251
- ```
252
-
253
- As you can see, it's still up to you to _instantiate_ the object, the builder only helps you computing the concrete class.
267
+ Run the builders using `::build!`.
254
268
 
255
269
  ```ruby
256
- Listener.build({}) #=> Listener
257
- Listener.build({current_user: @current_user}) #=> SignedIn
270
+ User.build!(User, {}) #=> User
271
+ User.build!(User, { admin: true }) #=> Admin
258
272
  ```
273
+ The first argument is the context in which the builder blocks will be executed. This is also the default return value if all builders returned a falsey value.
259
274
 
260
- ## Proc Syntax
261
-
262
- Setting up builders using the proc syntax allows to call `return` in the block. This is our preferred way to define builders.
263
-
264
- ```ruby
265
- build ->(params) do
266
- return SignedIn if params[:current_user]
267
- return Admin if params[:admin]
268
- Default
269
- end
270
- ```
275
+ All following arguments will be passed straight through to the procs.
271
276
 
272
- This makes the block extremely readable.
277
+ Your API should communicate `User` as the only public class, since the builder hides details about computing the concrete class.
273
278
 
274
- ## Method Syntax
279
+ ### Builder: Procs
275
280
 
276
- You can also specify a build method.
281
+ You may also use procs instead of blocks.
277
282
 
278
283
  ```ruby
279
- build :build_method
284
+ class User
285
+ include Uber::Builder
280
286
 
281
- def self.build_method(params)
282
- return SignedIn if params[:current_user]
287
+ builds ->(options) do
288
+ return SignedIn if params[:current_user]
289
+ return Admin if params[:admin]
290
+ Anonymous
291
+ end
283
292
  end
284
293
  ```
285
294
 
286
- The method has to be a class method on the building class.
295
+ Note that this allows `return`s in the block.
287
296
 
288
- ## Build Context
297
+ ## Builder: Direct API
289
298
 
290
- Normally, build blocks and methods are run in the context where they were defined in. You can change that by passing any context object to `class_builder`.
299
+ In case you don't want the `builds` DSL, you can instantiate a `Builders` object yourself and add builders to it using `#<<`.
291
300
 
292
301
  ```ruby
293
- def self.build(params)
294
- class_builder(context_object) # ...
302
+ MyBuilders = Uber::Builder::Builders.new
303
+ MyBuilders << ->(options) do
304
+ return Admin if options[:admin]
295
305
  end
296
306
  ```
297
307
 
298
- This allows copying builders to other classes and evaluate blocks in the new context.
299
-
300
- ## More On Builders
301
-
302
- Note that builders are _not_ inherited to subclasses. This allows instantiating subclasses directly without running builders.
303
-
304
- This pattern is used in [Cells](https://github.com/apotonick/cells), [Trailblazer](https://github.com/apotonick/trailblazer) and soon Reform and Representable/Roar, too.
305
-
306
- # Version
308
+ Note that you can call `Builders#<<` multiple times per instance.
307
309
 
308
- Writing gems against other gems often involves checking for versions and loading appropriate version strategies - e.g. _"is Rails >= 4.0?"_. Uber gives you `Version` for easy, semantic version deciders.
310
+ Invoke the builder using `#call`.
309
311
 
310
312
  ```ruby
311
- version = Uber::Version.new("1.2.3")
313
+ MyBuilders.call(User, {}) #=> User
314
+ MyBuilders.call(User, { admin: true }) #=> Admin
312
315
  ```
313
316
 
314
- The API currently gives you `#>=` and `#~`.
317
+ Again, the first object is the context/default return value, all other arguments are passed to the builder procs.
315
318
 
316
- ```ruby
317
- version >= "1.1" #=> true
318
- version >= "1.3" #=> false
319
- ```
319
+ ## Builder: Contexts
320
320
 
321
- The `~` method does a semantic check (currently on major and minor level, only).
321
+ Every proc is `instance_exec`ed in the context you pass into `build!` (or `call`), allowing you to define generic, shareable builders.
322
322
 
323
323
  ```ruby
324
- version.~ "1.1" #=> false
325
- version.~ "1.2" #=> true
326
- version.~ "1.3" #=> false
327
- ```
324
+ MyBuilders = Uber::Builder::Builders.new
325
+ MyBuilders << ->(options) do
326
+ return self::Admin if options[:admin] # note the self:: !
327
+ end
328
328
 
329
- Accepting a list of versions, it makes it simple to check for multiple minor versions.
329
+ class User
330
+ class Admin
331
+ end
332
+ end
330
333
 
331
- ```ruby
332
- version.~ "1.1", "1.0" #=> false
333
- version.~ "1.1", "1.2" #=> true
334
+ class Instructor
335
+ class Admin
336
+ end
337
+ end
334
338
  ```
335
339
 
340
+ Now, depending on the context class, the builder will return different classes.
336
341
 
337
- # Undocumented Features
338
-
339
- (Please don't read this!)
342
+ ```ruby
343
+ MyBuilders.call(User, {}) #=> User
344
+ MyBuilders.call(User, { admin: true }) #=> User::Admin
345
+ MyBuilders.call(Instructor, {}) #=> Instructor
346
+ MyBuilders.call(Instructor, { admin: true }) #=> Instructor::Admin
347
+ ```
340
348
 
341
- * You can enforce treating values as dynamic (or not): `Uber::Options::Value.new("time_to_live", dynamic: true)` will always run `#time_to_live` as an instance method on the context, even though it is not a symbol.
349
+ Don't forget the `self::` when writing generic builders, and write tests.
342
350
 
343
351
  # License
344
352
 
@@ -1,97 +1,41 @@
1
- require "uber/options"
1
+ require "uber/option"
2
2
 
3
3
  module Uber
4
- # When included, allows to add builder on the class level.
5
- #
6
- # class Operation
7
- # include Uber::Builder
8
- #
9
- # builds do |params|
10
- # SignedIn if params[:current_user]
11
- # end
12
- #
13
- # class SignedIn
14
- # end
15
- #
16
- # The class then has to call the builder to compute a class name using the build blocks you defined.
17
- #
18
- # def self.build(params)
19
- # class_builder.call(params).
20
- # new(params)
21
- # end
22
4
  module Builder
23
5
  def self.included(base)
24
- base.class_eval do
25
- def self.builders
26
- @builders ||= []
27
- end
28
-
29
- extend ClassMethods
30
- end
6
+ base.extend DSL
7
+ base.extend Build
31
8
  end
32
9
 
33
- # Computes the concrete target class.
34
- class Constant
35
- def initialize(constant, context)
36
- @constant = constant
37
- @context = context
38
- @builders = @constant.builders # only dependency, must be a Cell::Base subclass.
39
- end
10
+ class Builders < Array
11
+ def call(context, *args)
12
+ each do |block|
13
+ klass = block.(context, *args) and return klass # Uber::Value#call()
14
+ end
40
15
 
41
- def call(*args)
42
- build_class_for(*args)
16
+ context
43
17
  end
44
18
 
45
- private
46
- def build_class_for(*args)
47
- @builders.each do |blk|
48
- klass = run_builder_block(blk, *args) and return klass
49
- end
50
- @constant
19
+ def <<(proc)
20
+ super Uber::Option[proc, instance_exec: true]
51
21
  end
22
+ end
52
23
 
53
- def run_builder_block(block, *args)
54
- block.(@context, *args) # Uber::Value.call()
24
+ module DSL
25
+ def builders
26
+ @builders ||= Builders.new
55
27
  end
56
- end
57
28
 
58
- module ClassMethods
59
- # Adds a builder to the cell class. Builders are used in #cell to find out the concrete
60
- # class for rendering. This is helpful if you frequently want to render subclasses according
61
- # to different circumstances (e.g. login situations) and you don't want to place these deciders in
62
- # your view code.
63
- #
64
- # Passes the model and options from #cell into the block.
65
- #
66
- # Multiple build blocks are ORed, if no builder matches the building cell is used.
67
- #
68
- # Example:
69
- #
70
- # Consider two different user box cells in your app.
71
- #
72
- # class AuthorizedUserBox < UserInfoBox
73
- # end
74
- #
75
- # class AdminUserBox < UserInfoBox
76
- # end
77
- #
78
- # Now you don't want to have deciders all over your views - use a declarative builder.
79
- #
80
- # UserInfoBox.build do |model, options|
81
- # AuthorizedUserBox if options[:is_signed_in]
82
- # AdminUserBox if model.admin?
83
- # end
84
- #
85
- # In your view #cell will instantiate the right class for you now.
86
29
  def builds(proc=nil, &block)
87
- builders << Uber::Options::Value.new(proc.nil? ? block : proc) # TODO: provide that in Uber::O:Value.
30
+ builders << (proc || block)
88
31
  end
32
+ end
89
33
 
90
- # Call this from your classes' own ::build method to compute the concrete target class.
91
- # The class_builder is cached, you can't change the context once it's set.
92
- def class_builder(context=nil)
93
- @class_builder ||= Constant.new(self, context)
34
+ module Build
35
+ # Call this from your class to compute the concrete target class.
36
+ def build!(context, *args)
37
+ builders.(context, *args)
94
38
  end
95
- end # ClassMethods
39
+ end
96
40
  end
97
41
  end
@@ -2,7 +2,7 @@ module Uber
2
2
  module InheritableAttr
3
3
 
4
4
  def inheritable_attr(name, options={})
5
- instance_eval %Q{
5
+ instance_eval <<-RUBY, __FILE__, __LINE__ + 1
6
6
  def #{name}=(v)
7
7
  @#{name} = v
8
8
  end
@@ -11,7 +11,7 @@ module Uber
11
11
  return @#{name} if instance_variable_defined?(:@#{name})
12
12
  @#{name} = InheritableAttribute.inherit_for(self, :#{name}, #{options})
13
13
  end
14
- }
14
+ RUBY
15
15
  end
16
16
 
17
17
  def self.inherit_for(klass, name, options={})
@@ -0,0 +1,16 @@
1
+ require "uber/callable"
2
+
3
+ module Uber
4
+ class Option
5
+ def self.[](value, options={}) # TODO: instance_exec: true
6
+ if value.is_a?(Proc)
7
+ return ->(context, *args) { context.instance_exec(*args, &value) } if options[:instance_exec]
8
+ return value
9
+ end
10
+
11
+ return value if value.is_a?(Uber::Callable)
12
+ return ->(context, *args){ context.send(value, *args) } if value.is_a?(Symbol)
13
+ ->(*) { value }
14
+ end
15
+ end
16
+ end
@@ -1,4 +1,5 @@
1
- require 'uber/callable'
1
+ require "uber/callable"
2
+ require "uber/option"
2
3
 
3
4
  module Uber
4
5
  class Options < Hash
@@ -6,41 +7,27 @@ module Uber
6
7
  @static = options
7
8
 
8
9
  options.each do |k,v|
9
- self[k] = option = Value.new(v)
10
- @static = nil if option.dynamic?
10
+ self[k] = Option[v, instance_exec: true]
11
11
  end
12
12
  end
13
13
 
14
- # 1.100000 0.060000 1.160000 ( 1.159762) original
15
- # 0.120000 0.010000 0.130000 ( 0.135803) return self
16
- # 0.930000 0.060000 0.990000 ( 0.997095) without v.evaluate
17
-
18
14
  # Evaluates every element and returns a hash. Accepts context and arbitrary arguments.
19
15
  def evaluate(context, *args)
20
- return @static unless dynamic?
21
-
22
- evaluate_for(context, *args)
23
- end
24
-
25
- # Evaluates a single value.
26
- def eval(key, *args)
27
- self[key].evaluate(*args)
28
- end
29
-
30
- private
31
- def evaluate_for(context, *args)
32
16
  {}.tap do |evaluated|
33
17
  each do |k,v|
34
- evaluated[k] = v.evaluate(context, *args)
18
+ evaluated[k] = v.(context, *args)
35
19
  end
36
20
  end
37
21
  end
38
22
 
39
- def dynamic?
40
- not @static
23
+ # Evaluates a single value.
24
+ def eval(key, *args)
25
+ self[key].(*args)
41
26
  end
42
27
 
28
+ private
43
29
 
30
+ # DEPRECATED! PLEASE USE UBER::OPTION.
44
31
  class Value # TODO: rename to Value.
45
32
  def initialize(value, options={})
46
33
  @value, @dynamic = value, options[:dynamic]
@@ -103,4 +90,4 @@ module Uber
103
90
  end
104
91
  end
105
92
  end
106
- end
93
+ end
@@ -1,33 +1,3 @@
1
1
  module Uber
2
- class Version < Hash
3
- def initialize(version)
4
- @version = Gem::Version.new(version)
5
- major, minor, patch = @version.segments
6
-
7
- self[:major] = major || 0
8
- self[:minor] = minor || 0
9
- self[:patch] = patch || 0
10
- end
11
-
12
- def >=(version)
13
- major, minor, patch = parse(version)
14
-
15
- self[:major] > major or
16
- (self[:major] == major and self[:minor] >= minor and self[:patch] >= patch)
17
- end
18
-
19
- def ~(*versions)
20
- !! versions.find do |v|
21
- major, minor, patch = parse(v)
22
-
23
- self[:major] == major and self[:minor] == minor
24
- end
25
- end
26
-
27
- private
28
- def parse(version)
29
- major, minor, patch = Gem::Version.new(version).segments
30
- [major||0, minor||0, patch||0]
31
- end
32
- end
33
- end
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,17 @@
1
+ require "test_helper"
2
+ require "uber/options"
3
+ require "uber/option"
4
+ require "benchmark/ips"
5
+
6
+ proc = ->(options) { "great" }
7
+
8
+ value = Uber::Options::Value.new(proc)
9
+ option = Uber::Option[proc, instance_exec: true]
10
+
11
+ Benchmark.ips do |x|
12
+ x.report(:value) { value.(self, {}) }
13
+ x.report(:option) { option.(self, {}) }
14
+ end
15
+
16
+ # value 946.110k (± 2.4%) i/s - 4.766M in 5.040395s
17
+ # option 1.583M (± 1.6%) i/s - 7.928M in 5.009953s
@@ -17,7 +17,7 @@ class BuilderTest < MiniTest::Spec
17
17
  end
18
18
 
19
19
  def self.build(options)
20
- class_builder.call(options).new
20
+ build!(self, options).new
21
21
  end
22
22
  end
23
23
 
@@ -40,7 +40,7 @@ class BuilderTest < MiniTest::Spec
40
40
  end
41
41
 
42
42
  def self.build(options)
43
- class_builder.call(options).new
43
+ build!(self, options).new
44
44
  end
45
45
  end
46
46
 
@@ -66,7 +66,7 @@ class BuilderTest < MiniTest::Spec
66
66
  end
67
67
 
68
68
  def self.build(options)
69
- class_builder.call(options).new
69
+ build!(self, options).new
70
70
  end
71
71
  end
72
72
 
@@ -94,10 +94,17 @@ class BuilderScopeTest < MiniTest::Spec
94
94
  end
95
95
 
96
96
  def self.build(context, options={})
97
- class_builder(context).call(options)
97
+ build!(context, options)
98
98
  end
99
99
  end
100
100
 
101
+ it do
102
+ Song.build(self.class).must_equal BuilderScopeTest::Hit
103
+
104
+ # this runs BuilderScopeTest::builder_method and returns self.
105
+ Song.build(self.class, from_builder_method: true).must_equal BuilderScopeTest
106
+ end
107
+
101
108
  class Evergreen
102
109
  class Hit
103
110
  end
@@ -110,7 +117,7 @@ class BuilderScopeTest < MiniTest::Spec
110
117
  self.builders = Song.builders
111
118
 
112
119
  def self.build(context, options={})
113
- class_builder(context).call(options)
120
+ build!(context, options)
114
121
  end
115
122
 
116
123
  def self.builder_method(options)
@@ -118,21 +125,23 @@ class BuilderScopeTest < MiniTest::Spec
118
125
  end
119
126
  end
120
127
 
121
- it do
122
- Song.build(self.class).must_equal BuilderScopeTest::Hit
123
-
124
- # this runs BuilderScopeTest::builder_method and returns self.
125
- Song.build(self.class, from_builder_method: true).must_equal BuilderScopeTest
126
-
127
- # since the class_builder gets cached, this won't change.
128
- Song.build(Song).must_equal BuilderScopeTest::Hit
129
- end
130
-
131
-
132
128
  it do
133
129
  # running the "copied" block in Evergreen will reference the correct @context.
134
130
  Evergreen.build(Evergreen).must_equal BuilderScopeTest::Evergreen::Hit
135
131
 
136
132
  Evergreen.build(Evergreen, from_builder_method: true).must_equal BuilderScopeTest::Evergreen
137
133
  end
138
- end
134
+
135
+ #---
136
+ # Builders API
137
+ # Builders#call
138
+ # Builders#<<
139
+ A = Class.new
140
+ MyBuilders = Uber::Builder::Builders.new
141
+ MyBuilders << ->(options) do
142
+ return Song if options[:hit]
143
+ end
144
+
145
+ it { MyBuilders.call(A, {}).new.must_be_instance_of A }
146
+ it { MyBuilders.call(A, { hit: true }).new.must_be_instance_of Song }
147
+ end
@@ -0,0 +1,47 @@
1
+ require "test_helper"
2
+ require "uber/option"
3
+
4
+ class OptionTest < Minitest::Spec
5
+ Option = Uber::Option
6
+
7
+ # proc
8
+ it { Option[ ->(*args) { "proc! #{args.inspect}" } ].(1,2).must_equal "proc! [1, 2]" }
9
+ it { Option[ lambda { "proc!" } ].().must_equal "proc!" }
10
+
11
+ # proc with instance_exec
12
+ it { Option[ ->(*args) { "#{self.class} #{args.inspect}" } ].(Object, 1, 2).must_equal "OptionTest [Object, 1, 2]" }
13
+ it { Option[ ->(*args) { "#{self} #{args.inspect}" }, instance_exec: true ].(Object, 1, 2).must_equal "Object [1, 2]" }
14
+
15
+ # static
16
+ it { Option[true].().must_equal true }
17
+ it { Option[nil].().must_equal nil }
18
+ it { Option[false].().must_equal false }
19
+ # args are ignored.
20
+ it { Option[true].(1,2,3).must_equal true }
21
+
22
+ # instance method
23
+ class Hello
24
+ def hello(*args); "Hello! #{args.inspect}" end
25
+ end
26
+ it { Option[:hello].(Hello.new).must_equal "Hello! []" }
27
+ it { Option[:hello].(Hello.new, 1, 2).must_equal "Hello! [1, 2]" }
28
+
29
+ #---
30
+ # Callable
31
+ class Callio
32
+ include Uber::Callable
33
+ def call(); "callable!" end
34
+ end
35
+
36
+ it { Option[Callio.new].().must_equal "callable!" }
37
+ end
38
+
39
+ # require "benchmark/ips"
40
+
41
+ # method = Uber::Option[->(context, options) { context }]
42
+ # proc = Uber::Option[A.new { |context, options| context }]
43
+
44
+ # Benchmark.ips do |x|
45
+ # x.report(:method) { method.(Object, {}) }
46
+ # x.report(:proc) { proc.(Object, {}) }
47
+ # end
@@ -1,96 +1,12 @@
1
1
  require 'test_helper'
2
2
  require 'uber/options'
3
3
 
4
- class UberOptionTest < MiniTest::Spec
5
- Value = Uber::Options::Value
6
- let (:object) { Object.new }
7
-
8
- class Callable
9
- include Uber::Callable
10
- def call(*); 999 end
11
- end
12
-
13
- describe "#dynamic?" do
14
- it { Value.new(1).dynamic?.must_equal false }
15
- it { Value.new(true).dynamic?.must_equal false }
16
- it { Value.new("loud").dynamic?.must_equal false }
17
- it { Value.new(:loud, :dynamic => false).dynamic?.must_equal false }
18
- it { Value.new("loud", :dynamic => true).dynamic?.must_equal true }
19
-
20
- it { Value.new(lambda {}).dynamic?.must_equal true }
21
- it { Value.new(Proc.new{}).dynamic?.must_equal true }
22
- it { Value.new(:method).dynamic?.must_equal true }
23
-
24
- # Uber::Callable
25
- it { Value.new(Callable.new).dynamic?.must_equal true }
26
- end
27
-
28
- describe "#call" do
29
- let (:version) { Module.new { def version; 999 end } }
30
-
31
- it { Value.new(nil).(Object.new).must_equal nil }
32
- # it { Value.new(nil, :dynamic => true).(Object.new).must_equal nil } # DISCUSS: should we handle that?
33
-
34
- it { Value.new(true).(Object.new).must_equal true }
35
-
36
- it { Value.new(:version).(object.extend(version)).must_equal 999 }
37
- it { Value.new("version", :dynamic => true).(object.extend(version)).must_equal 999 }
38
- it { Value.new(:version, :dynamic => false).(object.extend(version)).must_equal :version }
39
- it { Value.new(lambda { self }).(object).must_equal object }
40
- it { Value.new(lambda { self }).(nil).must_equal self }
41
-
42
- it { Value.new(lambda { :loud }, :dynamic => true).(object).must_equal :loud }
43
-
44
- # Uber::Callable
45
- it { Value.new(Callable.new).(nil).must_equal 999 }
46
- end
47
-
48
- it "#call is aliased to evaluate" do
49
- Value.new(Callable.new).(nil).must_equal 999
50
- end
51
-
52
- describe "passing options" do
53
- let (:version) { Module.new { def version(*args); args.inspect end } }
54
- let (:block) { Proc.new { |*args| args.inspect } }
55
- let (:callable) { (Class.new { include Uber::Callable; def call(*args); args.inspect; end }).new }
56
-
57
- it { Value.new(:version).(object.extend(version), 1, 2, 3).must_equal "[1, 2, 3]" }
58
- it { Value.new(block).(object, 1, 2, 3).must_equal "[1, 2, 3]" }
59
- it { Value.new(callable).(Object, 1, 2, 3).must_equal "[Object, 1, 2, 3]" }
60
- end
61
-
62
- # it "speed" do
63
- # require "benchmark"
64
-
65
- # options = 1000000.times.collect do
66
- # Uber::Options.new(expires: false)
67
- # end
68
-
69
- # time = Benchmark.measure do
70
- # options.each do |opt|
71
- # opt.evaluate(nil)
72
- # end
73
- # end
74
-
75
- # puts "good results"
76
- # puts time
77
- # end
78
- end
79
-
80
- # TODO: test passing arguments to block and method optionally.
81
-
82
4
  class UberOptionsTest < MiniTest::Spec
83
5
  Options = Uber::Options
84
6
 
85
7
  let (:dynamic) { Options.new(:volume =>1, :style => "Punkrock", :track => Proc.new { |i| i.to_s }) }
86
8
 
87
- describe "#dynamic?" do
88
- it { Options.new(:volume =>1, :style => "Punkrock").send(:dynamic?).must_equal false }
89
- it { Options.new(:style => Proc.new{}, :volume =>1).send(:dynamic?).must_equal true }
90
- end
91
-
92
9
  describe "#evaluate" do
93
-
94
10
  it { dynamic.evaluate(Object.new, 999).must_equal({:volume =>1, :style => "Punkrock", :track => "999"}) }
95
11
 
96
12
  describe "static" do
@@ -114,4 +30,4 @@ class UberOptionsTest < MiniTest::Spec
114
30
  it { dynamic.eval(:style, 999).must_equal "Punkrock" }
115
31
  it { dynamic.eval(:track, Object.new, 999).must_equal "999" }
116
32
  end
117
- end
33
+ end
@@ -1,11 +1,11 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/uber/uber_version', __FILE__)
2
+ require File.expand_path('../lib/uber/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Nick Sutterer"]
6
6
  gem.email = ["apotonick@gmail.com"]
7
7
  gem.description = %q{A gem-authoring framework.}
8
- gem.summary = %q{Gem-authoring tools like class method inheritance in modules, dynamic options and more.}
8
+ gem.summary = %q{Gem-authoring tools like generic builders, dynamic options and more.}
9
9
  gem.homepage = "https://github.com/apotonick/uber"
10
10
  gem.license = "MIT"
11
11
 
@@ -16,6 +16,6 @@ Gem::Specification.new do |gem|
16
16
  gem.require_paths = ["lib"]
17
17
  gem.version = Uber::VERSION
18
18
 
19
- gem.add_development_dependency "rake", ">= 0.10.1"
20
- gem.add_development_dependency "minitest", ">= 5.0.0"
19
+ gem.add_development_dependency "rake"
20
+ gem.add_development_dependency "minitest"
21
21
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.15
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-01 00:00:00.000000000 Z
11
+ date: 2016-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.10.1
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.10.1
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 5.0.0
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 5.0.0
40
+ version: '0'
41
41
  description: A gem-authoring framework.
42
42
  email:
43
43
  - apotonick@gmail.com
@@ -57,16 +57,17 @@ files:
57
57
  - lib/uber/callable.rb
58
58
  - lib/uber/delegates.rb
59
59
  - lib/uber/inheritable_attr.rb
60
+ - lib/uber/option.rb
60
61
  - lib/uber/options.rb
61
- - lib/uber/uber_version.rb
62
62
  - lib/uber/version.rb
63
+ - test/builder-benchmark.rb
63
64
  - test/builder_test.rb
64
65
  - test/delegates_test.rb
65
66
  - test/inheritable_attr_test.rb
66
67
  - test/inheritance_test.rb
68
+ - test/option_test.rb
67
69
  - test/options_test.rb
68
70
  - test/test_helper.rb
69
- - test/version_test.rb
70
71
  - test/zeugs.rb
71
72
  - uber.gemspec
72
73
  homepage: https://github.com/apotonick/uber
@@ -89,18 +90,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
90
  version: '0'
90
91
  requirements: []
91
92
  rubyforge_project:
92
- rubygems_version: 2.4.8
93
+ rubygems_version: 2.6.3
93
94
  signing_key:
94
95
  specification_version: 4
95
- summary: Gem-authoring tools like class method inheritance in modules, dynamic options
96
- and more.
96
+ summary: Gem-authoring tools like generic builders, dynamic options and more.
97
97
  test_files:
98
+ - test/builder-benchmark.rb
98
99
  - test/builder_test.rb
99
100
  - test/delegates_test.rb
100
101
  - test/inheritable_attr_test.rb
101
102
  - test/inheritance_test.rb
103
+ - test/option_test.rb
102
104
  - test/options_test.rb
103
105
  - test/test_helper.rb
104
- - test/version_test.rb
105
106
  - test/zeugs.rb
106
- has_rdoc:
@@ -1,3 +0,0 @@
1
- module Uber
2
- VERSION = "0.0.15"
3
- end
@@ -1,27 +0,0 @@
1
- require 'test_helper'
2
-
3
-
4
- class VersionTest < MiniTest::Spec
5
- subject { Uber::Version.new("1.2.3") } # Rails version
6
-
7
- it { subject.~("1.0").must_equal false }
8
- it { subject.~("1.1").must_equal false }
9
- it { subject.~("1.2").must_equal true }
10
- it { subject.~("1.3").must_equal false }
11
- it { subject.~("2.2").must_equal false }
12
- it { subject.~("1.0", "1.1").must_equal false }
13
- it { subject.~("1.0", "1.1", "1.2").must_equal true }
14
- it { subject.~("1.2", "1.3").must_equal true }
15
-
16
- it { (subject >= "1.2.4").must_equal false }
17
- it { (subject >= "1.2.2").must_equal true }
18
- it { (subject >= "0.3.1").must_equal true }
19
- it { (subject >= "0.3.6").must_equal true }
20
- it { (subject >= "0.3").must_equal true }
21
- it { (subject >= "0.2.4").must_equal true }
22
- it { (subject >= "0.2").must_equal true }
23
- it { (subject >= "1.2").must_equal true }
24
- it { (subject >= "1.1").must_equal true }
25
- it { (subject >= "1.3").must_equal false }
26
- it { (subject >= "2.1").must_equal false }
27
- end