sapling 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -10,3 +10,111 @@ Heritage
10
10
 
11
11
  This is a port of the [rollout gem](https://github.com/jamesgolick/rollout) for
12
12
  use with ActiveRecord instead of Redis. We dropped the groups functionality, but otherwise we mirrored the API.
13
+
14
+ Setup
15
+ -----
16
+
17
+ Add a route to your routes file. Feel free to change the 'sapling/stylesheet.css' to whatever you want.
18
+
19
+ map.sapling_script 'sapling/script.js', :controller => 'sapling', :action => 'script'
20
+
21
+ After including your javascript library, add this:
22
+
23
+ <%= javascript_include_tag sapling_script_path, :id => 'sapling_script' %>
24
+
25
+ Run the `db/create.sql` file to create the sapling_settings table on your DB
26
+
27
+ Ensure you have a current_user method to your application controller, which returns a user object, or nil if no active user.
28
+
29
+ class ApplicationController < ActionController::Base
30
+ ...
31
+ def current_user
32
+ # Return your current user
33
+ # This is already done for you in Devise, RestfulAuthentication, and other authentication lib
34
+ end
35
+ ...
36
+ end
37
+
38
+
39
+
40
+ For a given feature `space_chat`,
41
+
42
+ Server-side Usage
43
+ -----------------
44
+
45
+ To check if a feature is enabled for a user in rails controllers or views: use
46
+
47
+ feature_active?(:space_chat [, :user => the current user])
48
+
49
+ *Note*: sapling will automatically populate the user argument by calling `current_user`
50
+
51
+ To enable a feature for a specific user, in the rails console:
52
+
53
+ Sapling::ActiveRecord.new.activate_user(:space_chat, space_admin)
54
+
55
+ To disable a feature for a specific user, in the rails console:
56
+
57
+ Sapling::ActiveRecord.new.deactivate_user(:space_chat, space_admin)
58
+
59
+ To enable a feature for 50% of the users, in the rails console:
60
+
61
+ Sapling::ActiveRecord.new.activate_percentage(:space_chat, 50)`
62
+
63
+ To disable a feature activated for anyone but individually-activated users, in the rails console:
64
+
65
+ Sapling::ActiveRecord.new.deactivate_percentage(:space_chat)
66
+
67
+ *Note*: Individually-activated users are always activated, regardless of the percentage setting. A deactivated user
68
+ may still have access to a feature if they fall within an active percentage.
69
+
70
+ Client-side Usage
71
+ -----------------
72
+
73
+ Sapling include a javascript helper which add and remove classes on the HTML element for a given user, based upon the
74
+ features available to that user.
75
+
76
+ Given the feature `space_chat`, if it is enabled for the given user, the html root will get an class `sapling_feature_space_chat_on`. If disabled for the given user, the class will be `sapling_feature_space_chat_off`.
77
+
78
+ To retrieve the class name for a given feature, you can use the `sapling_js_generator.css_container_class(feature)` and `sapling_js_generator.css_toggle_class(feature, on? (boolean) )` helpers in your ERB files.
79
+
80
+ Using the client-side tools, you can define CSS classes which will be visible/invisible when a feature is enabled/disabled. For example:
81
+
82
+ CSS:
83
+
84
+ /* Chat Defaults to being hidden for everyone */
85
+ .sapling_feature_chat.enabled {
86
+ display: none;
87
+ }
88
+ .sapling_feature_chat.disabled {
89
+ display: block;
90
+ }
91
+
92
+ /* When chat is enabled, we hide the disabled container, and show the enabled container */
93
+ html.sapling_feature_chat_on .sapling_feature_chat.enabled {
94
+ display: block;
95
+ }
96
+ html.sapling_feature_chat_on .sapling_feature_chat.disabled {
97
+ display: none;
98
+ }
99
+
100
+
101
+ ERB:
102
+
103
+ <div class="<%= feature_class(:chat) %> enabled">
104
+ REJOICE - SPIFF CHAT ENABLED FOR YOU
105
+ </div>
106
+
107
+ <div class="<%= feature_class(:chat) %> disabled">
108
+ DESPAIR - SPIFF CHAT NOT ENABLED FOR YOU
109
+ </div>
110
+
111
+
112
+ *Note* Currently, the javascript generated depends on MooTools being loaded first.
113
+
114
+ TODO
115
+ ----
116
+
117
+ * Rails 3 compatibility
118
+ * Remove mootools dependency
119
+ * CSS generator
120
+ * Database migration generator
@@ -0,0 +1,47 @@
1
+ require "set"
2
+
3
+ class Sapling::JavascriptGenerator
4
+ attr_accessor :sapling
5
+
6
+ def initialize(sapling)
7
+ @sapling=sapling
8
+ end
9
+
10
+ def prefix
11
+ "sapling_feature"
12
+ end
13
+
14
+ # Use these classes on the container elements of your features
15
+ def css_container_class(feature)
16
+ "#{prefix}_#{feature.to_s}"
17
+ end
18
+
19
+ # Put these on the html element to turn on/off features
20
+ def css_toggle_class(feature, on)
21
+ "#{css_container_class(feature)}_#{on ? 'on' : 'off'}"
22
+ end
23
+
24
+
25
+ # see Sapling::API::Client for options
26
+
27
+ def mootools_for_feature(feature, on)
28
+ "html.removeClass('" + css_toggle_class(feature,!on) + "');html.addClass('" + css_toggle_class(feature,on) + "');"
29
+ end
30
+
31
+ def generate(options={})
32
+ features = Set.new @sapling.features
33
+ active_features = Set.new @sapling.active_features options
34
+ inactive_features = features - active_features
35
+
36
+
37
+ <<-END
38
+ (function() {
39
+ if (window.MooTools) {
40
+ html = $$('html')[0];
41
+ #{inactive_features.map{|f| mootools_for_feature(f,false) }.join}
42
+ #{active_features.map{|f| mootools_for_feature(f,true) }.join}
43
+ }
44
+ })();
45
+ END
46
+ end
47
+ end
@@ -11,13 +11,13 @@ module Sapling::ActionControllerExt
11
11
  @@sapling ||= Sapling::ActiveRecord.new
12
12
  end
13
13
 
14
- def sapling_css_generator
15
- @@sapling_css_generator ||= Sapling::CssGenerator.new(sapling)
14
+ def sapling_js_generator
15
+ @@sapling_js_generator ||= Sapling::JavascriptGenerator.new(sapling)
16
16
  end
17
17
 
18
18
  end
19
19
 
20
20
  class ActionController::Base
21
21
  include Sapling::ActionControllerExt
22
- helper_method :feature_active?, :sapling, :sapling_css_generator
22
+ helper_method :feature_active?, :sapling, :sapling_js_generator
23
23
  end
@@ -1,7 +1,6 @@
1
- class SaplingController < ApplicationController
2
- def stylesheet
3
- styles = sapling_css_generator.to_s(:user => current_user, :context_id => (request.session_options[:id] if request.session))
4
- # styles = "boo"
5
- render :text => styles, :content_type => 'text/css'
1
+ class SaplingController < ApplicationController
2
+ def script
3
+ js = sapling_js_generator.generate(:user => current_user, :context_id => (request.session_options[:id] if request.session))
4
+ render :text => js, :content_type => 'text/javascript'
6
5
  end
7
6
  end
@@ -2,21 +2,9 @@ module Sapling
2
2
  module ViewHelpers
3
3
 
4
4
  # include ActionView::Helpers::CaptureHelper
5
- def feature_on(feature, &block)
6
- feature_block(sapling_css_generator.css_class(feature, "style=\"display:none;\"", true), &block)
5
+ def feature_class(feature)
6
+ sapling_js_generator.css_container_class(feature)
7
7
  end
8
-
9
- def feature_off(feature, &block)
10
- feature_block(sapling_css_generator.css_class(feature, nil, false), &block)
11
- end
12
-
13
- private
14
- def feature_block(css_class, params, &block)
15
- concat "<span class=\"#{css_class}\" #{params}>"
16
- block.call
17
- concat "</span>"
18
- end
19
-
8
+
20
9
  end
21
- end
22
-
10
+ end
@@ -1,3 +1,3 @@
1
1
  module Sapling
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/sapling.rb CHANGED
@@ -4,6 +4,7 @@ require "sapling/base"
4
4
  require "sapling/memory"
5
5
  require "sapling/model"
6
6
  require "sapling/active_record"
7
+ require "sapling/generators/javascript_generator"
7
8
  require "sapling/generators/css_generator"
8
9
 
9
10
  module Sapling
@@ -0,0 +1,61 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Sapling::JavascriptGenerator" do
4
+
5
+ before do
6
+ ActiveRecord::Base.establish_connection(
7
+ :adapter => 'sqlite3',
8
+ :database => ':memory:'
9
+ )
10
+ sql = File.read(File.expand_path(File.dirname(__FILE__) + '/../db/create.sql'))
11
+ ActiveRecord::Base.connection.execute sql
12
+ @sapling = Sapling::ActiveRecord.new
13
+ end
14
+
15
+ describe "creating basic js" do
16
+ before do
17
+ @sapling.activate_user(:chat, stub(:id => 1))
18
+ end
19
+
20
+ it "outputs js" do
21
+ Sapling::JavascriptGenerator.new(@sapling).generate(:user => stub(:id => 1)).should include 'html.addClass(\'sapling_feature_chat_on\');'
22
+ end
23
+ end
24
+
25
+ describe "creating more complex js" do
26
+ before do
27
+ @sapling.activate_percentage(:bicycle, 10)
28
+ @sapling.activate_user(:chat, stub(:id => 115))
29
+ @sapling.activate_user(:pwn, stub(:id => 102))
30
+ @sapling.activate_user(:juggle, stub(:id => 115))
31
+ end
32
+
33
+ it "test user bicycle & pwn user" do
34
+ output = Sapling::JavascriptGenerator.new(@sapling).generate(:user => stub(:id => 102))
35
+ expected_features = {
36
+ 'bicycle' => true,
37
+ 'chat' => false,
38
+ 'juggle' => false,
39
+ 'pwn' => true
40
+ }
41
+ expected_features.each_pair do |key, enabled|
42
+ output.should include "html.addClass('sapling_feature_#{key}_#{enabled ? 'on' : 'off'}');"
43
+ output.should include "html.removeClass('sapling_feature_#{key}_#{!enabled ? 'on' : 'off'}');"
44
+ end
45
+ end
46
+
47
+ it "test chat & juggle user" do
48
+ output = Sapling::JavascriptGenerator.new(@sapling).generate(:user => stub(:id => 115))
49
+ expected_features = {
50
+ 'bicycle' => false,
51
+ 'chat' => true,
52
+ 'juggle' => true,
53
+ 'pwn' => false
54
+ }
55
+ expected_features.each_pair do |key, enabled|
56
+ output.should include "html.addClass('sapling_feature_#{key}_#{enabled ? 'on' : 'off'}');"
57
+ output.should include "html.removeClass('sapling_feature_#{key}_#{!enabled ? 'on' : 'off'}');"
58
+ end
59
+ end
60
+ end
61
+ end
@@ -6,7 +6,9 @@
6
6
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
7
7
  <title>SpacemanSpiffs: <%= controller.action_name %></title>
8
8
  <%= stylesheet_link_tag 'scaffold' %>
9
- <%= stylesheet_link_tag sapling_stylesheet_path %>
9
+ <%= stylesheet_link_tag 'sapling_features' %>
10
+ <%= javascript_include_tag 'https://ajax.googleapis.com/ajax/libs/mootools/1.4.1/mootools.js' %>
11
+ <%= javascript_include_tag sapling_script_path %>
10
12
  </head>
11
13
  <body>
12
14
 
@@ -1,7 +1,7 @@
1
- <% feature_on(:chat) do %>
2
- SPIFF CHAT ENABLED
3
- <% end %>
1
+ <div class="<%= feature_class(:chat) %> enabled">
2
+ REJOICE - SPIFF CHAT ENABLED FOR YOU
3
+ </div>
4
4
 
5
- <% feature_off(:chat) do %>
6
- SPIFF CHAT FORSAKEN
7
- <% end %>
5
+ <div class="<%= feature_class(:chat) %> disabled">
6
+ DESPAIR - SPIFF CHAT NOT ENABLED FOR YOU
7
+ </div>
@@ -2,7 +2,7 @@ ActionController::Routing::Routes.draw do |map|
2
2
  map.resources :spaceman_spiffs, :collection => {:multiple_features => :get }
3
3
  map.resource :user_sessions, :collection => {:set_manually => :get }
4
4
 
5
- # map.sapling_stylesheet 'sapling/stylesheet.css', :controller => 'sapling', :action => 'stylesheet'
5
+ map.sapling_script 'sapling/script.js', :controller => 'sapling', :action => 'script'
6
6
 
7
7
  # The priority is based upon order of creation: first created -> highest priority.
8
8
 
@@ -0,0 +1,15 @@
1
+ /* Chat Defaults */
2
+ .sapling_feature_chat.enabled {
3
+ display: none;
4
+ }
5
+ .sapling_feature_chat.disabled {
6
+ display: block;
7
+ }
8
+
9
+ /* Chat Enabled */
10
+ html.sapling_feature_chat_on .sapling_feature_chat.enabled {
11
+ display: block;
12
+ }
13
+ html.sapling_feature_chat_on .sapling_feature_chat.disabled {
14
+ display: none;
15
+ }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sapling
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Shane Brinkman-Davis
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-12-13 00:00:00 -08:00
19
+ date: 2011-12-20 00:00:00 -08:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -117,6 +117,7 @@ files:
117
117
  - lib/sapling/api.rb
118
118
  - lib/sapling/base.rb
119
119
  - lib/sapling/generators/css_generator.rb
120
+ - lib/sapling/generators/javascript_generator.rb
120
121
  - lib/sapling/memory.rb
121
122
  - lib/sapling/model.rb
122
123
  - lib/sapling/rails.rb
@@ -130,6 +131,7 @@ files:
130
131
  - sapling.gemspec
131
132
  - spec/active_record_spec.rb
132
133
  - spec/css_generator_spec.rb
134
+ - spec/javascript_generator_spec.rb
133
135
  - spec/memory_feature_spec.rb
134
136
  - spec/memory_spec.rb
135
137
  - spec/rails_app/README
@@ -178,6 +180,7 @@ files:
178
180
  - spec/rails_app/public/javascripts/effects.js
179
181
  - spec/rails_app/public/javascripts/prototype.js
180
182
  - spec/rails_app/public/robots.txt
183
+ - spec/rails_app/public/stylesheets/sapling_features.css
181
184
  - spec/rails_app/public/stylesheets/scaffold.css
182
185
  - spec/rails_app/script/about
183
186
  - spec/rails_app/script/console
@@ -196,8 +199,6 @@ files:
196
199
  - spec/rails_app/test/performance/browsing_test.rb
197
200
  - spec/rails_app/test/test_helper.rb
198
201
  - spec/rails_app/test/unit/helpers/spaceman_spiffs_helper_test.rb
199
- - spec/rails_app/test/unit/spaceman_spiff_test.rb
200
- - spec/rails_app/test/unit/user_test.rb
201
202
  - spec/sapling_examples.rb
202
203
  - spec/spec.opts
203
204
  - spec/spec_helper.rb
@@ -238,6 +239,7 @@ summary: Incrementally roll out your features. Uses ActiveRecord to store config
238
239
  test_files:
239
240
  - spec/active_record_spec.rb
240
241
  - spec/css_generator_spec.rb
242
+ - spec/javascript_generator_spec.rb
241
243
  - spec/memory_feature_spec.rb
242
244
  - spec/memory_spec.rb
243
245
  - spec/rails_app/README
@@ -286,6 +288,7 @@ test_files:
286
288
  - spec/rails_app/public/javascripts/effects.js
287
289
  - spec/rails_app/public/javascripts/prototype.js
288
290
  - spec/rails_app/public/robots.txt
291
+ - spec/rails_app/public/stylesheets/sapling_features.css
289
292
  - spec/rails_app/public/stylesheets/scaffold.css
290
293
  - spec/rails_app/script/about
291
294
  - spec/rails_app/script/console
@@ -304,8 +307,6 @@ test_files:
304
307
  - spec/rails_app/test/performance/browsing_test.rb
305
308
  - spec/rails_app/test/test_helper.rb
306
309
  - spec/rails_app/test/unit/helpers/spaceman_spiffs_helper_test.rb
307
- - spec/rails_app/test/unit/spaceman_spiff_test.rb
308
- - spec/rails_app/test/unit/user_test.rb
309
310
  - spec/sapling_examples.rb
310
311
  - spec/spec.opts
311
312
  - spec/spec_helper.rb
@@ -1,8 +0,0 @@
1
- require 'test_helper'
2
-
3
- class SpacemanSpiffTest < ActiveSupport::TestCase
4
- # Replace this with your real tests.
5
- test "the truth" do
6
- assert true
7
- end
8
- end
@@ -1,8 +0,0 @@
1
- require 'test_helper'
2
-
3
- class UserTest < ActiveSupport::TestCase
4
- # Replace this with your real tests.
5
- test "the truth" do
6
- assert true
7
- end
8
- end