experiment_light 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +6 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +0 -2
- data/README.md +22 -1
- data/experiment_light.gemspec +4 -0
- data/lib/experiment_light.rb +1 -0
- data/lib/experiment_light/action_controller/base.rb +5 -4
- data/lib/experiment_light/active_record/base.rb +1 -11
- data/lib/experiment_light/experiment.rb +55 -14
- data/lib/experiment_light/helper_methods.rb +13 -0
- data/lib/experiment_light/version.rb +1 -1
- data/lib/generators/experiment_light/install_generator.rb +1 -1
- data/lib/generators/experiment_light/install_toggle_generator.rb +19 -0
- data/lib/generators/templates/experiments_controller.rb +13 -0
- data/lib/generators/templates/index.html.erb +37 -0
- data/spec/config/experiment.yml +9 -0
- data/spec/experiment_light_spec.rb +91 -0
- data/spec/spec_helper.rb +33 -0
- metadata +58 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 321c9146d36b78ece9250963412a581b6192e6db
|
4
|
+
data.tar.gz: b768c6980f76478ef05f3d2df343d3cd189a9621
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 278303702d11befe8cf853813b297850f53a84be2525c3e0b99698961b1f010e791278c17435fec5dccc6129adb153d3935d427104cbe718030e428da1af3d8b
|
7
|
+
data.tar.gz: 4d99cfbb06591974c319268e74e08e0d743fdc36e6934b8f5971f4fdf583e78a6fe497f3741250bb0a3537f1fec7fd7a8d9253abdd45c3b95c4bc11912228e7a
|
data/.codeclimate.yml
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# ExperimentLight
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/Sen-Zhang/experiment_light)
|
4
|
+
[](https://travis-ci.org/Sen-Zhang/experiment_light)
|
2
5
|
|
3
6
|
Turn features on or off in various rails environments
|
4
7
|
|
@@ -20,6 +23,8 @@ Or install it yourself as:
|
|
20
23
|
|
21
24
|
## Usage
|
22
25
|
|
26
|
+
### Basic
|
27
|
+
|
23
28
|
Run install generator:
|
24
29
|
|
25
30
|
$ rails generate experiment_light:install
|
@@ -78,6 +83,22 @@ And in views like this:
|
|
78
83
|
<p>Experiment bar is off</p>
|
79
84
|
<% end %>
|
80
85
|
|
86
|
+
### Toggel feature from view
|
87
|
+
|
88
|
+
Run install generator:
|
89
|
+
|
90
|
+
$ rails generate experiment_light:install_toggle
|
91
|
+
|
92
|
+
- A controller file named `experiments_controller.rb` will be added into `app/controllers`
|
93
|
+
- A view template file name 'index.html.erb' will be added into `app/views/experiments`
|
94
|
+
- The following two routes will be added:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
get 'experiments' => 'experiments#index'
|
98
|
+
post 'update_experiment' => 'experiments#update'
|
99
|
+
```
|
100
|
+
You may want to customize the newly generated files base on your need. Now you are ready to go to `localhost:3000/experiments` to check out and toggle your features.
|
101
|
+
|
81
102
|
## Contributing
|
82
103
|
|
83
104
|
1. Fork it ( https://github.com/[my-github-username]/experiment_light/fork )
|
data/experiment_light.gemspec
CHANGED
@@ -18,6 +18,10 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency "jquery-rails"
|
22
|
+
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.7"
|
22
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rails", "~> 4.2.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.3.0"
|
23
27
|
end
|
data/lib/experiment_light.rb
CHANGED
@@ -2,13 +2,14 @@ require 'action_controller'
|
|
2
2
|
|
3
3
|
module ExperimentLight
|
4
4
|
class ActionController::Base
|
5
|
+
include ExperimentLight::HelperMethods
|
5
6
|
|
6
|
-
def
|
7
|
-
ExperimentLight::Experiment.
|
7
|
+
def experiment_data
|
8
|
+
ExperimentLight::Experiment.experiment_feature_data
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
-
|
11
|
+
def toggle_feature(experiment, env, value)
|
12
|
+
ExperimentLight::Experiment.update(experiment, env, value)
|
12
13
|
end
|
13
14
|
|
14
15
|
helper_method :experiment_on?, :experiment_off?
|
@@ -2,16 +2,6 @@ require 'active_record'
|
|
2
2
|
|
3
3
|
module ExperimentLight
|
4
4
|
class ActiveRecord::Base
|
5
|
-
|
6
|
-
class << self
|
7
|
-
def experiment_on?(experiment_name)
|
8
|
-
ExperimentLight::Experiment.on?(experiment_name)
|
9
|
-
end
|
10
|
-
|
11
|
-
def experiment_off?(experiment_name)
|
12
|
-
!experiment_on?(experiment_name)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
5
|
+
extend ExperimentLight::HelperMethods
|
16
6
|
end
|
17
7
|
end
|
@@ -1,37 +1,78 @@
|
|
1
1
|
class ExperimentLight::Experiment
|
2
2
|
class << self
|
3
|
-
def
|
3
|
+
def experiment_feature_data
|
4
|
+
@experiment_data ||= load(experiment_file_name)
|
5
|
+
end
|
6
|
+
|
7
|
+
def on?(experiment, env=Rails.env)
|
4
8
|
experiment_feature_data
|
5
9
|
|
6
|
-
|
10
|
+
@experiment_data[experiment][env] if valid_feature_name_and_env?(experiment, env)
|
11
|
+
end
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
def off?(experiment, env=Rails.env)
|
14
|
+
!on?(experiment, env)
|
15
|
+
end
|
16
|
+
|
17
|
+
def update(experiment, env, value)
|
18
|
+
experiment_feature_data
|
19
|
+
|
20
|
+
@experiment_data[experiment][env] = value if valid_feature_name_and_env?(experiment, env)
|
11
21
|
|
12
|
-
|
22
|
+
save_to_yaml
|
13
23
|
end
|
14
24
|
|
15
|
-
def
|
16
|
-
|
25
|
+
def delete(experiment)
|
26
|
+
experiment_feature_data
|
27
|
+
|
28
|
+
@experiment_data.delete(experiment) if valid_feature_name?(experiment)
|
29
|
+
|
30
|
+
save_to_yaml
|
17
31
|
end
|
18
32
|
|
19
33
|
private
|
20
|
-
def
|
34
|
+
def experiment_file_name
|
21
35
|
'experiment.yml'
|
22
36
|
end
|
23
37
|
|
38
|
+
def file_path(file_name)
|
39
|
+
Rails.root.join('config', file_name)
|
40
|
+
end
|
41
|
+
|
24
42
|
def load(file_name)
|
25
|
-
|
26
|
-
HashWithIndifferentAccess.new(YAML::load(File.open(file_path)))
|
43
|
+
HashWithIndifferentAccess.new(YAML::load(File.open(file_path(file_name))))
|
27
44
|
end
|
28
45
|
|
29
46
|
def reload!
|
30
|
-
@experiment_data = load(
|
47
|
+
@experiment_data = load(experiment_file_name)
|
31
48
|
end
|
32
49
|
|
33
|
-
def
|
34
|
-
|
50
|
+
def save_to_yaml
|
51
|
+
File.open(file_path(experiment_file_name), 'w') do |f|
|
52
|
+
f.puts @experiment_data.to_hash.to_yaml
|
53
|
+
end
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def valid_feature_name?(experiment)
|
59
|
+
reload! unless @experiment_data.has_key?(experiment)
|
60
|
+
|
61
|
+
unless @experiment_data.has_key?(experiment)
|
62
|
+
raise "Cannot find experiment '#{experiment}', check out your #{experiment_file_name} file"
|
63
|
+
end
|
64
|
+
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def valid_feature_name_and_env?(experiment, env=Rails.env)
|
69
|
+
valid_feature_name?(experiment)
|
70
|
+
|
71
|
+
unless @experiment_data[experiment].has_key?(env)
|
72
|
+
raise "Cannot find environment '#{env}' for experiment '#{experiment}', check out your #{experiment_file_name} file"
|
73
|
+
end
|
74
|
+
|
75
|
+
true
|
35
76
|
end
|
36
77
|
end
|
37
78
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module ExperimentLight
|
4
|
+
module Generators
|
5
|
+
class InstallToggleGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../../templates', __FILE__)
|
7
|
+
|
8
|
+
desc 'Copy experiment_sample.yml files to your application.'
|
9
|
+
|
10
|
+
def copy_initializer
|
11
|
+
copy_file 'experiments_controller.rb', 'app/controllers/experiments_controller.rb'
|
12
|
+
copy_file 'index.html.erb', 'app/views/experiments/index.html.erb'
|
13
|
+
|
14
|
+
route "get 'experiments' => 'experiments#index'"
|
15
|
+
route "post 'update_experiment' => 'experiments#update'"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<h2>Experiment Features</h2>
|
2
|
+
|
3
|
+
<% @experiments.each do |feature, status| %>
|
4
|
+
<h4><%= feature %></h4>
|
5
|
+
|
6
|
+
<% status.each do |env, value| %>
|
7
|
+
<div>
|
8
|
+
<%= check_box_tag "#{feature}_#{env}", 1, value,
|
9
|
+
class: 'js-feature-checkbox', data: {url: update_experiment_path(name: feature, env: env)} %>
|
10
|
+
<%= label_tag "#{feature}_#{env}", env %>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<script>
|
16
|
+
'use strict';
|
17
|
+
|
18
|
+
$(function () {
|
19
|
+
$('.js-feature-checkbox').on('change', function () {
|
20
|
+
var checkBox = $(this),
|
21
|
+
url = checkBox.data('url'),
|
22
|
+
value = checkBox.is(':checked') ? true : false;
|
23
|
+
|
24
|
+
$.ajax({
|
25
|
+
url: url,
|
26
|
+
type: 'POST',
|
27
|
+
dataType: 'json',
|
28
|
+
data: {value: value}
|
29
|
+
}).done(function () {
|
30
|
+
alert('Feature toggle succeed!')
|
31
|
+
}).fail(function () {
|
32
|
+
alert('Feature toggle failed!')
|
33
|
+
})
|
34
|
+
});
|
35
|
+
});
|
36
|
+
|
37
|
+
</script>
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ExperimentLight' do
|
4
|
+
describe 'Experiment' do
|
5
|
+
it 'on? works fine' do
|
6
|
+
allow(Rails).to receive(:env).and_return('development')
|
7
|
+
|
8
|
+
expect(ExperimentLight::Experiment.on?(:foo)).to be_truthy
|
9
|
+
expect(ExperimentLight::Experiment.on?(:bar)).to be_truthy
|
10
|
+
|
11
|
+
allow(Rails).to receive(:env).and_return('test')
|
12
|
+
|
13
|
+
expect(ExperimentLight::Experiment.on?(:foo)).to be_truthy
|
14
|
+
expect(ExperimentLight::Experiment.on?(:bar)).to be_falsey
|
15
|
+
|
16
|
+
allow(Rails).to receive(:env).and_return('production')
|
17
|
+
|
18
|
+
expect(ExperimentLight::Experiment.on?(:foo)).to be_falsey
|
19
|
+
expect(ExperimentLight::Experiment.on?(:bar)).to be_truthy
|
20
|
+
|
21
|
+
expect(ExperimentLight::Experiment.on?(:foo, :development)).to be_truthy
|
22
|
+
expect(ExperimentLight::Experiment.on?(:bar, :development)).to be_truthy
|
23
|
+
|
24
|
+
expect(ExperimentLight::Experiment.on?(:foo, :test)).to be_truthy
|
25
|
+
expect(ExperimentLight::Experiment.on?(:bar, :test)).to be_falsey
|
26
|
+
|
27
|
+
expect(ExperimentLight::Experiment.on?(:foo, :production)).to be_falsey
|
28
|
+
expect(ExperimentLight::Experiment.on?(:bar, :production)).to be_truthy
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'off? works fine' do
|
32
|
+
allow(Rails).to receive(:env).and_return('development')
|
33
|
+
|
34
|
+
expect(ExperimentLight::Experiment.off?(:foo)).to be_falsey
|
35
|
+
expect(ExperimentLight::Experiment.off?(:bar)).to be_falsey
|
36
|
+
|
37
|
+
allow(Rails).to receive(:env).and_return('test')
|
38
|
+
|
39
|
+
expect(ExperimentLight::Experiment.off?(:foo)).to be_falsey
|
40
|
+
expect(ExperimentLight::Experiment.off?(:bar)).to be_truthy
|
41
|
+
|
42
|
+
allow(Rails).to receive(:env).and_return('production')
|
43
|
+
|
44
|
+
expect(ExperimentLight::Experiment.off?(:foo)).to be_truthy
|
45
|
+
expect(ExperimentLight::Experiment.off?(:bar)).to be_falsey
|
46
|
+
|
47
|
+
expect(ExperimentLight::Experiment.off?(:foo, :development)).to be_falsey
|
48
|
+
expect(ExperimentLight::Experiment.off?(:bar, :development)).to be_falsey
|
49
|
+
|
50
|
+
expect(ExperimentLight::Experiment.off?(:foo, :test)).to be_falsey
|
51
|
+
expect(ExperimentLight::Experiment.off?(:bar, :test)).to be_truthy
|
52
|
+
|
53
|
+
expect(ExperimentLight::Experiment.off?(:foo, :production)).to be_truthy
|
54
|
+
expect(ExperimentLight::Experiment.off?(:bar, :production)).to be_falsey
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'update works fine' do
|
58
|
+
expect(ExperimentLight::Experiment.on?(:foo, :development)).to be_truthy
|
59
|
+
|
60
|
+
ExperimentLight::Experiment.update(:foo, :development, false)
|
61
|
+
|
62
|
+
expect(ExperimentLight::Experiment.off?(:foo, :development)).to be_truthy
|
63
|
+
|
64
|
+
expect(ExperimentLight::Experiment.off?(:bar, :test)).to be_truthy
|
65
|
+
|
66
|
+
ExperimentLight::Experiment.update(:bar, :test, true)
|
67
|
+
|
68
|
+
expect(ExperimentLight::Experiment.on?(:bar, :test)).to be_truthy
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'delete works fine' do
|
72
|
+
expect(ExperimentLight::Experiment.on?(:foo, :development)).to be_truthy
|
73
|
+
|
74
|
+
ExperimentLight::Experiment.delete(:foo)
|
75
|
+
|
76
|
+
expect {
|
77
|
+
ExperimentLight::Experiment.on?(:foo, :development)
|
78
|
+
}.to raise_error(RuntimeError, "Cannot find experiment 'foo', check out your experiment.yml file")
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'raise errors correctly' do
|
82
|
+
expect {
|
83
|
+
ExperimentLight::Experiment.update(:foobar, :development, false)
|
84
|
+
}.to raise_error(RuntimeError, "Cannot find experiment 'foobar', check out your experiment.yml file")
|
85
|
+
|
86
|
+
expect {
|
87
|
+
ExperimentLight::Experiment.update(:foo, :dev, false)
|
88
|
+
}.to raise_error(RuntimeError, "Cannot find environment 'dev' for experiment 'foo', check out your experiment.yml file")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'experiment_light'
|
3
|
+
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
8
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
9
|
+
config.order = :random
|
10
|
+
|
11
|
+
config.before :each do
|
12
|
+
# reset experiment.yml
|
13
|
+
File.open('spec/config/experiment.yml', 'w') do |f|
|
14
|
+
init_hash = HashWithIndifferentAccess.new({
|
15
|
+
foo: {
|
16
|
+
development: true,
|
17
|
+
test: true,
|
18
|
+
production: false
|
19
|
+
},
|
20
|
+
bar: {
|
21
|
+
development: true,
|
22
|
+
test: false,
|
23
|
+
production: true
|
24
|
+
}
|
25
|
+
})
|
26
|
+
|
27
|
+
f.puts init_hash.to_hash.to_yaml
|
28
|
+
end
|
29
|
+
|
30
|
+
allow(ExperimentLight::Experiment).to receive(:file_path).and_return('spec/config/experiment.yml')
|
31
|
+
ExperimentLight::Experiment.send(:reload!)
|
32
|
+
end
|
33
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: experiment_light
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sen-Zhang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jquery-rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,6 +52,34 @@ dependencies:
|
|
38
52
|
- - ~>
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.2.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.2.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.3.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.3.0
|
41
83
|
description: Turn features on or off in various rails environments
|
42
84
|
email:
|
43
85
|
- solowolf21@gmail.com
|
@@ -45,7 +87,10 @@ executables: []
|
|
45
87
|
extensions: []
|
46
88
|
extra_rdoc_files: []
|
47
89
|
files:
|
90
|
+
- .codeclimate.yml
|
48
91
|
- .gitignore
|
92
|
+
- .rspec
|
93
|
+
- .travis.yml
|
49
94
|
- Gemfile
|
50
95
|
- LICENSE.txt
|
51
96
|
- README.md
|
@@ -55,9 +100,16 @@ files:
|
|
55
100
|
- lib/experiment_light/action_controller/base.rb
|
56
101
|
- lib/experiment_light/active_record/base.rb
|
57
102
|
- lib/experiment_light/experiment.rb
|
103
|
+
- lib/experiment_light/helper_methods.rb
|
58
104
|
- lib/experiment_light/version.rb
|
59
105
|
- lib/generators/experiment_light/install_generator.rb
|
106
|
+
- lib/generators/experiment_light/install_toggle_generator.rb
|
60
107
|
- lib/generators/templates/experiment_sample.yml
|
108
|
+
- lib/generators/templates/experiments_controller.rb
|
109
|
+
- lib/generators/templates/index.html.erb
|
110
|
+
- spec/config/experiment.yml
|
111
|
+
- spec/experiment_light_spec.rb
|
112
|
+
- spec/spec_helper.rb
|
61
113
|
homepage: https://github.com/Sen-Zhang/experiment_light.git
|
62
114
|
licenses:
|
63
115
|
- MIT
|
@@ -82,4 +134,7 @@ rubygems_version: 2.4.4
|
|
82
134
|
signing_key:
|
83
135
|
specification_version: 4
|
84
136
|
summary: Feature Toggles for Rails
|
85
|
-
test_files:
|
137
|
+
test_files:
|
138
|
+
- spec/config/experiment.yml
|
139
|
+
- spec/experiment_light_spec.rb
|
140
|
+
- spec/spec_helper.rb
|