uber 0.0.2 → 0.0.3
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 +4 -4
- data/CHANGES.md +4 -0
- data/README.md +74 -4
- data/lib/uber/options.rb +22 -9
- data/lib/uber/version.rb +1 -1
- data/test/inheritance_test.rb +3 -0
- data/test/options_test.rb +29 -6
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca88c9e957495cdf978405ff7c9c7f67d6d0258a
|
4
|
+
data.tar.gz: 151fb75aea52f7485dd8eee7d86afac8b80b61ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4748f03b0f45606e62428bf2d14a7ad5ab22c29b68f887c012faafb738110d4279c0c818098ba5c3b2f7c531d65dab13710ff53ec50f13f8d00aa6a21cb36e85
|
7
|
+
data.tar.gz: ce170857e5789abd29ea165be5aae4ee4ecb527d80de5224bf1a70c0c232461adade8483a6d1ad3af030c64a83eed1bb0c549aa57d56ebfeda5f768322be0f7b
|
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -56,19 +56,89 @@ It's similar to ActiveSupport's `class_attribute` but with a simpler implementat
|
|
56
56
|
This module is very popular amongst numerous gems like Cells, Representable, Roar and Reform.
|
57
57
|
|
58
58
|
|
59
|
-
# Options
|
59
|
+
# Dynamic Options
|
60
60
|
|
61
|
-
Implements the pattern of defining configuration options and evaluating them at run-time.
|
61
|
+
Implements the pattern of defining configuration options and dynamically evaluating them at run-time.
|
62
|
+
|
63
|
+
Usually DSL methods accept a number of options that can either be static values, symbolized instance method names, or blocks (lambdas/Procs).
|
64
|
+
|
65
|
+
Here's an example from Cells.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
cache :show, tags: lambda { Tag.last }, expire_in: 5.mins, ttl: :time_to_live
|
69
|
+
```
|
70
|
+
|
71
|
+
Usually, when processing these options, you'd have to check every option for its type, evaluate the `tag:` lambda in a particular context, call the `#time_to_live` instance method, etc.
|
72
|
+
|
73
|
+
This is abstracted in `Uber::Options` and could be implemented like this.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
options = Uber::Options.new(tags: lambda { Tag.last }, expire_in: 5.mins, ttl: :time_to_live)
|
77
|
+
```
|
78
|
+
|
79
|
+
Just initialize `Options` with your actual options hash. While this usually happens on class level at compile-time, evaluating the hash happens at run-time.
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
class User < ActiveRecord::Base # this could be any Ruby class.
|
83
|
+
# .. lots of code
|
84
|
+
|
85
|
+
def time_to_live
|
86
|
+
"n/a"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
user = User.find(1)
|
91
|
+
|
92
|
+
options.evaluate(user, *args) #=> {tags: "hot", expire_in: 300, ttl: "n/a"}
|
93
|
+
```
|
94
|
+
|
95
|
+
## Evaluating Dynamic Options
|
96
|
+
|
97
|
+
To evaluate the options to a real hash, the following happens:
|
98
|
+
|
99
|
+
* The `tags:` lambda is executed in `user` context (using `instance_exec`). This allows accessing instance variables or calling instance methods. All `*args` are passed as block parameters to the lambda.
|
100
|
+
* Nothing is done with `expires_in`'s value, it is static.
|
101
|
+
* `user.time_to_live?` is called as the symbol `:time_to_live` indicates that this is an instance method.
|
102
|
+
|
103
|
+
The default behaviour is to treat `Proc`s, lambdas and symbolized `:method` names as dynamic options, everything else is considered static. This is a pattern well-known from Rails and other frameworks.
|
104
|
+
|
105
|
+
## Evaluating Elements
|
106
|
+
|
107
|
+
If you wanna evaluate a single option element, use `#eval`.
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
options.eval(:ttl, user) #=> "n/a"
|
111
|
+
```
|
112
|
+
|
113
|
+
## Single Values
|
114
|
+
|
115
|
+
Sometimes you don't need an entire hash but a dynamic value, only.
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
value = Uber::Options::Value.new(lambda { |volume| volume < 0 ? 0 : volume })
|
119
|
+
|
120
|
+
value.evaluate(context, -122.18) #=> 0
|
121
|
+
```
|
122
|
+
|
123
|
+
Use `Options::Value#evaluate` to handle single values.
|
124
|
+
|
125
|
+
## Performance
|
126
|
+
|
127
|
+
Evaluating an options hash can be time-consuming. When `Options` contains static elements only, it behaves *and performs* like an ordinary hash.
|
62
128
|
|
63
|
-
Usually DSL methods accept a number of options that can either be static values, instance method names as symbols, or blocks (lambdas/Procs).
|
64
129
|
|
65
130
|
Uber::Options.new volume: 9, track: lambda { |s| s.track }
|
66
131
|
|
67
132
|
|
68
|
-
|
133
|
+
dynamic: true
|
69
134
|
|
70
135
|
only use for declarative assets, not at runtime (use a hash)
|
71
136
|
|
137
|
+
# Undocumented Features
|
138
|
+
|
139
|
+
(Please don't read this!)
|
140
|
+
|
141
|
+
* 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 thou it is not a symbol.
|
72
142
|
|
73
143
|
# License
|
74
144
|
|
data/lib/uber/options.rb
CHANGED
@@ -41,29 +41,42 @@ module Uber
|
|
41
41
|
|
42
42
|
class Value # TODO: rename to Value.
|
43
43
|
def initialize(value, options={})
|
44
|
-
@value = value
|
45
|
-
|
44
|
+
@value, @options = value, options
|
45
|
+
|
46
|
+
return if @options.has_key?(:dynamic)
|
47
|
+
|
48
|
+
# conventional behaviour:
|
49
|
+
@callable = @options[:dynamic] = true if @value.kind_of?(Proc)
|
50
|
+
@options[:dynamic] = true if @value.is_a?(Symbol)
|
46
51
|
end
|
47
52
|
|
48
53
|
def evaluate(context, *args)
|
49
|
-
return
|
54
|
+
return @value unless dynamic?
|
50
55
|
|
51
56
|
evaluate_for(context, *args)
|
52
57
|
end
|
53
58
|
|
54
59
|
def dynamic?
|
55
|
-
@options[:
|
60
|
+
@options[:dynamic]
|
56
61
|
end
|
57
62
|
|
58
63
|
private
|
59
|
-
def evaluate_for(
|
60
|
-
return
|
61
|
-
|
64
|
+
def evaluate_for(*args)
|
65
|
+
return method!(*args) unless callable?
|
66
|
+
# TODO: change to context.instance_exec and deprecate first argument.
|
67
|
+
proc!(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def method!(context, *args)
|
71
|
+
context.send(@value, *args)
|
62
72
|
end
|
63
73
|
|
64
74
|
def proc!(context, *args)
|
65
|
-
|
66
|
-
|
75
|
+
context.instance_exec(*args, &@value)
|
76
|
+
end
|
77
|
+
|
78
|
+
def callable?
|
79
|
+
@callable
|
67
80
|
end
|
68
81
|
end
|
69
82
|
end
|
data/lib/uber/version.rb
CHANGED
data/test/inheritance_test.rb
CHANGED
@@ -21,6 +21,9 @@ class InheritanceTest < MiniTest::Spec
|
|
21
21
|
|
22
22
|
CODE_BLOCK = lambda { |base| base.class_eval { extend ClassMethods } } # i want that to be executed at every include
|
23
23
|
|
24
|
+
instance_exec do
|
25
|
+
@block = CODE_BLOCK # this would happen in inherited_included do .. end
|
26
|
+
end
|
24
27
|
|
25
28
|
def self.included(includer) #
|
26
29
|
# CODE_BLOCK.call(base)
|
data/test/options_test.rb
CHANGED
@@ -3,18 +3,39 @@ require 'uber/options'
|
|
3
3
|
|
4
4
|
class UberOptionTest < MiniTest::Spec
|
5
5
|
Value = Uber::Options::Value
|
6
|
+
let (:object) { Object.new }
|
6
7
|
|
7
8
|
describe "#dynamic?" do
|
8
|
-
it { Value.new(1).dynamic?.must_equal
|
9
|
-
it { Value.new(true).dynamic?.must_equal
|
10
|
-
it { Value.new(
|
9
|
+
it { Value.new(1).dynamic?.must_equal nil }
|
10
|
+
it { Value.new(true).dynamic?.must_equal nil }
|
11
|
+
it { Value.new("loud").dynamic?.must_equal nil }
|
12
|
+
it { Value.new(:loud, :dynamic => false).dynamic?.must_equal false }
|
11
13
|
|
12
14
|
it { Value.new(lambda {}).dynamic?.must_equal true }
|
13
15
|
it { Value.new(Proc.new{}).dynamic?.must_equal true }
|
14
|
-
it { Value.new(:method
|
16
|
+
it { Value.new(:method).dynamic?.must_equal true }
|
15
17
|
end
|
16
18
|
|
19
|
+
describe "#evaluate" do
|
20
|
+
let (:version) { Module.new { def version; 999 end } }
|
21
|
+
|
22
|
+
it { Value.new(nil).evaluate(Object.new).must_equal nil }
|
23
|
+
# it { Value.new(nil, :dynamic => true).evaluate(Object.new).must_equal nil } # DISCUSS: should we handle that?
|
24
|
+
|
25
|
+
it { Value.new(true).evaluate(Object.new).must_equal true }
|
26
|
+
|
27
|
+
it { Value.new(:version).evaluate(object.extend(version)).must_equal 999 }
|
28
|
+
it { Value.new("version", :dynamic => true).evaluate(object.extend(version)).must_equal 999 }
|
29
|
+
it { Value.new(:version, :dynamic => false).evaluate(object.extend(version)).must_equal :version }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "passing options" do
|
33
|
+
let (:version) { Module.new { def version(*args); args.inspect end } }
|
34
|
+
let (:block) { Proc.new { |*args| args.inspect } }
|
17
35
|
|
36
|
+
it { Value.new(:version).evaluate(object.extend(version), 1, 2, 3).must_equal "[1, 2, 3]" }
|
37
|
+
it { Value.new(block).evaluate(object, 1, 2, 3).must_equal "[1, 2, 3]" }
|
38
|
+
end
|
18
39
|
|
19
40
|
# it "speed" do
|
20
41
|
# require "benchmark"
|
@@ -34,6 +55,8 @@ class UberOptionTest < MiniTest::Spec
|
|
34
55
|
# end
|
35
56
|
end
|
36
57
|
|
58
|
+
# TODO: test passing arguments to block and method optionally.
|
59
|
+
|
37
60
|
class UberOptionsTest < MiniTest::Spec
|
38
61
|
Options = Uber::Options
|
39
62
|
|
@@ -46,7 +69,7 @@ class UberOptionsTest < MiniTest::Spec
|
|
46
69
|
|
47
70
|
describe "#evaluate" do
|
48
71
|
|
49
|
-
it { dynamic.evaluate(999).must_equal({:volume =>1, :style => "Punkrock", :track => "999"}) }
|
72
|
+
it { dynamic.evaluate(Object.new, 999).must_equal({:volume =>1, :style => "Punkrock", :track => "999"}) }
|
50
73
|
|
51
74
|
describe "static" do
|
52
75
|
let (:static) { Options.new(:volume =>1, :style => "Punkrock") }
|
@@ -67,6 +90,6 @@ class UberOptionsTest < MiniTest::Spec
|
|
67
90
|
describe "#eval" do
|
68
91
|
it { dynamic.eval(:volume, 999).must_equal 1 }
|
69
92
|
it { dynamic.eval(:style, 999).must_equal "Punkrock" }
|
70
|
-
it { dynamic.eval(:track, 999).must_equal "999" }
|
93
|
+
it { dynamic.eval(:track, Object.new, 999).must_equal "999" }
|
71
94
|
end
|
72
95
|
end
|
metadata
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uber
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 0.10.1
|
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
26
|
version: 0.10.1
|
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
33
|
version: 5.0.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
40
|
version: 5.0.0
|
41
41
|
description: A gem-authoring framework.
|
@@ -45,7 +45,7 @@ executables: []
|
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
|
-
-
|
48
|
+
- .gitignore
|
49
49
|
- CHANGES.md
|
50
50
|
- Gemfile
|
51
51
|
- LICENSE
|
@@ -70,17 +70,17 @@ require_paths:
|
|
70
70
|
- lib
|
71
71
|
required_ruby_version: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - '>='
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
77
|
requirements:
|
78
|
-
- -
|
78
|
+
- - '>='
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: '0'
|
81
81
|
requirements: []
|
82
82
|
rubyforge_project:
|
83
|
-
rubygems_version: 2.
|
83
|
+
rubygems_version: 2.0.3
|
84
84
|
signing_key:
|
85
85
|
specification_version: 4
|
86
86
|
summary: Gem-authoring tools like class method inheritance in modules, dynamic options
|