pinball_wizard 0.0.1.pre → 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 +5 -13
- data/.gitignore +9 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +32 -0
- data/LICENSE +21 -0
- data/README.md +336 -0
- data/Rakefile +4 -0
- data/bower.json +14 -0
- data/dist/css_tagger.js +42 -0
- data/dist/css_tagger.min.js +3 -0
- data/dist/pinball_wizard.js +227 -0
- data/gulpfile.js +40 -0
- data/karma.conf.js +66 -0
- data/lib/pinball_wizard.rb +5 -0
- data/lib/pinball_wizard/configuration.rb +17 -0
- data/lib/pinball_wizard/dsl.rb +38 -0
- data/lib/pinball_wizard/feature.rb +52 -0
- data/lib/pinball_wizard/helpers/hash.rb +26 -0
- data/lib/pinball_wizard/null_feature.rb +7 -0
- data/lib/pinball_wizard/railtie.rb +9 -0
- data/lib/pinball_wizard/registry.rb +44 -0
- data/lib/pinball_wizard/version.rb +3 -0
- data/lib/pinball_wizard/view_helpers/rails.rb +13 -0
- data/lib/pinball_wizard/view_helpers/sinatra/slim.rb +15 -0
- data/package.json +45 -0
- data/pinball_wizard.gemspec +20 -0
- data/spec/pinball_wizard/dsl_spec.rb +73 -0
- data/spec/pinball_wizard/feature_spec.rb +89 -0
- data/spec/pinball_wizard/registry_spec.rb +56 -0
- data/src/css_tagger.coffee +44 -0
- data/src/pinball_wizard.coffee +157 -0
- data/test/spec/css_tagger_spec.coffee +50 -0
- data/test/spec/pinball_wizard_spec.coffee +268 -0
- data/test/test-main.coffee +25 -0
- metadata +47 -10
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'pinball_wizard/helpers/hash'
|
2
|
+
|
3
|
+
module PinballWizard
|
4
|
+
class Feature
|
5
|
+
attr_reader :name, :active, :options
|
6
|
+
attr_reader :disabled, :message
|
7
|
+
|
8
|
+
def initialize(name, options = {})
|
9
|
+
@name = name.to_s
|
10
|
+
@options = options
|
11
|
+
@active = ensure_callable(options.fetch(:active, false))
|
12
|
+
@options = Helpers::Hash.without(options, :name, :active)
|
13
|
+
@disabled = false
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :to_s, :name
|
17
|
+
|
18
|
+
def active?
|
19
|
+
active.call
|
20
|
+
end
|
21
|
+
|
22
|
+
def disabled?
|
23
|
+
determine_state # Called here for Registry#disabled?
|
24
|
+
disabled
|
25
|
+
end
|
26
|
+
|
27
|
+
def disable(message = 'No reason given.')
|
28
|
+
@disabled = true
|
29
|
+
@message = message
|
30
|
+
end
|
31
|
+
|
32
|
+
def determine_state
|
33
|
+
# noop: use defaults
|
34
|
+
end
|
35
|
+
|
36
|
+
def state
|
37
|
+
if disabled?
|
38
|
+
"disabled: #{message}"
|
39
|
+
elsif active?
|
40
|
+
'active'
|
41
|
+
else
|
42
|
+
'inactive'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def ensure_callable(object)
|
49
|
+
object.respond_to?(:call) ? object : proc { object }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Further Reading: https://practicingruby.com/articles/ruby-and-the-singleton-pattern-dont-get-along
|
2
|
+
module PinballWizard
|
3
|
+
module Helpers
|
4
|
+
module Hash
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Convert [:a, :b, { c: 'd' }] => { a: true, b: true, c: 'd' }
|
8
|
+
def normalize_options(options)
|
9
|
+
options.each_with_object({}) do |opt, memo|
|
10
|
+
if opt.is_a?(::Hash)
|
11
|
+
memo.merge!(opt)
|
12
|
+
else
|
13
|
+
memo[opt] = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def without(hash, *keys)
|
19
|
+
keys.each do |key|
|
20
|
+
hash.delete(key)
|
21
|
+
end
|
22
|
+
hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'pinball_wizard/helpers/hash'
|
2
|
+
require 'pinball_wizard/null_feature'
|
3
|
+
|
4
|
+
# Further Reading: https://practicingruby.com/articles/ruby-and-the-singleton-pattern-dont-get-along
|
5
|
+
module PinballWizard
|
6
|
+
module Registry
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def add(feature)
|
10
|
+
collection[feature.name] = feature
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(name)
|
14
|
+
collection.fetch(name.to_s) { null_feature }
|
15
|
+
end
|
16
|
+
|
17
|
+
def disabled?(name)
|
18
|
+
get(name).disabled?
|
19
|
+
end
|
20
|
+
|
21
|
+
def collection
|
22
|
+
@collection ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear
|
26
|
+
@collection = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_h
|
30
|
+
pairs = collection.map do |name, feature|
|
31
|
+
[feature.to_s, feature.state]
|
32
|
+
end
|
33
|
+
::Hash[pairs]
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method :to_hash, :to_h
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def null_feature
|
41
|
+
@null_feature ||= NullFeature.new 'null'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PinballWizard
|
2
|
+
module ViewHelpers
|
3
|
+
module Rails
|
4
|
+
def feature(name, options = {})
|
5
|
+
unless PinballWizard::Registry.disabled?(name)
|
6
|
+
partial_name = options.fetch(:partial) { name }
|
7
|
+
options[:partial] = "features/#{partial_name}"
|
8
|
+
render options
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PinballWizard
|
2
|
+
module ViewHelpers
|
3
|
+
module Sinatra
|
4
|
+
module Slim
|
5
|
+
def feature(name, opts_or_locals = {})
|
6
|
+
unless PinballWizard::Registry.disabled?(name)
|
7
|
+
partial_name = opts_or_locals.fetch(:partial) { name }
|
8
|
+
locals = opts_or_locals.tap { |h| h.delete(:partial) }
|
9
|
+
partial "features/#{partial_name}", locals
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/package.json
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
{
|
2
|
+
"name": "pinball_wizard",
|
3
|
+
"version": "0.2.0",
|
4
|
+
"description": "pinball_wizard",
|
5
|
+
"homepage": "https://github.com/primedia/pinball_wizard",
|
6
|
+
"license": "MIT",
|
7
|
+
"author": {
|
8
|
+
"name": "RentPath"
|
9
|
+
},
|
10
|
+
"repository": {
|
11
|
+
"type": "git",
|
12
|
+
"url": "https://github.com/primedia/pinball_wizard.git"
|
13
|
+
},
|
14
|
+
"github": "https://github.com/primedia/pinball_wizard.git",
|
15
|
+
"devDependencies": {
|
16
|
+
"del": "^1.1.0",
|
17
|
+
"gulp": "^3.8.10",
|
18
|
+
"gulp-coffee": "^2.2.0",
|
19
|
+
"gulp-sourcemaps": "~1.2.0",
|
20
|
+
"gulp-util": "^3.0.1",
|
21
|
+
"karma": "~0.12.31",
|
22
|
+
"karma-jasmine": "~0.3.4",
|
23
|
+
"karma-cli": "0.0.4",
|
24
|
+
"karma-chrome-launcher": "~0.1.7",
|
25
|
+
"requirejs": "~2.1.15",
|
26
|
+
"karma-requirejs": "~0.2.2",
|
27
|
+
"gulp": "~3.8.10"
|
28
|
+
},
|
29
|
+
"scripts": {
|
30
|
+
"compile": "gulp build",
|
31
|
+
"build": "gulp dist",
|
32
|
+
"livereload": "gulp",
|
33
|
+
"watch": "gulp",
|
34
|
+
"watch:test": "karma start",
|
35
|
+
"test": "karma start --single-run",
|
36
|
+
"test:clean": "npm run clean && npm run compile && npm test",
|
37
|
+
"clean": "rm -rf .tmp",
|
38
|
+
"preclean:all": "npm run clean",
|
39
|
+
"clean:all": "rm -rf node_modules",
|
40
|
+
"prereset": "npm run clean:all",
|
41
|
+
"reset": "npm install",
|
42
|
+
"postreset": "npm run compile",
|
43
|
+
"preversion": "npm run reset && npm test && npm run build"
|
44
|
+
}
|
45
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/pinball_wizard/version', __FILE__)
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ["Caleb Wright", "Mark Herman", "RentPath Team"]
|
7
|
+
gem.email = ["cwright@rentpath.com", "markherman@rentpath.com"]
|
8
|
+
gem.homepage = 'https://github.com/primedia/pinball_wizard'
|
9
|
+
gem.description = 'Build flippable features.'
|
10
|
+
gem.summary = "Lib to build flippable features."
|
11
|
+
gem.date = Date.today.to_s
|
12
|
+
gem.licenses = ['MIT']
|
13
|
+
gem.executables = []
|
14
|
+
gem.files = `git ls-files | grep -v myapp`.split("\n")
|
15
|
+
gem.test_files = `git ls-files -- test/*`.split("\n")
|
16
|
+
gem.name = "pinball_wizard"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = PinballWizard::VERSION
|
19
|
+
gem.required_ruby_version = '>= 1.9'
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'pinball_wizard'
|
2
|
+
|
3
|
+
describe PinballWizard::DSL do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
PinballWizard::Registry.clear
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.build' do
|
10
|
+
context 'without builder class flags' do
|
11
|
+
before(:each) do
|
12
|
+
PinballWizard::DSL.build do
|
13
|
+
feature :example_a
|
14
|
+
feature :example_b, :active
|
15
|
+
feature :example_c, active: proc { false }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'adds to the registry' do
|
20
|
+
expect(PinballWizard::Registry.to_h).to eq({
|
21
|
+
'example_a' => 'inactive',
|
22
|
+
'example_b' => 'active',
|
23
|
+
'example_c' => 'inactive'
|
24
|
+
})
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'creates a Feature class instance' do
|
28
|
+
expect(PinballWizard::Registry.get('example_a')).to be_a(PinballWizard::Feature)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with a builder class flag' do
|
33
|
+
|
34
|
+
class MyCustomFeature < PinballWizard::Feature; end
|
35
|
+
|
36
|
+
let(:config) do
|
37
|
+
PinballWizard::Configuration.new my_custom_feature: MyCustomFeature
|
38
|
+
end
|
39
|
+
|
40
|
+
before(:each) do
|
41
|
+
PinballWizard::DSL.build(config) do
|
42
|
+
feature :example_a, :my_custom_feature
|
43
|
+
feature :example_b, :foo, my_custom_feature: { b: true }
|
44
|
+
feature :example_c
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'adds to the registry' do
|
49
|
+
expect(PinballWizard::Registry.to_h).to eq({
|
50
|
+
'example_a' => 'inactive',
|
51
|
+
'example_b' => 'inactive',
|
52
|
+
'example_c' => 'inactive'
|
53
|
+
})
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'creates a custom class instance when given a symbol' do
|
57
|
+
expect(PinballWizard::Registry.get('example_a')).to be_a(MyCustomFeature)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'creates a custom class instance when given a hash' do
|
61
|
+
expect(PinballWizard::Registry.get('example_b')).to be_a(MyCustomFeature)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'createa a Feature class instance for the non-specified' do
|
65
|
+
expect(PinballWizard::Registry.get('example_c')).to be_a(PinballWizard::Feature)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'passes on symbols and hash into Feature options' do
|
69
|
+
expect(PinballWizard::Registry.get('example_b').options).to eq({ foo: true, my_custom_feature: { b: true } })
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'pinball_wizard'
|
2
|
+
|
3
|
+
describe PinballWizard::Feature do
|
4
|
+
describe '#state' do
|
5
|
+
context 'with defaults' do
|
6
|
+
subject { PinballWizard::Feature.new 'example' }
|
7
|
+
|
8
|
+
it 'should not be active' do
|
9
|
+
expect(subject.state).to eq('inactive')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when setting active to a boolean' do
|
14
|
+
subject do
|
15
|
+
PinballWizard::Feature.new 'example', active: true
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should be activate' do
|
19
|
+
expect(subject.state).to eq('active')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when setting active to a proc' do
|
24
|
+
subject do
|
25
|
+
PinballWizard::Feature.new 'example', active: proc { true }
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should be activate' do
|
29
|
+
expect(subject.state).to eq('active')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when disabled' do
|
34
|
+
subject do
|
35
|
+
PinballWizard::Feature.new 'example'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should be disabled with a message' do
|
39
|
+
subject.disable 'reason'
|
40
|
+
expect(subject.state).to eq('disabled: reason')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#active?' do
|
46
|
+
context 'without setting a value' do
|
47
|
+
subject { PinballWizard::Feature.new 'example' }
|
48
|
+
|
49
|
+
it 'should not be active' do
|
50
|
+
expect(subject).not_to be_active
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with a boolean value' do
|
55
|
+
subject do
|
56
|
+
PinballWizard::Feature.new 'example', active: true
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should not be activated' do
|
60
|
+
expect(subject).to be_active
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with a proc' do
|
65
|
+
subject do
|
66
|
+
PinballWizard::Feature.new 'example', active: proc { true }
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should not be activated' do
|
70
|
+
expect(subject).to be_active
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#name' do
|
76
|
+
subject do
|
77
|
+
PinballWizard::Feature.new('my_super_duper').name
|
78
|
+
end
|
79
|
+
|
80
|
+
it { should eq('my_super_duper') }
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '.new' do
|
84
|
+
it 'takes a hash of options' do
|
85
|
+
feature = PinballWizard::Feature.new 'example', foo: 'bar'
|
86
|
+
expect(feature.options).to eq({ foo: 'bar' })
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'pinball_wizard'
|
2
|
+
|
3
|
+
describe PinballWizard::Registry do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
PinballWizard::Registry.clear
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:default_feature) do
|
10
|
+
PinballWizard::Feature.new 'default'
|
11
|
+
end
|
12
|
+
|
13
|
+
class DisabledFeature < PinballWizard::Feature
|
14
|
+
def determine_state
|
15
|
+
disable 'Reason'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:disabled_feature) do
|
20
|
+
DisabledFeature.new 'disabled'
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '.add' do
|
24
|
+
it 'should add to the registry with defaults' do
|
25
|
+
PinballWizard::Registry.add(default_feature)
|
26
|
+
expect(PinballWizard::Registry.collection).to eq({
|
27
|
+
'default' => default_feature
|
28
|
+
})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '.disabled?' do
|
33
|
+
it 'should be true for a disabled feature' do
|
34
|
+
PinballWizard::Registry.add(disabled_feature)
|
35
|
+
expect(PinballWizard::Registry.disabled?('disabled')).to eq(true)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should be true for a non-existant feature' do
|
39
|
+
expect(PinballWizard::Registry.disabled?('foo')).to eq(true)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should be false for a active feature' do
|
43
|
+
PinballWizard::Registry.add(default_feature)
|
44
|
+
expect(PinballWizard::Registry.disabled?('default')).to eq(false)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.to_h' do
|
49
|
+
it 'should build a hash' do
|
50
|
+
PinballWizard::Registry.add(default_feature)
|
51
|
+
expect(PinballWizard::Registry.to_h).to eq({
|
52
|
+
'default' => 'inactive'
|
53
|
+
})
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|