softlaunch 0.5.1
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.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +233 -0
- data/Rakefile +7 -0
- data/app/controllers/soft_launch/soft_launch_controller.rb +26 -0
- data/app/views/soft_launch/soft_launch/show.html.erb +31 -0
- data/config/routes.rb +3 -0
- data/lib/generators/soft_launch/setup/setup_generator.rb +10 -0
- data/lib/generators/soft_launch/setup/templates/softlaunch.yml +14 -0
- data/lib/generators/soft_launch/setup/templates/softlaunch_init.rb +23 -0
- data/lib/softlaunch.rb +9 -0
- data/lib/softlaunch/accessors.rb +33 -0
- data/lib/softlaunch/config.rb +33 -0
- data/lib/softlaunch/cookies.rb +24 -0
- data/lib/softlaunch/engine.rb +20 -0
- data/lib/softlaunch/errors.rb +12 -0
- data/lib/softlaunch/finders.rb +27 -0
- data/lib/softlaunch/include_appctlr.rb +17 -0
- data/lib/softlaunch/initialize.rb +14 -0
- data/lib/softlaunch/version.rb +3 -0
- data/softlaunch.gemspec +23 -0
- data/spec/config_templates/basic.yml +11 -0
- data/spec/softlaunch_asker.rb +22 -0
- data/spec/softlaunch_spec.rb +83 -0
- data/spec/spec_helper.rb +2 -0
- metadata +87 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Lee Atchison
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
# Soft Launch
|
2
|
+
|
3
|
+
Soft Launch is a mechanism that allows you to add new features to a web application, but limit which of your users has access to them. This allows you to give access to a new feature to a limited set of users,
|
4
|
+
while at the same time letting them try out the new feature in a full production environment.
|
5
|
+
|
6
|
+
You use Soft Launch by defining "features". A feature is a piece of functionality that you want to roll out to a specific subset of individuals. An example feature may be a newly designed
|
7
|
+
account settings page, for example.
|
8
|
+
|
9
|
+
You define features in your config/softlaunch.yml file. This file specifies the name of a feature and whether or not the feature should be enabled for a particular rails environment or not.
|
10
|
+
Here is an example configuration file for launching a newly redesigned accout settings page:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
development:
|
14
|
+
acctsettings:
|
15
|
+
name: New Account Settings Page
|
16
|
+
status: enabled
|
17
|
+
|
18
|
+
test:
|
19
|
+
acctsettings:
|
20
|
+
name: New Account Settings Page
|
21
|
+
status: enabled
|
22
|
+
|
23
|
+
production:
|
24
|
+
acctsettings:
|
25
|
+
name: New Account Settings Page
|
26
|
+
status: disabled
|
27
|
+
```
|
28
|
+
|
29
|
+
Now, in your controllers, models, views, helpers, etc., you can query whether a given feature is enabled for a specific page request or not by using code similar to this?
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
if launched? :acctsettings
|
33
|
+
...code for the new page...
|
34
|
+
else
|
35
|
+
...code for the old page...
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
Using Soft Launch, you can enable/disable features by rails environment (shown above). For a disabled feature, you can enable it for a given subset of users.
|
40
|
+
You can also allow a user to enable the feature by setting a cookie by going to a special URL in your application to enable/disable a feature.
|
41
|
+
|
42
|
+
Note: Soft Launch should not be used to enable unstable or untested features. It's purpose isn't to replace the normal testing process. It's purpose is to provide limited exposure to a set
|
43
|
+
of features before making them more globally available to everyone using your application.
|
44
|
+
|
45
|
+
# Installation
|
46
|
+
|
47
|
+
Soft Launch requires Rails 3.0 or greater.
|
48
|
+
Add this to your Gemfile and run the bundle command:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
gem "softlaunch"
|
52
|
+
```
|
53
|
+
|
54
|
+
Now, run this command:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
rails generate soft_launch:setup
|
58
|
+
```
|
59
|
+
|
60
|
+
This will create an initializer file and add an entry to your route.rb file.
|
61
|
+
|
62
|
+
# Getting Started
|
63
|
+
|
64
|
+
Let's say you added a feature to display a new newsfeed on your home page. You want to develop and test that feature in a controlled environment, as normal.
|
65
|
+
However, you'll want to roll this out to only a few people in production to judge feedback first, before rolling it out to everyone, so you enable
|
66
|
+
Soft Launch.
|
67
|
+
|
68
|
+
Follow the installation instructions above, then you'll find a config/softlaunch.yml file in your application, it will contain something like this:
|
69
|
+
|
70
|
+
```yml
|
71
|
+
development:
|
72
|
+
myfeature:
|
73
|
+
name: My New Feature
|
74
|
+
status: enabled
|
75
|
+
|
76
|
+
test:
|
77
|
+
myfeature:
|
78
|
+
name: My New Feature
|
79
|
+
status: enabled
|
80
|
+
|
81
|
+
production:
|
82
|
+
myfeature:
|
83
|
+
name: My New Feature
|
84
|
+
status: disabled
|
85
|
+
```
|
86
|
+
|
87
|
+
By default, this feature will be setup to be enabled in development and test, but not production. Let's change the name and identifier of this feature to
|
88
|
+
something more meaningful to us:
|
89
|
+
|
90
|
+
```yml
|
91
|
+
development:
|
92
|
+
newsfeed:
|
93
|
+
name: Display home page newsfeed
|
94
|
+
status: enabled
|
95
|
+
|
96
|
+
test:
|
97
|
+
newsfeed:
|
98
|
+
name: Display home page newsfeed
|
99
|
+
status: enabled
|
100
|
+
|
101
|
+
production:
|
102
|
+
newsfeed:
|
103
|
+
name: Display home page newsfeed
|
104
|
+
status: disabled
|
105
|
+
```
|
106
|
+
|
107
|
+
Now, let's go to our view template for our home page:
|
108
|
+
|
109
|
+
```erb
|
110
|
+
file: app/views/home/index.html.erb
|
111
|
+
...
|
112
|
+
<%if launched? :newsfeed%>
|
113
|
+
<h2>Welcome to my New Newsfeed!</h2>
|
114
|
+
<@newsfeed.each do |article%>
|
115
|
+
...
|
116
|
+
<%end%>
|
117
|
+
<%else%>
|
118
|
+
Put whatever I had on the page before the newsfeed was added here.
|
119
|
+
<%end%>
|
120
|
+
...
|
121
|
+
```
|
122
|
+
|
123
|
+
Since your newsfeed will likely need data from our controller, add it as well:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
file: app/controllers/home_controllber.rb
|
127
|
+
...
|
128
|
+
def index
|
129
|
+
... do other stuff here ...
|
130
|
+
if launched? :newsfeed
|
131
|
+
@newsfeed=NewsFeed.most_recent_news
|
132
|
+
end
|
133
|
+
end
|
134
|
+
...
|
135
|
+
```
|
136
|
+
|
137
|
+
Now, when you show this page in development and test mode, you'll see the newsfeed. But when it is displayed in production, the original
|
138
|
+
content is displayed.
|
139
|
+
|
140
|
+
## Enabling it for a Specific Person's Browser
|
141
|
+
Let's say you want to see what it looks like now in production.
|
142
|
+
Update your config file to show the following:
|
143
|
+
|
144
|
+
```yml
|
145
|
+
development:
|
146
|
+
newsfeed:
|
147
|
+
name: Display home page newsfeed
|
148
|
+
status: enabled
|
149
|
+
|
150
|
+
test:
|
151
|
+
newsfeed:
|
152
|
+
name: Display home page newsfeed
|
153
|
+
status: enabled
|
154
|
+
|
155
|
+
production:
|
156
|
+
newsfeed:
|
157
|
+
name: Display home page newsfeed
|
158
|
+
status: user
|
159
|
+
usercode: C04D9454C8EB449C8B4E6A72157B4AA4
|
160
|
+
```
|
161
|
+
|
162
|
+
The 'usercode' is a random, unique code assigned to this newsfeed. We used a UUID for this example (sans dashes), but you can
|
163
|
+
use any value you like. You want the value to be "hard to guess", as it will be used in the URL that enables your new feature
|
164
|
+
for a specific user and their browser.
|
165
|
+
|
166
|
+
Now, in production, go to your home page, you'll see the "old" content. You can enable the new content just for you by going to this
|
167
|
+
URL:
|
168
|
+
|
169
|
+
http://<myapplication>/softlaunch/C04D9454C8EB449C8B4E6A72157B4AA4
|
170
|
+
|
171
|
+
On this page, you'll see whether this feature is enabled for your browser or not, and will be given the option to enable/disable this feature.
|
172
|
+
Enable the feature, and go back to your home page.
|
173
|
+
|
174
|
+
You will now see that your home page has the new newsfeed.
|
175
|
+
|
176
|
+
Go to a different browser or a different computer and load the home page. You'll see the old content is still visible to you.
|
177
|
+
|
178
|
+
A cookie has been set in your browser, which is what is used to enable the feature. It is only enabled for you and only with the
|
179
|
+
current browser.
|
180
|
+
|
181
|
+
You can now give that URL to other people (beta testers, for instance) and let them use the feature. If they have problems with
|
182
|
+
it or don't like it, they can always turn it off. Note that you should tell them not to share the URL with other people, as
|
183
|
+
anyone with the URL will know how to enable the feature (which is why the unique identifier should be hard to guess).
|
184
|
+
|
185
|
+
At any point in time, you can enable this feature for everybody by changing your config file (and redeploying), changing the
|
186
|
+
production section of the config file to show this:
|
187
|
+
|
188
|
+
```yml
|
189
|
+
production:
|
190
|
+
newsfeed:
|
191
|
+
name: Display home page newsfeed
|
192
|
+
status: enabled
|
193
|
+
```
|
194
|
+
|
195
|
+
Or, if you want to disable the feature to everyone (because it wasn't well received or you want to work on it some more),
|
196
|
+
you can change it to this:
|
197
|
+
|
198
|
+
```yml
|
199
|
+
production:
|
200
|
+
newsfeed:
|
201
|
+
name: Display home page newsfeed
|
202
|
+
status: disabled
|
203
|
+
```
|
204
|
+
|
205
|
+
When it is disabled, even people with the cookie set will not be able to see the feature.
|
206
|
+
|
207
|
+
Once you have finished permanently launching the feature (or permanently deciding not to launch it), you can go and refactor
|
208
|
+
your code to remove the "if launched?" statements and the unused code, then remove the feature from the softlaunch.yml
|
209
|
+
file.
|
210
|
+
|
211
|
+
# Questions or Problems?
|
212
|
+
|
213
|
+
Please note that this is not yet released, and is still a work in progress.
|
214
|
+
|
215
|
+
Suggestions for this gem can be sent to me.
|
216
|
+
|
217
|
+
# Testing Soft Launch
|
218
|
+
|
219
|
+
Simply run "rake test". The gem uses rspec for all of it's test suites.
|
220
|
+
|
221
|
+
# Using Can Can?
|
222
|
+
|
223
|
+
If you are using Can Can, and have the following line in your application_controller.rb file:
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
check_authorization
|
227
|
+
```
|
228
|
+
|
229
|
+
You should update it to exclude the soft launch controller:
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
check_authorization :unless => :soft_launch_controller?
|
233
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
class SoftLaunch
|
2
|
+
class SoftLaunchController < ApplicationController
|
3
|
+
|
4
|
+
def show
|
5
|
+
@softlaunch=SoftLaunch.find_by_usercode params[:id]
|
6
|
+
render :layout => SoftLaunch.engine_layout
|
7
|
+
end
|
8
|
+
|
9
|
+
def update
|
10
|
+
@softlaunch=SoftLaunch.find_by_usercode params[:id]
|
11
|
+
if params[:sl_enable].to_i!=0
|
12
|
+
@softlaunch.enable=true
|
13
|
+
flash[:info]="Enabled feature #{@softlaunch.name}"
|
14
|
+
else
|
15
|
+
@softlaunch.enable=false
|
16
|
+
flash[:info]="Disabled feature #{@softlaunch.name}"
|
17
|
+
end
|
18
|
+
redirect_to soft_launch_path id: @softlaunch.usercode
|
19
|
+
end
|
20
|
+
|
21
|
+
def soft_launch_controller?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<h1>Soft Launch: <%=@softlaunch.name%></h1>
|
2
|
+
<table class="softlaunchstatus">
|
3
|
+
<tr>
|
4
|
+
<td class="label">Public Status: </td>
|
5
|
+
<td class="value"><%=@softlaunch.status_string%></td>
|
6
|
+
</tr>
|
7
|
+
<tr>
|
8
|
+
<td class="label">This Browser Status: </td>
|
9
|
+
<%if @softlaunch.launched?%>
|
10
|
+
<td class="value">
|
11
|
+
Enabled
|
12
|
+
</td>
|
13
|
+
<td class="value">
|
14
|
+
<%=form_for @softlaunch.usercode,:method=>:put do |f|%>
|
15
|
+
<%=hidden_field_tag :sl_enable,0%>
|
16
|
+
<%=f.submit "Disable"%>
|
17
|
+
<%end%>
|
18
|
+
</td>
|
19
|
+
<%else%>
|
20
|
+
<td class="value">
|
21
|
+
Disabled
|
22
|
+
</td>
|
23
|
+
<td class="value">
|
24
|
+
<%=form_for @softlaunch.usercode,:method=>:put do |f|%>
|
25
|
+
<%=hidden_field_tag :sl_enable,1%>
|
26
|
+
<%=f.submit "Enable"%>
|
27
|
+
<%end%>
|
28
|
+
</td>
|
29
|
+
<%end%>
|
30
|
+
</tr>
|
31
|
+
</table>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
class SoftLaunch
|
2
|
+
class SetupGenerator < Rails::Generators::Base
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
4
|
+
def create_initializer
|
5
|
+
copy_file "softlaunch_init.rb", "config/initializers/softlaunch_init.rb"
|
6
|
+
copy_file "softlaunch.yml", "config/softlaunch.yml"
|
7
|
+
route "mount SoftLaunch::Engine => \"/softlaunch\""
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
# SoftLaunch Configuration
|
4
|
+
#
|
5
|
+
#
|
6
|
+
|
7
|
+
#
|
8
|
+
# Specify the location of the configuraiton file.
|
9
|
+
# Default: RAILS_ROOT/config/softlaunch.yml
|
10
|
+
#
|
11
|
+
SoftLaunch.config_file=Rails.root.join "config/softlaunch.yml"
|
12
|
+
|
13
|
+
#
|
14
|
+
# Specify a layout file to use for the softlaunch feature page.
|
15
|
+
# NOTE: If you specify a layout file for the engine, then in this layout file, all
|
16
|
+
# URL helpers must be prefixed with "main_app". So, for instance, you can't use
|
17
|
+
# "root_path", you must use "main_app.root_path". If you do not do this, you will
|
18
|
+
# get errors when rendering a page from the engine. Specify 'nil' will not use any
|
19
|
+
# template file.
|
20
|
+
# This is due to how engines handle namespaced applications.
|
21
|
+
#
|
22
|
+
SoftLaunch.engine_layout=nil # Do not use any layout file.
|
23
|
+
# SoftLaunch.engine_layout="application" # Use the global application layout file.
|
data/lib/softlaunch.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require "softlaunch/version"
|
2
|
+
require "softlaunch/errors"
|
3
|
+
require "softlaunch/config"
|
4
|
+
require "softlaunch/finders"
|
5
|
+
require "softlaunch/cookies"
|
6
|
+
require "softlaunch/accessors"
|
7
|
+
require "softlaunch/initialize"
|
8
|
+
require "softlaunch/include_appctlr"
|
9
|
+
require "softlaunch/engine"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
class SoftLaunch
|
3
|
+
|
4
|
+
def launched?
|
5
|
+
return true if @status==:enabled
|
6
|
+
return false if @status==:disabled
|
7
|
+
return false if !user_specific?
|
8
|
+
return cookie_enabled?
|
9
|
+
end
|
10
|
+
|
11
|
+
def enabled?
|
12
|
+
@status==:enabled
|
13
|
+
end
|
14
|
+
|
15
|
+
def disabled?
|
16
|
+
@status==:disabled
|
17
|
+
end
|
18
|
+
|
19
|
+
def user_specific?
|
20
|
+
@status==:user
|
21
|
+
end
|
22
|
+
|
23
|
+
def status_string
|
24
|
+
case @status
|
25
|
+
when :disabled then "Disabled"
|
26
|
+
when :enabled then "Enabled"
|
27
|
+
when :user then "Per User"
|
28
|
+
else
|
29
|
+
"???#{@status}???"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class SoftLaunch
|
2
|
+
|
3
|
+
class << self
|
4
|
+
attr_accessor :cookies, :engine_layout
|
5
|
+
|
6
|
+
def reset
|
7
|
+
@config_file=nil
|
8
|
+
@config=nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def config_file= config_file # Where is the config file located?
|
12
|
+
raise FileAlreadyInUse if @config
|
13
|
+
@config_file=config_file
|
14
|
+
end
|
15
|
+
def config_file
|
16
|
+
@config_file||=Rails.root.join "config/softlaunch.yml"
|
17
|
+
end
|
18
|
+
|
19
|
+
def config
|
20
|
+
return @config if @config
|
21
|
+
raw_config = File.read(config_file)
|
22
|
+
if raw_config.nil?
|
23
|
+
puts "No configuration file provided"
|
24
|
+
logger.error "No configuration file provided"
|
25
|
+
raise InvalidConfiguration
|
26
|
+
end
|
27
|
+
@config = YAML::load(raw_config)[ENV["RAILS_ENV"]||"development"]
|
28
|
+
@config
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class SoftLaunch
|
2
|
+
|
3
|
+
def enable= status
|
4
|
+
if status
|
5
|
+
SoftLaunch.cookies[cookie_name]=true
|
6
|
+
else
|
7
|
+
SoftLaunch.cookies.delete cookie_name
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def cookie_enabled?
|
14
|
+
raise SoftLaunch::CookiesNotConfigured if SoftLaunch.cookies.nil?
|
15
|
+
# User Status...(force true and false, even if 1, 0, nil, etc.)
|
16
|
+
return true if SoftLaunch.cookies[cookie_name]
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
|
20
|
+
def cookie_name
|
21
|
+
"soft_launch_#{id}".to_sym
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class SoftLaunch
|
2
|
+
|
3
|
+
if defined? Rails
|
4
|
+
|
5
|
+
class Engine < Rails::Engine
|
6
|
+
engine_name "soft_launch"
|
7
|
+
isolate_namespace SoftLaunch
|
8
|
+
initializer 'soft_launch.controller' do |app|
|
9
|
+
ActiveSupport.on_load(:action_controller) do
|
10
|
+
include SoftLaunchApplicationController
|
11
|
+
helper_method :launched?
|
12
|
+
helper_method :softlaunch
|
13
|
+
before_filter :setup_soft_launch
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class SoftLaunch
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def all
|
6
|
+
ret=[]
|
7
|
+
config.each do |id,data|
|
8
|
+
ret<<SoftLaunch.new(id)
|
9
|
+
end
|
10
|
+
ret
|
11
|
+
end
|
12
|
+
def find feature
|
13
|
+
config.each do |id,data|
|
14
|
+
return SoftLaunch.new id if id.to_s==feature.to_s
|
15
|
+
end
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
def find_by_usercode usercode
|
19
|
+
config.each do |id,data|
|
20
|
+
return SoftLaunch.new id if data["usercode"] and data["usercode"].to_s == usercode.to_s
|
21
|
+
end
|
22
|
+
return nil
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SoftLaunchApplicationController
|
2
|
+
|
3
|
+
def launched? ident
|
4
|
+
sl=SoftLaunch.find ident
|
5
|
+
return false if sl.nil?
|
6
|
+
sl.launched?
|
7
|
+
end
|
8
|
+
|
9
|
+
def softlaunch ident
|
10
|
+
launched? ident
|
11
|
+
end
|
12
|
+
|
13
|
+
def setup_soft_launch
|
14
|
+
SoftLaunch.cookies=cookies
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
class SoftLaunch
|
3
|
+
attr_reader :id,:name,:usercode
|
4
|
+
|
5
|
+
def initialize feature_id
|
6
|
+
info=SoftLaunch.config[feature_id.to_s]
|
7
|
+
raise SoftLaunch::InvalidFeature if info.nil?
|
8
|
+
@id=feature_id
|
9
|
+
@name=info["name"]
|
10
|
+
@status=info["status"].to_sym
|
11
|
+
@usercode=info["usercode"]
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/softlaunch.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "softlaunch/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "softlaunch"
|
7
|
+
s.version = Softlaunch::VERSION
|
8
|
+
s.authors = ["Lee Atchison"]
|
9
|
+
s.email = ["lee@leeatchison.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Stage the launch of new features to smaller groups of people first.}
|
12
|
+
s.description = %q{Stage the launch of new features to smaller groups of people first.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "softlaunch"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Soft Launch Asker" do
|
4
|
+
before :each do
|
5
|
+
SoftLaunch.reset
|
6
|
+
SoftLaunch.config_file=Rails.root.join "spec/config_templates/basic.yml"
|
7
|
+
end
|
8
|
+
it "should report launched if feature is enabled" do
|
9
|
+
launched?(:feature1).should be_true
|
10
|
+
end
|
11
|
+
it "should report not launched if feature is disabled" do
|
12
|
+
launched?(:feature2).should be_false
|
13
|
+
end
|
14
|
+
it "should report launched if feature is user and there is a proper cookie" do
|
15
|
+
SoftLaunch.cookies={:soft_launch_feature3=>true}
|
16
|
+
launched?(:feature3).should be_true
|
17
|
+
end
|
18
|
+
it "should report not launched if feature is user and there is no proper cookie" do
|
19
|
+
SoftLaunch.cookies={}
|
20
|
+
launched?(:feature1).should be_false
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SoftLaunch do
|
4
|
+
before :each do
|
5
|
+
SoftLaunch.reset
|
6
|
+
SoftLaunch.config_file="spec/config_templates/basic.yml"
|
7
|
+
end
|
8
|
+
it "reads configuration from a specified configuration file" do
|
9
|
+
SoftLaunch.config_file.should eq "spec/config_templates/basic.yml"
|
10
|
+
end
|
11
|
+
it "allows setting features as enabled" do
|
12
|
+
sl=SoftLaunch.find "feature1"
|
13
|
+
sl.name.should eq "My New Feature 1"
|
14
|
+
sl.enabled?.should be_true
|
15
|
+
sl.disabled?.should be_false
|
16
|
+
sl.user_specific?.should be_false
|
17
|
+
end
|
18
|
+
it "allows setting features as disabled" do
|
19
|
+
sl=SoftLaunch.find "feature2"
|
20
|
+
sl.name.should eq "My New Feature 2"
|
21
|
+
sl.enabled?.should be_false
|
22
|
+
sl.disabled?.should be_true
|
23
|
+
sl.user_specific?.should be_false
|
24
|
+
end
|
25
|
+
it "allows setting features as user enablable" do
|
26
|
+
sl=SoftLaunch.find "feature3"
|
27
|
+
sl.name.should eq "My New Feature 3"
|
28
|
+
sl.enabled?.should be_false
|
29
|
+
sl.disabled?.should be_false
|
30
|
+
sl.user_specific?.should be_true
|
31
|
+
end
|
32
|
+
it "should report launched if feature is enabled" do
|
33
|
+
sl=SoftLaunch.find "feature1"
|
34
|
+
sl.launched?.should be_true
|
35
|
+
end
|
36
|
+
it "should report not launched if feature is disabled" do
|
37
|
+
sl=SoftLaunch.find "feature2"
|
38
|
+
sl.launched?.should be_false
|
39
|
+
end
|
40
|
+
it "should report launched if feature is user and there is a proper cookie" do
|
41
|
+
SoftLaunch.cookies={:soft_launch_feature3=>true}
|
42
|
+
sl=SoftLaunch.find "feature3"
|
43
|
+
sl.launched?.should be_true
|
44
|
+
end
|
45
|
+
it "should report not launched if feature is user and there is no proper cookie" do
|
46
|
+
SoftLaunch.cookies={}
|
47
|
+
sl=SoftLaunch.find "feature3"
|
48
|
+
sl.launched?.should be_false
|
49
|
+
end
|
50
|
+
it "should be able to get a list of all features" do
|
51
|
+
list=SoftLaunch.all
|
52
|
+
list.size.should eq 3
|
53
|
+
list[0].name.should eq "My New Feature 1"
|
54
|
+
list[1].name.should eq "My New Feature 2"
|
55
|
+
list[2].name.should eq "My New Feature 3"
|
56
|
+
end
|
57
|
+
it "should be able to look up a feature by feature name" do
|
58
|
+
feature=SoftLaunch.find "feature2"
|
59
|
+
feature.name.should eq "My New Feature 2"
|
60
|
+
end
|
61
|
+
it "should be able to look up a feature by user code" do
|
62
|
+
feature=SoftLaunch.find_by_usercode "thisisasampleusercode"
|
63
|
+
feature.name.should eq "My New Feature 3"
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be able to set a cookie to enable a feature" do
|
67
|
+
cookies={}
|
68
|
+
cookies[:soft_launch_feature3].should be_nil
|
69
|
+
SoftLaunch.cookies=cookies
|
70
|
+
sl=SoftLaunch.find "feature3"
|
71
|
+
sl.enable=true
|
72
|
+
cookies[:soft_launch_feature3].should be_true
|
73
|
+
end
|
74
|
+
it "should be able to clear a cookie to disable a feature" do
|
75
|
+
cookies={:soft_launch_feature3=>true}
|
76
|
+
cookies[:soft_launch_feature3].should be_true
|
77
|
+
SoftLaunch.cookies=cookies
|
78
|
+
sl=SoftLaunch.find "feature3"
|
79
|
+
sl.enable=false
|
80
|
+
cookies[:soft_launch_feature3].should be_nil
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: softlaunch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Lee Atchison
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-23 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70316284707900 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70316284707900
|
25
|
+
description: Stage the launch of new features to smaller groups of people first.
|
26
|
+
email:
|
27
|
+
- lee@leeatchison.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- .rspec
|
34
|
+
- Gemfile
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- app/controllers/soft_launch/soft_launch_controller.rb
|
39
|
+
- app/views/soft_launch/soft_launch/show.html.erb
|
40
|
+
- config/routes.rb
|
41
|
+
- lib/generators/soft_launch/setup/setup_generator.rb
|
42
|
+
- lib/generators/soft_launch/setup/templates/softlaunch.yml
|
43
|
+
- lib/generators/soft_launch/setup/templates/softlaunch_init.rb
|
44
|
+
- lib/softlaunch.rb
|
45
|
+
- lib/softlaunch/accessors.rb
|
46
|
+
- lib/softlaunch/config.rb
|
47
|
+
- lib/softlaunch/cookies.rb
|
48
|
+
- lib/softlaunch/engine.rb
|
49
|
+
- lib/softlaunch/errors.rb
|
50
|
+
- lib/softlaunch/finders.rb
|
51
|
+
- lib/softlaunch/include_appctlr.rb
|
52
|
+
- lib/softlaunch/initialize.rb
|
53
|
+
- lib/softlaunch/version.rb
|
54
|
+
- softlaunch.gemspec
|
55
|
+
- spec/config_templates/basic.yml
|
56
|
+
- spec/softlaunch_asker.rb
|
57
|
+
- spec/softlaunch_spec.rb
|
58
|
+
- spec/spec_helper.rb
|
59
|
+
homepage: ''
|
60
|
+
licenses: []
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project: softlaunch
|
79
|
+
rubygems_version: 1.8.10
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Stage the launch of new features to smaller groups of people first.
|
83
|
+
test_files:
|
84
|
+
- spec/config_templates/basic.yml
|
85
|
+
- spec/softlaunch_asker.rb
|
86
|
+
- spec/softlaunch_spec.rb
|
87
|
+
- spec/spec_helper.rb
|