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 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: 91
4
+ hash: 95
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
9
  - 3
10
- - 4
11
- version: 0.2.3.4
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: 2010-11-04 00:00:00 -07:00
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