sapling 0.1.1 → 0.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
@@ -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