step_by_step 0.0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +22 -0
- data/README.md +190 -0
- data/Rakefile +2 -0
- data/lib/step_by_step.rb +13 -0
- data/lib/step_by_step/controller.rb +27 -0
- data/lib/step_by_step/rollout.rb +86 -0
- data/lib/step_by_step/version.rb +3 -0
- data/spec/rollout_spec.rb +79 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/step_by_step.sqlite3 +0 -0
- data/spec/support/schema.rb +13 -0
- data/step_by_step.gemspec +27 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 914546dc63e40d0896c7b86b82b42671ae777299
|
4
|
+
data.tar.gz: 95cf8bf3d06b7c14d629669f214a23e4fae5c76a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1d06e4a64e8249df4fa010ab93952490c8fb1aecabaa01c0d17ff1ac7cc64a3bc926a2bb18f091805f18994a7b5b6fc2927b7883acc5eb18624db7208fbd1fdb
|
7
|
+
data.tar.gz: 5ce8f9b3a8b4a796242ea15e6b0d8722de1c1c80a0e0250b5c7fe20468029509631cacfd89547843b7a25a4a10fa13e28f0f3f9ba61ddfc259353acbbc8887b1
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 CrowdCurity
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Dennis Charles Hackethal
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# StepByStep
|
2
|
+
|
3
|
+
This gem is largely inspired by the [Railscast on rollouts and degrading](http://railscasts.com/episodes/315-rollout-and-degrade), as well as the [Rollout gem](https://github.com/FetLife/rollout) that is covered in aforementioned Railscast. While very comprehensive, it uses a Redis backend, that might not be desirable to everyone. Though there are some alternatives out there, most of them are either outdated or not as comprehensive as the rollout gem itself.
|
4
|
+
|
5
|
+
StepByStep uses ActiveRecord as backend instead and tries to cover as many use cases and features as the original rollout gem. It is easy to use since no additional backend needs to be set up, and adds some helpful methods that shall assist you in rolling out and degrading features reliably.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'step_by_step'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install step_by_step
|
20
|
+
|
21
|
+
Since StepByStep uses ActiveRecord, you need to create the following migration:
|
22
|
+
|
23
|
+
$ rails g migration create_rollouts name group user_id:integer percentage:integer failure_count:integer
|
24
|
+
|
25
|
+
Then migrate your database.
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Let's say you have this new commenting feature that you would like to deploy to production, but you only want a subset of your users to be able to see it. Also, you may want to degrade a feature later, or disable it again altogether.
|
30
|
+
|
31
|
+
### Rolling Out Features
|
32
|
+
|
33
|
+
#### Rolling Out To A Group Of Users
|
34
|
+
|
35
|
+
You can define groups with any custom logic like this:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
StepByStep::Rollout.define_group :admins do |user|
|
39
|
+
user.admin?
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
Consider putting this in an initializer, such as `config/initializers/step_by_step.rb`.
|
44
|
+
|
45
|
+
Roll out your feature to your group:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
StepByStep::Rollout.activate_group(:comments, :admins)
|
49
|
+
```
|
50
|
+
|
51
|
+
#### Rolling Out To A Fraction Of Users
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
StepByStep::Rollout.activate_percentage(:comments, 20)
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
Now 20% of your users can see the comments feature. As in the original gem, this is based on the following algorithm:
|
59
|
+
|
60
|
+
```
|
61
|
+
CRC32(user.id) % 100 < percentage # pseudocode
|
62
|
+
```
|
63
|
+
|
64
|
+
So, for 20%, a feature will be rolled out to users with ids 0, 1, 10, 11, 20, 21, etc. These users also remain as the percentage increases.
|
65
|
+
|
66
|
+
#### Rolling Out To A Specific User
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
StepByStep::Rollout.activate_user(:comments, User.first)
|
70
|
+
```
|
71
|
+
|
72
|
+
Now your first user can see the comments feature.
|
73
|
+
|
74
|
+
#### Rolling Out To Everyone
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
StepByStep::Rollout.activate(:comments)
|
78
|
+
```
|
79
|
+
|
80
|
+
Now everyone can see the comments feature. This is theoretically the same as activating a percentage with value 100 or defining a group with a block that always returns true.
|
81
|
+
|
82
|
+
Activating a feature for everyone is common after you have determined that your rollout phase was successful.
|
83
|
+
|
84
|
+
### Deactivating Features
|
85
|
+
|
86
|
+
You can easily deactivate new features depending on your needs.
|
87
|
+
|
88
|
+
#### Deactivating A Feature For Everyone
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
StepByStep::Rollout.deactivate(:comments)
|
92
|
+
```
|
93
|
+
|
94
|
+
Nobody can see your new comments feature anymore.
|
95
|
+
|
96
|
+
#### Deactivating A Group Feature
|
97
|
+
|
98
|
+
Your admins shouldn't be able to see the comments anymore? Easy:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
StepByStep::Rollout.deactivate_group(:comments, :admins)
|
102
|
+
```
|
103
|
+
|
104
|
+
#### Deactivating A Single User Feature
|
105
|
+
|
106
|
+
And there this one user who could see the comments. Let's hide them from him again:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
StepByStep::Rollout.deactivate_user(:comments, User.first)
|
110
|
+
```
|
111
|
+
|
112
|
+
#### Deactivating A Feature For A Fraction Of Users
|
113
|
+
|
114
|
+
Remember those 20% who could see your new feature? Let's get rid of them, too:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
StepByStep::Rollout.deactivate_percentage(:comments)
|
118
|
+
```
|
119
|
+
|
120
|
+
### Displaying Features
|
121
|
+
|
122
|
+
StepByStep comes with a few helper methods, one of which is called `rollout?`. It's available in your controllers as well as in your views. You just pass it the feature name to check if it has been rolled out to your `current_user`.
|
123
|
+
|
124
|
+
#### View Example
|
125
|
+
|
126
|
+
It's as simple as:
|
127
|
+
|
128
|
+
```erb
|
129
|
+
<% if rollout?(:comments) %>
|
130
|
+
<%= # your awesome feature here %>
|
131
|
+
<% end %>
|
132
|
+
```
|
133
|
+
|
134
|
+
#### Controller Example
|
135
|
+
|
136
|
+
Sometimes, you may want to hide a view completely. You can either do this directly in a controller action:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
def show
|
140
|
+
unless rollout?(:comments)
|
141
|
+
redirect_to root_path, notice: 'Access denied'
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
or preferably using before actions:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
before_action :rollout
|
150
|
+
|
151
|
+
private
|
152
|
+
def rollout
|
153
|
+
unless rollout?(:comments)
|
154
|
+
redirect_to root_path, notice: 'Access denied'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
### Degrading A Feature
|
160
|
+
|
161
|
+
Every rollout comes with a `failure_count`. A helper method is added to your application controller that allows you to track exceptions and increments the failure count for your feature. A failure count of 1 or higher disables your feature.
|
162
|
+
|
163
|
+
For instance, you could degrade a feature doing the following in your feature controller:
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
around_action :degrade
|
167
|
+
|
168
|
+
private
|
169
|
+
def degrade
|
170
|
+
degrade_feature(:comments) { yield }
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
## Foolish Assumptions
|
175
|
+
|
176
|
+
- You have a `current_user` method in your application controller that returns the authenticated user or nil if not authenticated (standard behavior, used e.g. by Devise)
|
177
|
+
- You are using Rails
|
178
|
+
- Your user model's primary key is `id` (Rails default)
|
179
|
+
|
180
|
+
## Dependencies
|
181
|
+
|
182
|
+
StepByStep requires Rails 3.2 or higher.
|
183
|
+
|
184
|
+
## Contributing
|
185
|
+
|
186
|
+
1. Fork it ( https://github.com/[my-github-username]/step_by_step/fork )
|
187
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
188
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
189
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
190
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/lib/step_by_step.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "active_record"
|
2
|
+
require "rails"
|
3
|
+
require "step_by_step/version"
|
4
|
+
require "step_by_step/rollout"
|
5
|
+
require "step_by_step/controller"
|
6
|
+
|
7
|
+
module StepByStep
|
8
|
+
class Engine < ::Rails::Engine
|
9
|
+
initializer 'step_by_step.controllers' do |app|
|
10
|
+
ActionController::Base.send :include, StepByStep::Controller
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module StepByStep
|
2
|
+
module Controller
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
helper_method :rollout?
|
7
|
+
hide_action :rollout?
|
8
|
+
|
9
|
+
hide_action :degrade_feature
|
10
|
+
end
|
11
|
+
|
12
|
+
def rollout?(name)
|
13
|
+
Rollout.where(name: name).any? do |rollout|
|
14
|
+
rollout.match?(current_user)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def degrade_feature(name)
|
19
|
+
yield
|
20
|
+
rescue StandardError => e
|
21
|
+
Rollout.where(name: name).each do |rollout|
|
22
|
+
rollout.increment!(:failure_count)
|
23
|
+
end
|
24
|
+
raise e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module StepByStep
|
2
|
+
class Rollout < ActiveRecord::Base
|
3
|
+
def match?(user)
|
4
|
+
user && enabled? && (match_group?(user) || match_user?(user) || match_percentage?(user))
|
5
|
+
end
|
6
|
+
|
7
|
+
# Activates a feature for everyone
|
8
|
+
def self.activate(name)
|
9
|
+
create! name: name, group: :all
|
10
|
+
end
|
11
|
+
|
12
|
+
# Activates a feature for a specific group
|
13
|
+
def self.activate_group(name, group)
|
14
|
+
create! name: name, group: group
|
15
|
+
end
|
16
|
+
|
17
|
+
# Activates a feature for a fraction of users
|
18
|
+
def self.activate_percentage(name, percentage)
|
19
|
+
create! name: name, percentage: percentage
|
20
|
+
end
|
21
|
+
|
22
|
+
# Activates a feature for a specific user
|
23
|
+
def self.activate_user(name, user)
|
24
|
+
create! name: name, user_id: user.id
|
25
|
+
end
|
26
|
+
|
27
|
+
# Deactivates a feature for everyone
|
28
|
+
def self.deactivate(name)
|
29
|
+
where(name: name).destroy_all
|
30
|
+
end
|
31
|
+
|
32
|
+
# Deactivates a feature for a specific group
|
33
|
+
def self.deactivate_group(name, group)
|
34
|
+
where(name: name, group: group).destroy_all
|
35
|
+
end
|
36
|
+
|
37
|
+
# Deactivates a feature for a specific user
|
38
|
+
def self.deactivate_user(name, user)
|
39
|
+
where(name: name, user_id: user.id).destroy_all
|
40
|
+
end
|
41
|
+
|
42
|
+
# Deactivates a feature for a fraction of users
|
43
|
+
def self.deactivate_percentage(name)
|
44
|
+
where('name = ? AND percentage IS NOT NULL', name).destroy_all
|
45
|
+
end
|
46
|
+
|
47
|
+
# Define groups
|
48
|
+
def self.define_group(group, &block)
|
49
|
+
@@groups ||= []
|
50
|
+
@@groups << [group.to_sym, ->(user){ yield(user) }]
|
51
|
+
end
|
52
|
+
|
53
|
+
define_group(:all) do
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def enabled?
|
59
|
+
failure_count.to_i < 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def match_group?(user)
|
63
|
+
if group
|
64
|
+
# Find the group whose block should be evaluated based on its name
|
65
|
+
g = @@groups.select { |i| i.first.to_sym == group.to_sym }
|
66
|
+
|
67
|
+
# There could theoretically be multiple groups with the same name;
|
68
|
+
# only take the last one
|
69
|
+
g = g.last
|
70
|
+
|
71
|
+
# g is now an array that looks like this:
|
72
|
+
# [:group_name, code_block]
|
73
|
+
# Call the code block here with the user
|
74
|
+
g.last.call(user)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def match_user?(user)
|
79
|
+
user_id ? user_id == user.id : false
|
80
|
+
end
|
81
|
+
|
82
|
+
def match_percentage?(user)
|
83
|
+
percentage ? Zlib::crc32(user.id.to_s) % 100 < percentage : false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module StepByStep
|
4
|
+
describe Rollout do
|
5
|
+
let!(:user) { Struct.new(:id, :email) }
|
6
|
+
|
7
|
+
# User who matches
|
8
|
+
let!(:user_1) { user.new(1, 'test@test.com') }
|
9
|
+
|
10
|
+
# User who doesn't match
|
11
|
+
let!(:user_2) { user.new(2, 'nope@test.com') }
|
12
|
+
|
13
|
+
# User who is not logged in (current user will return nil if nobody is logged in)
|
14
|
+
let!(:user_3) { nil }
|
15
|
+
|
16
|
+
before :all do
|
17
|
+
Rollout.define_group(:admins) do |user|
|
18
|
+
emails = [
|
19
|
+
'test@test.com'
|
20
|
+
]
|
21
|
+
emails.include?(user.email)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'rolls out to all' do
|
26
|
+
rollout = Rollout.activate(:feature)
|
27
|
+
expect(rollout.match?(user_1)).to be_truthy
|
28
|
+
expect(rollout.match?(user_2)).to be_truthy
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'rolls out to a group' do
|
32
|
+
rollout = Rollout.activate_group(:feature, :admins)
|
33
|
+
expect(rollout.match?(user_1)).to be_truthy
|
34
|
+
expect(rollout.match?(user_2)).to be_falsey
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'rolls out to a user' do
|
38
|
+
rollout = Rollout.activate_user(:feature, user_1)
|
39
|
+
expect(rollout.match?(user_1)).to be_truthy
|
40
|
+
expect(rollout.match?(user_2)).to be_falsey
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'rolls out to a percentage' do
|
44
|
+
rollout = Rollout.activate_percentage(:feature, 50)
|
45
|
+
|
46
|
+
expect(rollout.match?(user_1)).to be_falsey
|
47
|
+
expect(rollout.match?(user_2)).to be_truthy
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'handles users who are not logged in' do
|
51
|
+
rollout = Rollout.activate_group(:feature, :admins)
|
52
|
+
expect(rollout.match?(user_3)).to be_falsey
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'deactivates all' do
|
56
|
+
Rollout.activate_group(:feature, :all)
|
57
|
+
Rollout.deactivate(:feature)
|
58
|
+
expect(Rollout.where(name: :feature).any?).to be_falsey
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'deactivates a group' do
|
62
|
+
Rollout.activate_group(:feature, :admins)
|
63
|
+
Rollout.deactivate_group(:feature, :admins)
|
64
|
+
expect(Rollout.where(name: :feature, group: :admins).any?).to be_falsey
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'deactivates a user' do
|
68
|
+
Rollout.activate_user(:feature, user_1)
|
69
|
+
Rollout.deactivate_user(:feature, user_1)
|
70
|
+
expect(Rollout.where(name: :feature, user_id: user_1.id).any?).to be_falsey
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'deactivates a percentage' do
|
74
|
+
Rollout.activate_percentage(:feature, 50)
|
75
|
+
Rollout.deactivate_percentage(:feature)
|
76
|
+
expect(Rollout.where('name = ? AND percentage IS NOT NULL', :feature).any?).to be_falsey
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require "step_by_step"
|
2
|
+
|
3
|
+
# Establish a connection to db
|
4
|
+
ActiveRecord::Base.establish_connection(
|
5
|
+
adapter: "sqlite3",
|
6
|
+
database: File.dirname(__FILE__) + "/step_by_step.sqlite3"
|
7
|
+
)
|
8
|
+
|
9
|
+
# Load schema every time the specs are run
|
10
|
+
load File.dirname(__FILE__) + '/support/schema.rb'
|
Binary file
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'step_by_step/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "step_by_step"
|
8
|
+
spec.version = StepByStep::VERSION
|
9
|
+
spec.authors = ["Dennis Charles Hackethal"]
|
10
|
+
spec.email = ["dennis.hackethal@gmail.com"]
|
11
|
+
spec.summary = %q{Active Record alternative to https://github.com/FetLife/rollout.}
|
12
|
+
spec.description = %q{Alternative to https://github.com/FetLife/rollout, with an Active Record backend and additional helpers, partially based on Ryan Bates's custom solution in http://railscasts.com/episodes/315-rollout-and-degrade.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec-rails"
|
24
|
+
spec.add_development_dependency "sqlite3"
|
25
|
+
spec.add_development_dependency "pry"
|
26
|
+
spec.add_dependency 'rails', '>= 3.2'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: step_by_step
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dennis Charles Hackethal
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.2'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.2'
|
97
|
+
description: Alternative to https://github.com/FetLife/rollout, with an Active Record
|
98
|
+
backend and additional helpers, partially based on Ryan Bates's custom solution
|
99
|
+
in http://railscasts.com/episodes/315-rollout-and-degrade.
|
100
|
+
email:
|
101
|
+
- dennis.hackethal@gmail.com
|
102
|
+
executables: []
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- lib/step_by_step.rb
|
113
|
+
- lib/step_by_step/controller.rb
|
114
|
+
- lib/step_by_step/rollout.rb
|
115
|
+
- lib/step_by_step/version.rb
|
116
|
+
- spec/rollout_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
- spec/step_by_step.sqlite3
|
119
|
+
- spec/support/schema.rb
|
120
|
+
- step_by_step.gemspec
|
121
|
+
homepage: ''
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata: {}
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubyforge_project:
|
141
|
+
rubygems_version: 2.2.2
|
142
|
+
signing_key:
|
143
|
+
specification_version: 4
|
144
|
+
summary: Active Record alternative to https://github.com/FetLife/rollout.
|
145
|
+
test_files:
|
146
|
+
- spec/rollout_spec.rb
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
- spec/step_by_step.sqlite3
|
149
|
+
- spec/support/schema.rb
|