arturo 1.1.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +14 -0
- data/lib/arturo.rb +27 -0
- data/lib/arturo/feature_caching.rb +81 -0
- metadata +16 -28
data/README.md
CHANGED
@@ -239,6 +239,20 @@ The latter can be used like so:
|
|
239
239
|
widgets
|
240
240
|
end
|
241
241
|
|
242
|
+
#### Outside a Controller
|
243
|
+
|
244
|
+
If you want to check availability outside of a controller or view (really
|
245
|
+
outside of something that has `Arturo::FeatureAvailability` mixed in), you
|
246
|
+
can ask either
|
247
|
+
|
248
|
+
Arturo.feature_enabled_for?(:foo, recipient)
|
249
|
+
|
250
|
+
or the slightly fancier
|
251
|
+
|
252
|
+
Arturo.foo_enabled_for?(recipient)
|
253
|
+
|
254
|
+
Both check whether the `foo` feature exists and is enabled for `recipient`.
|
255
|
+
|
242
256
|
#### Caching
|
243
257
|
|
244
258
|
**Note**: Arturo does not yet have caching support. Be very careful when
|
data/lib/arturo.rb
CHANGED
@@ -3,7 +3,34 @@ module Arturo
|
|
3
3
|
require 'arturo/special_handling'
|
4
4
|
require 'arturo/feature_availability'
|
5
5
|
require 'arturo/feature_management'
|
6
|
+
require 'arturo/feature_caching'
|
6
7
|
require 'arturo/controller_filters'
|
7
8
|
require 'arturo/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
|
8
9
|
|
10
|
+
class <<self
|
11
|
+
|
12
|
+
# Quick check for whether a feature is enabled for a recipient.
|
13
|
+
# @param [String, Symbol] feature_name
|
14
|
+
# @param [#id] recipient
|
15
|
+
# @return [true,false] whether the feature exists and is enabled for the recipient
|
16
|
+
def feature_enabled_for?(feature_name, recipient)
|
17
|
+
f = self::Feature.to_feature(feature_name)
|
18
|
+
f && f.enabled_for?(recipient)
|
19
|
+
end
|
20
|
+
|
21
|
+
ENABLED_FOR_METHOD_NAME = /^(\w+)_enabled_for\?$/
|
22
|
+
|
23
|
+
def respond_to?(symbol)
|
24
|
+
symbol.to_s =~ ENABLED_FOR_METHOD_NAME || super(symbol)
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(symbol, *args, &block)
|
28
|
+
if (args.length == 1 && match = ENABLED_FOR_METHOD_NAME.match(symbol.to_s))
|
29
|
+
feature_enabled_for?(match[1], args[0])
|
30
|
+
else
|
31
|
+
super(symbol, *args, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
9
36
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Arturo
|
2
|
+
|
3
|
+
# To be extended by Arturo::Feature if you want to enable
|
4
|
+
# in-memory caching.
|
5
|
+
# NB: Arturo's feature caching only works when using
|
6
|
+
# Arturo::Feature.to_feature or when using the helper methods
|
7
|
+
# in Arturo and Arturo::FeatureAvailability.
|
8
|
+
# NB: if you have multiple application servers, you almost certainly
|
9
|
+
# want to clear this cache after each request:
|
10
|
+
#
|
11
|
+
# class ApplicationController < ActionController::Base
|
12
|
+
# after_filter { Arturo::Feature.clear_feature_cache }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# Alternatively, you could redefine Arturo::Feature.feature_cache
|
16
|
+
# to use a shared cache like Memcached.
|
17
|
+
module FeatureCaching
|
18
|
+
|
19
|
+
def self.extended(base)
|
20
|
+
class <<base
|
21
|
+
alias_method_chain :to_feature, :caching
|
22
|
+
attr_accessor :cache_ttl, :feature_cache
|
23
|
+
end
|
24
|
+
base.cache_ttl = 0
|
25
|
+
base.feature_cache = Arturo::FeatureCaching::Cache.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def caches_features?
|
29
|
+
self.cache_ttl.to_i > 0
|
30
|
+
end
|
31
|
+
|
32
|
+
# Wraps Arturo::Feature.to_feature with in-memory caching.
|
33
|
+
def to_feature_with_caching(feature_or_symbol)
|
34
|
+
if !self.caches_features?
|
35
|
+
return to_feature_without_caching(feature_or_symbol)
|
36
|
+
elsif (feature_or_symbol.kind_of?(Arturo::Feature))
|
37
|
+
feature_cache.write(feature_or_symbol.symbol, feature_or_symbol, :expires_in => cache_ttl)
|
38
|
+
feature_or_symbol
|
39
|
+
elsif (cached_feature = feature_cache.read(feature_or_symbol.to_sym))
|
40
|
+
cached_feature
|
41
|
+
elsif (f = to_feature_without_caching(feature_or_symbol))
|
42
|
+
feature_cache.write(f.symbol, f, :expires_in => cache_ttl)
|
43
|
+
f
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
# Quack like a Rails cache.
|
50
|
+
class Cache
|
51
|
+
def initialize
|
52
|
+
@data = {} # of the form {key => [value, expires_at or nil]}
|
53
|
+
end
|
54
|
+
def read(name, options = nil)
|
55
|
+
name = name.to_s
|
56
|
+
value, expires_at = *@data[name]
|
57
|
+
if value && (expires_at.blank? || expires_at > Time.now)
|
58
|
+
value
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
def write(name, value, options = nil)
|
64
|
+
name = name.to_s
|
65
|
+
expires_at = if options && options.respond_to?(:[]) && options[:expires_in]
|
66
|
+
Time.now + options.delete(:expires_in)
|
67
|
+
else
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
value.freeze.tap do |val|
|
71
|
+
@data[name] = [value, expires_at]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
def clear
|
75
|
+
@data.clear
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arturo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 1
|
7
|
-
- 1
|
8
|
-
- 3
|
9
|
-
version: 1.1.3
|
4
|
+
prerelease:
|
5
|
+
version: 1.2.0
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- James A. Rosen
|
@@ -14,7 +10,7 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date:
|
13
|
+
date: 2011-04-05 00:00:00 -07:00
|
18
14
|
default_executable:
|
19
15
|
dependencies:
|
20
16
|
- !ruby/object:Gem::Dependency
|
@@ -25,9 +21,6 @@ dependencies:
|
|
25
21
|
requirements:
|
26
22
|
- - ~>
|
27
23
|
- !ruby/object:Gem::Version
|
28
|
-
segments:
|
29
|
-
- 3
|
30
|
-
- 0
|
31
24
|
version: "3.0"
|
32
25
|
type: :runtime
|
33
26
|
version_requirements: *id001
|
@@ -39,8 +32,6 @@ dependencies:
|
|
39
32
|
requirements:
|
40
33
|
- - ">="
|
41
34
|
- !ruby/object:Gem::Version
|
42
|
-
segments:
|
43
|
-
- 0
|
44
35
|
version: "0"
|
45
36
|
type: :development
|
46
37
|
version_requirements: *id002
|
@@ -52,8 +43,6 @@ dependencies:
|
|
52
43
|
requirements:
|
53
44
|
- - ">="
|
54
45
|
- !ruby/object:Gem::Version
|
55
|
-
segments:
|
56
|
-
- 0
|
57
46
|
version: "0"
|
58
47
|
type: :development
|
59
48
|
version_requirements: *id003
|
@@ -65,9 +54,6 @@ dependencies:
|
|
65
54
|
requirements:
|
66
55
|
- - ~>
|
67
56
|
- !ruby/object:Gem::Version
|
68
|
-
segments:
|
69
|
-
- 1
|
70
|
-
- 2
|
71
57
|
version: "1.2"
|
72
58
|
type: :development
|
73
59
|
version_requirements: *id004
|
@@ -79,9 +65,6 @@ dependencies:
|
|
79
65
|
requirements:
|
80
66
|
- - ~>
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
segments:
|
83
|
-
- 1
|
84
|
-
- 3
|
85
68
|
version: "1.3"
|
86
69
|
type: :development
|
87
70
|
version_requirements: *id005
|
@@ -93,12 +76,20 @@ dependencies:
|
|
93
76
|
requirements:
|
94
77
|
- - ~>
|
95
78
|
- !ruby/object:Gem::Version
|
96
|
-
segments:
|
97
|
-
- 1
|
98
|
-
- 3
|
99
79
|
version: "1.3"
|
100
80
|
type: :development
|
101
81
|
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: timecop
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0.3"
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id007
|
102
93
|
description: Deploy features incrementally to your users
|
103
94
|
email: james.a.rosen@gmail.com
|
104
95
|
executables: []
|
@@ -111,6 +102,7 @@ files:
|
|
111
102
|
- lib/arturo/controller_filters.rb
|
112
103
|
- lib/arturo/engine.rb
|
113
104
|
- lib/arturo/feature_availability.rb
|
105
|
+
- lib/arturo/feature_caching.rb
|
114
106
|
- lib/arturo/feature_factories.rb
|
115
107
|
- lib/arturo/feature_management.rb
|
116
108
|
- lib/arturo/special_handling.rb
|
@@ -155,21 +147,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
155
147
|
requirements:
|
156
148
|
- - ">="
|
157
149
|
- !ruby/object:Gem::Version
|
158
|
-
segments:
|
159
|
-
- 0
|
160
150
|
version: "0"
|
161
151
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
152
|
none: false
|
163
153
|
requirements:
|
164
154
|
- - ">="
|
165
155
|
- !ruby/object:Gem::Version
|
166
|
-
segments:
|
167
|
-
- 0
|
168
156
|
version: "0"
|
169
157
|
requirements: []
|
170
158
|
|
171
159
|
rubyforge_project:
|
172
|
-
rubygems_version: 1.
|
160
|
+
rubygems_version: 1.6.1
|
173
161
|
signing_key:
|
174
162
|
specification_version: 2
|
175
163
|
summary: Feature sliders, wrapped up in an engine
|