feature_flagger 0.1.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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 74316fc70e25aa5098d8e500a59b1a983dabff2d
4
+ data.tar.gz: 98f838a09eb04c89b5f5f36b4b3a84859de36b14
5
+ SHA512:
6
+ metadata.gz: f9b56d90d8bda18e090fb81b84387aef0c9bc053e09c758b2cbe3ebc38103b2a7208049c5bb822dfe036750ffde5ed4736341956234b8bb0bc4f083f342622e2
7
+ data.tar.gz: a00d2a9aac2e8ab2082f7ec6950a73f0d1653a9497ea0ee7b69adf15e8f0ee8311d82c0dd0e2fbcefc1437cf4618de0325432fb4e6f7a23db49b6567e8d4ae87
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.1.5
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rollout.gemspec
4
+ gemspec
@@ -0,0 +1,62 @@
1
+ # FeatureFlagger
2
+
3
+ Partial release your features.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'feature_flagger'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install feature_flagger
20
+
21
+ ## Usage
22
+
23
+ ### Rails
24
+
25
+ 1. Create a `rollout.yml` in _config_ path and declare a rollout:
26
+ ```yml
27
+ account: # model name
28
+ email_marketing: # namespace (optional)
29
+ new_email_flow: # feature key
30
+ description:
31
+ @dispatch team uses this rollout to introduce a new email flow for certains users. Read more at [link]
32
+ ```
33
+
34
+ 2. Adds rollout funcionality to your model:
35
+ ```ruby
36
+ class Account < ActiveRecord::Base
37
+ include FeatureFlagger::Model
38
+ # ....
39
+ end
40
+ ```
41
+
42
+
43
+ 3. Check;
44
+ ```ruby
45
+ class DashboardController < ApplicationController
46
+ def index
47
+ if current_user.account.rollout?([:email_marketing, :new_email_flow])
48
+ # render something
49
+ else
50
+ # render something else
51
+ end
52
+ end
53
+ end
54
+ ```
55
+
56
+ P.S: If you try to check a inexistent rollout key will raise an error.
57
+
58
+
59
+ ## Contributing
60
+
61
+ Bug reports and pull requests are welcome on GitHub at
62
+ https://github.com/ResultadosDigitais/feature_flagger.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rollout"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'feature_flagger/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "feature_flagger"
8
+ spec.version = FeatureFlagger::VERSION
9
+ spec.authors = ["Nando Sousa"]
10
+ spec.email = ["nandosousafr@gmail.com"]
11
+
12
+ spec.summary = %q{Write a short summary, because Rubygems requires one.}
13
+ spec.description = %q{Write a longer description or delete this line.}
14
+ spec.homepage = "http://github.com/ResultadosDigitais/feature_flagger"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'redis-namespace'
22
+ spec.add_development_dependency "bundler", "~> 1.12"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ end
@@ -0,0 +1,56 @@
1
+ require 'yaml'
2
+ require 'redis-namespace'
3
+
4
+ require 'feature_flagger/version'
5
+ require 'feature_flagger/control'
6
+ require 'feature_flagger/model'
7
+ require 'feature_flagger/feature'
8
+
9
+ module FeatureFlagger
10
+ DEFAULT_CONFIG = { redis_namespace: 'rollout-control' }
11
+
12
+ class << self
13
+ def configure(&block)
14
+ set_config
15
+ yield self if block_given?
16
+ end
17
+
18
+ # TODO: rename to just _config_.
19
+ def config
20
+ @@config
21
+ end
22
+
23
+ def redis
24
+ redis_conn = @@config[:redis]
25
+ namespace = @@config[:redis_namespace]
26
+ @@redis ||= Redis::Namespace.new(namespace, redis: redis_conn)
27
+ end
28
+
29
+ def redis=(conn)
30
+ set_config
31
+ @@config[:redis] = conn
32
+ end
33
+
34
+ def redis_namespace=(namespace)
35
+ set_config
36
+ @@config[:redis_namespace] = namespace
37
+ end
38
+
39
+ private
40
+
41
+ def set_config
42
+ @@config ||= DEFAULT_CONFIG
43
+
44
+ # TODO: Provide a Rake to generate initial YAML file
45
+ # for new projects.
46
+
47
+ if defined?(Rails)
48
+ @@config[:yaml_filepath] ||= "#{Rails.root}/config/rollout.yml"
49
+ end
50
+
51
+ if file_path = @@config[:yaml_filepath]
52
+ @@config[:info] ||= YAML.load_file(file_path)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ module FeatureFlagger
2
+ module Control
3
+ extend self
4
+
5
+ def rollout?(feature_key, resource_id, resource_name = nil)
6
+ feature_key = rsolv_key(feature_key, resource_name)
7
+ FeatureFlagger.redis.sismember(feature_key, resource_id)
8
+ end
9
+
10
+ def release!(feature_key, resource_id, resource_name = nil)
11
+ feature_key = rsolv_key(feature_key, resource_name)
12
+ FeatureFlagger.redis.sadd(feature_key, resource_id)
13
+ end
14
+
15
+ def unrelease!(feature_key, resource_id, resource_name = nil)
16
+ feature_key = rsolv_key(feature_key, resource_name)
17
+ FeatureFlagger.redis.srem(feature_key, resource_id)
18
+ end
19
+
20
+ def resource_ids(feature_key, resource_name = nil)
21
+ feature_key = rsolv_key(feature_key, resource_name)
22
+ FeatureFlagger.redis.smembers(feature_key)
23
+ end
24
+
25
+ private
26
+
27
+ def rsolv_key(feature_key, resource_name = nil)
28
+ feature_key_arr = Array(feature_key)
29
+ feature_key_arr.insert(0, resource_name) unless resource_name.nil?
30
+ feature_key_arr.join(':')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ module FeatureFlagger
2
+ class Feature
3
+
4
+ def initialize(key, resource_name = nil)
5
+ @key = resource_name.nil? ? key : key.clone.insert(0, resource_name)
6
+ @key = Array(@key).collect(&:to_s)
7
+ @doc = FeatureFlagger.config[:info]
8
+ end
9
+
10
+ def fetch!
11
+ @data ||= find_value(@doc, *@key)
12
+ raise FeatureFlagger::KeyNotFoundError.new(@key) if @data.nil?
13
+ @data
14
+ end
15
+
16
+ def description
17
+ fetch!
18
+ @data['description']
19
+ end
20
+
21
+ private
22
+
23
+ def find_value(hash, key, *tail)
24
+ value = hash[key]
25
+
26
+ if value.nil? || tail.empty?
27
+ value
28
+ else
29
+ find_value(value, *tail)
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ class FeatureFlagger::KeyNotFoundError < StandardError ; end
@@ -0,0 +1,38 @@
1
+ module FeatureFlagger
2
+ # Model provides convinient methods for Rails Models
3
+ # class Account
4
+ # include FeatureFlagger::Model
5
+ # end
6
+ #
7
+ # Example:
8
+ # Account.first.rollout?([:email_marketing, :new_awesome_feature])
9
+ # #=> true
10
+ module Model
11
+ def rollout?(feature_key)
12
+ Feature.new(feature_key, rollout_resource_name).fetch!
13
+ Control.rollout?(feature_key, id, rollout_resource_name)
14
+ end
15
+
16
+ def release!(feature_key)
17
+ Feature.new(feature_key, rollout_resource_name).fetch!
18
+ Control.release!(feature_key, id, rollout_resource_name)
19
+ end
20
+
21
+ def unrelease!(feature_key)
22
+ Feature.new(feature_key, rollout_resource_name).fetch!
23
+ Control.unrelease!(feature_key, id, rollout_resource_name)
24
+ end
25
+
26
+ private
27
+
28
+ def rollout_resource_name
29
+ klass_name = self.class.name
30
+ klass_name.gsub!(/::/, '_')
31
+ klass_name.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
32
+ klass_name.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
33
+ klass_name.tr!("-", "_")
34
+ klass_name.downcase!
35
+ klass_name
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module FeatureFlagger
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feature_flagger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nando Sousa
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis-namespace
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'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.12'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: Write a longer description or delete this line.
70
+ email:
71
+ - nandosousafr@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/setup
84
+ - feature_flagger.gemspec
85
+ - lib/feature_flagger.rb
86
+ - lib/feature_flagger/control.rb
87
+ - lib/feature_flagger/feature.rb
88
+ - lib/feature_flagger/model.rb
89
+ - lib/feature_flagger/version.rb
90
+ homepage: http://github.com/ResultadosDigitais/feature_flagger
91
+ licenses: []
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.3
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Write a short summary, because Rubygems requires one.
113
+ test_files: []
114
+ has_rdoc: