feature_setting 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c5d91a5d153742d206b572e6cb52b4d948d8e2dd
4
+ data.tar.gz: f9a5fc4954f5288b53d4894ac5c7026520083663
5
+ SHA512:
6
+ metadata.gz: b3abbd0d8eafe2556fa25c91a69b76a0cfbed7031696ee5915a0ad723181f76ca9f3d68724a11876f618173b2fc0fa1b824cb9ee1cfe226e19e22dc72379f7d2
7
+ data.tar.gz: 28e2c3c8163cb41240b8f9cfde63a94c311a0e60f5b4209057c7af8c85cbe4cc431eac6cdfeb10f710204446e687d0caa05a434d679b3eff4bc010f09ea27ac8
data/.gitignore ADDED
@@ -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
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in feature_setting.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,168 @@
1
+ **Note: This gem is still under construction!**
2
+
3
+ # feature_setting
4
+
5
+ [![Code Climate](https://codeclimate.com/github/indrode/feature_setting/badges/gpa.svg)](https://codeclimate.com/github/indrode/feature_setting) [![Test Coverage](https://codeclimate.com/github/indrode/feature_setting/badges/coverage.svg)](https://codeclimate.com/github/indrode/feature_setting/coverage)
6
+
7
+ This gem introduces the concept of "features" and "settings" to your Rails app. It provides an easy way to define such features and settings with default values right in your code and will persist them in the database.
8
+
9
+ - a feature is a key that can either be enabled or disabled
10
+ - a setting is a key that has a value (of type String, Fixnum, Float, or Array)
11
+
12
+ In practice, features can be used to switch certain functionality in your code on or off. This can be used to roll out functionality without the need to deploy. Settings are very flexible in that they can hold any value. The possibilities are endless.
13
+
14
+ Both, features and settings are configured with default values that persist in the database and then can be updated if required.
15
+
16
+ ```ruby
17
+ # using features:
18
+ if Feature.caching_enabled?
19
+ # do this
20
+ else
21
+ # do that
22
+ end
23
+
24
+ # using settings:
25
+ if Setting.error_threshold > 500
26
+ # do this
27
+ end
28
+
29
+ if Setting.allowed_users.include?(current_user)
30
+ # to that
31
+ end
32
+
33
+
34
+ ## Installation
35
+
36
+ Add the gem to your application's Gemfile:
37
+
38
+ ```ruby
39
+ gem 'feature_setting'
40
+ ```
41
+
42
+ Now run the `feature_setting` installation:
43
+
44
+ $ rails generate feature_setting:install
45
+
46
+ This generates a migration file. To run this migration:
47
+
48
+ $ rake db:migrate
49
+
50
+ The next step is to define your Feature and/or Setting classes.
51
+
52
+
53
+ ## Usage
54
+
55
+ ### Features
56
+
57
+ To create a new Feature class, inherit a class from `FeatureSetting::FsFeature`. Then define your features in th `FEATURES` hash and call `init_features!`.
58
+
59
+ ```ruby
60
+ class Features < FeatureSetting::FsFeature
61
+ FEATURES = {
62
+ newfeature: true
63
+ }
64
+
65
+ init_features!
66
+ end
67
+ ```
68
+
69
+ For each key you have defined, a class method `keyname_enabled?` is generated. You can now do the following:
70
+
71
+ ```ruby
72
+ Features.newfeature_enabled? # => true
73
+ Features.disable!(:newfeature)
74
+ Features.newfeature_enabled? # => false
75
+ Features.enable!(:newfeature)
76
+ Features.newfeature_enabled? # => true
77
+ ```
78
+
79
+ Or you can use these shortcuts:
80
+
81
+ ```ruby
82
+ Features.enable_newfeature!
83
+ Features.disable_newfeature!
84
+ ```
85
+
86
+ Default values for features are defined in your class and current values are persisted in the database.
87
+
88
+
89
+ ### Settings
90
+
91
+ To create a new Setting class, inherit a class from `FeatureSetting::FsSetting`. Then define your settings in the `SETTINGS` hash and call `init_settings!`. The following example shows some possible definitions.
92
+
93
+ ```ruby
94
+ class Settings < FeatureSetting::FsSetting
95
+ SETTINGS = {
96
+ setting_one: 12300,
97
+ setting_two: 'some string',
98
+ setting_three: %w(one two three),
99
+ setting_four: ENV['SETTING_FOUR']
100
+ }
101
+
102
+ init_settings!
103
+ end
104
+ ```
105
+
106
+ You can now do the following:
107
+
108
+ ```ruby
109
+ Settings.newsetting # => 12300
110
+ Settings.set!(newsetting: 1000)
111
+ Settings.newsetting # => 1000
112
+
113
+ # other ways to set setting values
114
+ Settings.set!(:newsetting, 1000)
115
+ Settings.set!('newsetting', 1000)
116
+ ```
117
+
118
+ Default values for settings are defined in your class and current values are persisted in the database.
119
+
120
+
121
+ ### Advanced Features
122
+
123
+ Settings and features can be reset to their default values as configured in your class definition:
124
+
125
+ ```ruby
126
+ Features.reset_features!
127
+ Settings.reset_settings!
128
+ ```
129
+
130
+ Display all defined keys:
131
+
132
+ ```ruby
133
+ Features.defined_features
134
+ Settings.defined_settings
135
+ ```
136
+
137
+ You can create as many Setting or Feature classes as you desire. Here are some examples:
138
+
139
+ ```ruby
140
+ SearchSettings.levenshtein_distance
141
+ TestFeatures.experimental_search_enabled?
142
+ ```
143
+
144
+ ## Contributing
145
+
146
+ 1. Fork it (https://github.com/indrode/feature_setting/fork)
147
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
148
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
149
+ 4. Push to the branch (`git push origin my-new-feature`)
150
+ 5. Create a new Pull Request
151
+
152
+ Notes:
153
+
154
+ - Contributions without tests won't be accepted.
155
+ - Please don't update the gem version.
156
+
157
+
158
+ ## License
159
+
160
+ The MIT License (MIT)
161
+
162
+ Copyright (c) 2015 Indro De ([http://indrode.com](http://indrode.com))
163
+
164
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
165
+
166
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
167
+
168
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "feature_setting"
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
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'feature_setting/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'feature_setting'
8
+ spec.version = FeatureSetting::VERSION
9
+ spec.authors = ['Indro De']
10
+ spec.email = ['indro.de@gmail.com']
11
+
12
+ spec.summary = 'A lightweight feature/setting DSL for Rails applications.'
13
+ spec.description = %q{This gem introduces the concept of "features" and "settings" to your Rails app. It provides an easy way to define such features and settings with default values right in your code and will persist them in the database.}
14
+ spec.homepage = 'https://github.com/indrode/feature_setting'
15
+
16
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
+ # delete this section to allow pushing this gem to any host.
18
+ # if spec.respond_to?(:metadata)
19
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
20
+ # else
21
+ # raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
22
+ # end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ # spec.bindir = "exe"
26
+ # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_runtime_dependency 'activesupport', '~> 4.0'
30
+ spec.add_runtime_dependency 'activerecord', '~> 4.0'
31
+ spec.add_development_dependency 'bundler', '~> 1.9'
32
+ spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'rspec', '~> 3.0'
34
+ spec.add_development_dependency 'sqlite3', '~> 0'
35
+ spec.add_development_dependency 'byebug', '~> 0'
36
+ spec.add_development_dependency 'codeclimate-test-reporter', '~> 0'
37
+ end
@@ -0,0 +1,77 @@
1
+ require 'active_record'
2
+
3
+ module FeatureSetting
4
+ class FsFeature < ActiveRecord::Base
5
+ FEATURES = {
6
+ test: false
7
+ }
8
+
9
+ def features
10
+ self.class::FEATURES
11
+ end
12
+
13
+ def klass
14
+ self.class
15
+ end
16
+
17
+ class << self
18
+ def features
19
+ self.new.features
20
+ end
21
+
22
+ def klass
23
+ self.new.klass
24
+ end
25
+
26
+ def init_features!
27
+ features.each do |key, value|
28
+ self.create_with(key: key, enabled: value, klass: klass).find_or_create_by(klass: klass, key: key)
29
+ define_singleton_method("#{key}_enabled?") do
30
+ record = self.where(key: key, klass: klass).first
31
+ record.enabled
32
+ end
33
+ define_singleton_method("enable_#{key}!") do
34
+ enable!(key)
35
+ end
36
+ define_singleton_method("disable_#{key}!") do
37
+ disable!(key)
38
+ end
39
+ end
40
+ remove_old_features!
41
+ end
42
+
43
+ def remove_old_features!
44
+ self.where(key: all_stored_features - defined_features).destroy_all
45
+ end
46
+
47
+ def reset_features!
48
+ self.where(klass: klass).destroy_all
49
+ init_features!
50
+ end
51
+
52
+ def enable!(key)
53
+ if features.has_key?(key.to_sym)
54
+ record = self.where(key: key, klass: klass).first
55
+ record.update_attributes(enabled: true)
56
+ end
57
+ end
58
+
59
+ def disable!(key)
60
+ if features.has_key?(key.to_sym)
61
+ record = self.where(key: key, klass: klass).first
62
+ record.update_attributes(enabled: false)
63
+ end
64
+ end
65
+
66
+ def defined_features
67
+ features.keys.map(&:to_s)
68
+ end
69
+
70
+ private
71
+
72
+ def all_stored_features
73
+ self.all.pluck(:key)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,100 @@
1
+ require 'active_record'
2
+
3
+ module FeatureSetting
4
+ class FsSetting < ActiveRecord::Base
5
+ SETTINGS = {
6
+ test: 'value',
7
+ }
8
+
9
+ def settings
10
+ self.class::SETTINGS
11
+ end
12
+
13
+ def klass
14
+ self.class
15
+ end
16
+
17
+ class << self
18
+ def settings
19
+ self.new.settings
20
+ end
21
+
22
+ def klass
23
+ self.new.klass
24
+ end
25
+
26
+ def init_settings!
27
+ settings.each do |key, value|
28
+ self.create_with(key: key, value: convert_to_string(value, value.class.to_s), value_type: value.class.to_s, klass: klass).find_or_create_by(klass: klass, key: key)
29
+ define_singleton_method(key.to_s) do
30
+ record = self.where(key: key, klass: klass).first
31
+ convert_to_type(record.value, record.value_type)
32
+ end
33
+ end
34
+ remove_old_settings!
35
+ end
36
+
37
+ def remove_old_settings!
38
+ self.where(klass: klass, key: all_stored_settings - defined_settings).destroy_all
39
+ end
40
+
41
+ def reset_settings!
42
+ self.where(klass: klass).destroy_all
43
+ init_settings!
44
+ end
45
+
46
+ def set!(key = nil, value = nil, **hash)
47
+ key = existing_key(key, hash)
48
+ raise 'ERROR: FsSetting key is missing or does not exist.' unless key
49
+
50
+ record = self.where(key: key, klass: klass).first
51
+ new_value = hash.values.first || value
52
+ record.update_attributes(
53
+ value: convert_to_string(new_value, new_value.class.to_s),
54
+ value_type: new_value.class.to_s
55
+ )
56
+ end
57
+
58
+ def existing_key(key = nil, hash = {})
59
+ begin
60
+ settings.has_key?(hash.keys.first) || settings.has_key?(key.to_sym)
61
+ hash.keys.first || key.to_sym
62
+ rescue
63
+ nil
64
+ end
65
+ end
66
+
67
+ def defined_settings
68
+ settings.keys.map(&:to_s)
69
+ end
70
+
71
+ private
72
+
73
+ def all_stored_settings
74
+ self.all.pluck(:key)
75
+ end
76
+
77
+ def convert_to_type(value, type)
78
+ case type
79
+ when 'String'
80
+ value.to_s
81
+ when 'Fixnum'
82
+ value.to_i
83
+ when 'Float'
84
+ value.to_f
85
+ when 'Array'
86
+ value.split('|||')
87
+ end
88
+ end
89
+
90
+ def convert_to_string(value, type)
91
+ case type
92
+ when 'Array'
93
+ value.join('|||')
94
+ else
95
+ value.to_s
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,3 @@
1
+ module FeatureSetting
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,7 @@
1
+ require 'active_support/all'
2
+ require 'feature_setting/version'
3
+ require 'feature_setting/orm/active_record/fs_setting'
4
+ require 'feature_setting/orm/active_record/fs_feature'
5
+
6
+ module FeatureSetting
7
+ end
@@ -0,0 +1,26 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+ require 'rails/generators/active_record'
4
+
5
+
6
+ module FeatureSetting
7
+ module Generators
8
+
9
+ class InstallGenerator < Rails::Generators::Base
10
+ include Rails::Generators::Migration
11
+ extend ActiveRecord::Generators::Migration
12
+
13
+ def self.next_migration_number(path)
14
+ ActiveRecord::Generators::Base.next_migration_number(path)
15
+ end
16
+
17
+ desc 'Generates database tables for feature_settings'
18
+ source_root File.expand_path('../templates', __FILE__)
19
+
20
+ def create_migrations
21
+ migration_name = 'create_fs_tables.rb'
22
+ migration_template "migrations/#{migration_name}", "db/migrate/#{migration_name}"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ class CreateFsTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :fs_features do |t|
4
+ t.string :key
5
+ t.boolean :enabled, default: false
6
+ t.string :klass
7
+ t.timestamps null: false
8
+ end
9
+
10
+ create_table :fs_settings do |t|
11
+ t.string :key
12
+ t.string :value
13
+ t.string :value_type
14
+ t.string :klass
15
+ t.timestamps null: false
16
+ end
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feature_setting
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Indro De
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.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.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.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: codeclimate-test-reporter
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: This gem introduces the concept of "features" and "settings" to your
126
+ Rails app. It provides an easy way to define such features and settings with default
127
+ values right in your code and will persist them in the database.
128
+ email:
129
+ - indro.de@gmail.com
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - ".rspec"
136
+ - ".travis.yml"
137
+ - Gemfile
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - feature_setting.gemspec
143
+ - lib/feature_setting.rb
144
+ - lib/feature_setting/orm/active_record/fs_feature.rb
145
+ - lib/feature_setting/orm/active_record/fs_setting.rb
146
+ - lib/feature_setting/version.rb
147
+ - lib/generators/feature_setting/install/install_generator.rb
148
+ - lib/generators/feature_setting/install/templates/migrations/create_fs_tables.rb
149
+ homepage: https://github.com/indrode/feature_setting
150
+ licenses: []
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubyforge_project:
168
+ rubygems_version: 2.4.5
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: A lightweight feature/setting DSL for Rails applications.
172
+ test_files: []