arturo 0.2.3.4 → 0.2.3.6
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
- data/lib/arturo/test_support.rb +30 -0
- metadata +21 -4
data/README.md
CHANGED
@@ -256,6 +256,20 @@ The latter can be used like so:
|
|
256
256
|
widgets
|
257
257
|
end
|
258
258
|
|
259
|
+
#### Outside a Controller
|
260
|
+
|
261
|
+
If you want to check availability outside of a controller or view (really
|
262
|
+
outside of something that has `Arturo::FeatureAvailability` mixed in), you
|
263
|
+
can ask either
|
264
|
+
|
265
|
+
Arturo.feature_enabled_for?(:foo, recipient)
|
266
|
+
|
267
|
+
or the slightly fancier
|
268
|
+
|
269
|
+
Arturo.foo_enabled_for?(recipient)
|
270
|
+
|
271
|
+
Both check whether the `foo` feature exists and is enabled for `recipient`.
|
272
|
+
|
259
273
|
#### Caching
|
260
274
|
|
261
275
|
**Note**: Arturo does not yet have caching support. Be very careful when
|
data/lib/arturo.rb
CHANGED
@@ -3,8 +3,35 @@ 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/range_form_support'
|
8
9
|
require 'arturo/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 2 && Rails::VERSION::MINOR == 3
|
9
10
|
|
11
|
+
class <<self
|
12
|
+
|
13
|
+
# Quick check for whether a feature is enabled for a recipient.
|
14
|
+
# @param [String, Symbol] feature_name
|
15
|
+
# @param [#id] recipient
|
16
|
+
# @return [true,false] whether the feature exists and is enabled for the recipient
|
17
|
+
def feature_enabled_for?(feature_name, recipient)
|
18
|
+
f = self::Feature.to_feature(feature_name)
|
19
|
+
f && f.enabled_for?(recipient)
|
20
|
+
end
|
21
|
+
|
22
|
+
ENABLED_FOR_METHOD_NAME = /^(\w+)_enabled_for\?$/
|
23
|
+
|
24
|
+
def respond_to?(symbol)
|
25
|
+
symbol.to_s =~ ENABLED_FOR_METHOD_NAME || super(symbol)
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(symbol, *args, &block)
|
29
|
+
if (args.length == 1 && match = ENABLED_FOR_METHOD_NAME.match(symbol.to_s))
|
30
|
+
feature_enabled_for?(match[1], args[0])
|
31
|
+
else
|
32
|
+
super(symbol, *args, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
10
37
|
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
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Arturo.class_eval do
|
2
|
+
|
3
|
+
# Enable a feature; create it if necessary.
|
4
|
+
# For use in testing. Not auto-required on load. To load,
|
5
|
+
#
|
6
|
+
# require 'arturo/test_support'
|
7
|
+
#
|
8
|
+
# @param [Symbol, String] name the feature name
|
9
|
+
def enable_feature!(name)
|
10
|
+
feature = Arturo::Feature.find_by_symbol(name)
|
11
|
+
if feature
|
12
|
+
feature.update_attributes(:deployment_percentage => 100)
|
13
|
+
else
|
14
|
+
Arturo::Feature.create(:symbol => name, :deployment_percentage => 100)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Disable a feature if it exists.
|
19
|
+
# For use in testing. Not auto-required on load. To load,
|
20
|
+
#
|
21
|
+
# require 'arturo/test_support'
|
22
|
+
#
|
23
|
+
# @param [Symbol, String] name the feature name
|
24
|
+
def disable_feature!(name)
|
25
|
+
if (feature = Arturo::Feature.find_by_symbol(name))
|
26
|
+
feature.update_attributes(:deployment_percentage => 0)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arturo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 95
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
9
|
- 3
|
10
|
-
-
|
11
|
-
version: 0.2.3.
|
10
|
+
- 6
|
11
|
+
version: 0.2.3.6
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- James A. Rosen
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date:
|
19
|
+
date: 2011-04-05 00:00:00 -07:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -108,6 +108,21 @@ dependencies:
|
|
108
108
|
version: "1.3"
|
109
109
|
type: :development
|
110
110
|
version_requirements: *id006
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: timecop
|
113
|
+
prerelease: false
|
114
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ~>
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 13
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
- 3
|
123
|
+
version: "0.3"
|
124
|
+
type: :development
|
125
|
+
version_requirements: *id007
|
111
126
|
description: Deploy features incrementally to your users
|
112
127
|
email: james.a.rosen@gmail.com
|
113
128
|
executables: []
|
@@ -120,10 +135,12 @@ files:
|
|
120
135
|
- lib/arturo/controller_filters.rb
|
121
136
|
- lib/arturo/engine.rb
|
122
137
|
- lib/arturo/feature_availability.rb
|
138
|
+
- lib/arturo/feature_caching.rb
|
123
139
|
- lib/arturo/feature_factories.rb
|
124
140
|
- lib/arturo/feature_management.rb
|
125
141
|
- lib/arturo/range_form_support.rb
|
126
142
|
- lib/arturo/special_handling.rb
|
143
|
+
- lib/arturo/test_support.rb
|
127
144
|
- lib/arturo.rb
|
128
145
|
- lib/generators/arturo/arturo_generator.rb
|
129
146
|
- lib/generators/arturo/templates/arturo.css
|