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.
@@ -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,7 @@
1
+ module PinballWizard
2
+ class NullFeature < Feature
3
+ def determine_state
4
+ disable 'Feature not found'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'pinball_wizard/view_helpers/rails'
2
+
3
+ module PinballWizard
4
+ class Railtie < Rails::Railtie
5
+ initializer "pinball_wizard.action_view.add_pinball_helpers" do
6
+ ActionView::Base.send :include, PinballWizard::ViewHelpers::Rails
7
+ end
8
+ end
9
+ 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,3 @@
1
+ module PinballWizard
2
+ VERSION = '0.3.0'
3
+ 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