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 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