uber 0.0.15 → 0.1.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.
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