arturo 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: false
5
- segments:
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: 2010-12-19 00:00:00 -08:00
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.3.7
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