switches 0.1.0 → 0.1.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 CHANGED
@@ -20,3 +20,4 @@ pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
22
  *.gem
23
+ README.html
data/README ADDED
@@ -0,0 +1 @@
1
+ Please see README.markdown or visit http://github.com/seamusabshere/switches
data/README.markdown ADDED
@@ -0,0 +1,145 @@
1
+ # switches #
2
+
3
+ Switches lets you turn on and off sections of your code with <tt>Switches.foobar?</tt> booleans.
4
+
5
+ It's an extraction from [http://brighterplanet.com](http://brighterplanet.com), where we use it as an emergency button to turn on/off API integration with Facebook, Campaign Monitor, etc.
6
+
7
+ ## Quick start ##
8
+
9
+ Add 2 lines to <tt>config/environment.rb</tt>:
10
+
11
+ require File.join(File.dirname(__FILE__), 'boot')
12
+ [...]
13
+ require 'switches' # AFTER boot, BEFORE initializer
14
+ [...]
15
+ Rails::Initializer.run do |config|
16
+ [...]
17
+ config.gem 'switches', :lib => false # INSIDE initializer
18
+
19
+ Now run this:
20
+
21
+ my-macbook:~/my_app $ ./script/runner 'Switches.setup'
22
+
23
+ Add your defaults to <tt>config/switches/defaults.yml</tt>:
24
+
25
+ ---
26
+ ssl: true # ssl support is on by default
27
+ campaign_monitor: true # campaign monitor integration is on by default
28
+
29
+ ## Tasks ##
30
+
31
+ <table>
32
+ <tr>
33
+ <th>Rake task</th>
34
+ <th>Cap task</th>
35
+ <th>Notes</th>
36
+ </tr>
37
+ <tr>
38
+ <td>rake s:c</td>
39
+ <td>cap TARGET s:c</td>
40
+ <td>show current switches</td>
41
+ </tr>
42
+ <tr>
43
+ <td>rake s:d</td>
44
+ <td>cap TARGET s:d</td>
45
+ <td>show difference between current and default switches</td>
46
+ </tr>
47
+ <tr>
48
+ <td>rake s:on[SWITCH]</td>
49
+ <td>cap TARGET s:on ARG=SWITCH</td>
50
+ <td>turn on SWITCH</td>
51
+ </tr>
52
+ <tr>
53
+ <td>rake s:off[SWITCH]</td>
54
+ <td>cap TARGET s:off ARG=SWITCH</td>
55
+ <td>turn off SWITCH</td>
56
+ </tr>
57
+ <tr>
58
+ <td>rake s:clear[SWITCH]</td>
59
+ <td>cap TARGET s:clear ARG=SWITCH</td>
60
+ <td>clear any switch for SWITCH</td>
61
+ </tr>
62
+ <tr>
63
+ <td>rake s:reset</td>
64
+ <td>cap TARGET s:reset</td>
65
+ <td>go back to defaults (deletes <tt>config/switches/current.yml</tt>)</td>
66
+ </tr>
67
+ <tr>
68
+ <td>rake s:backup</td>
69
+ <td>cap TARGET s:backup</td>
70
+ <td>backup current switches (copies to <tt>config/switches/backup.yml</tt>)</td>
71
+ </tr>
72
+ <tr>
73
+ <td>rake s:restore</td>
74
+ <td>cap TARGET s:restore</td>
75
+ <td>restore backed-up switches (copies <tt>backup.yml</tt> to <tt>current.yml</tt>)</td>
76
+ </tr>
77
+ <tr>
78
+ <td>rake s:default</td>
79
+ <td>cap TARGET s:default</td>
80
+ <td>list default settings</td>
81
+ </tr>
82
+ </table>
83
+
84
+ ## Throwing switches remotely with Capistrano ##
85
+
86
+ This is the minimum needed in the TARGET task:
87
+
88
+ task :production do
89
+ role :app, 'ec2-88-77-66-55.compute-1.amazonaws.com'
90
+ role :app, '177.133.33.144'
91
+
92
+ set :rails_env, 'production'
93
+ set :deploy_to, '/data/my_app'
94
+ set :gfs, false
95
+ end
96
+
97
+ The switches will get applied to any role that matches <tt>/app/</tt> (so :app_master, :app, etc.)
98
+
99
+ ## Usage ##
100
+
101
+ You can do stuff like (in <tt>app/models/user.rb</tt>):
102
+
103
+ after_create :subscribe_email if Switches.campaign_monitor?
104
+ def subscribe_email
105
+ CampaignMonitor.subscribe email
106
+ end
107
+
108
+ Uhh ohh! Campaign Monitor's API is down and you need to shut off those failing after_creates, like, NOW.
109
+
110
+ production-server-1:/var/www/apps/my_app $ rake s:off[campaign_monitor]
111
+ production-server-1:/var/www/apps/my_app $ sudo monit restart all -g my_app
112
+ [...]
113
+ production-server-2:/var/www/apps/my_app $ rake s:off[campaign_monitor]
114
+ production-server-2:/var/www/apps/my_app $ sudo monit restart all -g my_app
115
+
116
+ Or, even better, do it with cap:
117
+
118
+ my-macbook:~/my_app $ cap production s:off ARG=campaign_monitor
119
+ my-macbook:~/my_app $ cap production mongrel:restart
120
+
121
+ For another example, let's say you're a developer who doesn't have a self-signed certificate:
122
+
123
+ my-macbook:~/my_app $ rake s:off[ssl]
124
+
125
+ Those changes get persisted in <tt>config/switches/current.yml</tt>.
126
+
127
+ If you want to see your switches vis-a-vis the defaults:
128
+
129
+ my-macbook:~/my_app $ rake s:d
130
+ ---
131
+ ssl: true => false
132
+
133
+ And if you want to go back to the defaults:
134
+
135
+ my-macbook:~/my_app $ rake s:reset
136
+
137
+ Remember, you should version control <tt>config/switches/defaults.yml</tt> and ignore <tt>config/switches/current.yml</tt>.
138
+
139
+ ## Rationale ##
140
+
141
+ Sometimes you just need an easy way to "turn off" code.
142
+
143
+ ## Copyright ##
144
+
145
+ Copyright (c) 2009 Seamus Abshere. See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/lib/switches.rb CHANGED
@@ -6,14 +6,17 @@ require 'activesupport'
6
6
 
7
7
  module Switches
8
8
  CONFIG_DIR = File.join RAILS_ROOT, 'config', 'switches'
9
- TASKS_DIR = File.join RAILS_ROOT, 'lib', 'tasks'
9
+ RAKE_PATH = File.join RAILS_ROOT, 'lib', 'tasks', 'switches.rake'
10
+ CAPISTRANO_PATH = File.join CONFIG_DIR, 'capistrano_tasks.rb'
11
+ CAPISTRANO_LOAD_PATH = CAPISTRANO_PATH.gsub "#{RAILS_ROOT}/", '' # => 'config/switches/capistrano_tasks.rb'
12
+ CAPFILE_PATH = File.join RAILS_ROOT, 'Capfile'
10
13
  CURRENT_PATH = File.join CONFIG_DIR, 'current.yml'
11
14
  DEFAULT_PATH = File.join CONFIG_DIR, 'default.yml'
12
15
  BACKUP_PATH = File.join CONFIG_DIR, 'backup.yml'
13
16
 
14
17
  class << self
15
18
  def say(str)
16
- $stderr.puts "[SWITCHES GEM] #{str}"
19
+ $stderr.puts "[SWITCHES GEM] #{str.gsub "#{RAILS_ROOT}/", ''}"
17
20
  end
18
21
 
19
22
  def setup
@@ -29,12 +32,33 @@ module Switches
29
32
  File.open(DEFAULT_PATH, 'w') { |f| f.write({ 'quick_brown' => true, 'fox_jumps' => false }.to_yaml) }
30
33
  end
31
34
 
32
- say "Copying Rake tasks into #{TASKS_DIR}."
33
- FileUtils.cp File.join(File.dirname(__FILE__), 'tasks', 'switches.rake'), TASKS_DIR
35
+ say "Refreshing gem-related Rake tasks at #{RAKE_PATH}."
36
+ FileUtils.cp File.join(File.dirname(__FILE__), 'tasks', 'switches.rake'), RAKE_PATH
37
+
38
+ say "Refreshing gem-related Capistrano tasks at #{CAPISTRANO_PATH}."
39
+ FileUtils.cp File.join(File.dirname(__FILE__), 'tasks', 'capistrano_tasks.rb'), CAPISTRANO_PATH
40
+
41
+ needs_append = false
42
+ if not File.exists?(CAPFILE_PATH)
43
+ say "Creating a Capfile and including our tasks in it."
44
+ needs_append = true
45
+ FileUtils.touch CAPFILE_PATH
46
+ elsif old_capfile = IO.read(CAPFILE_PATH) and old_capfile.include?(CAPISTRANO_LOAD_PATH)
47
+ say "Found a Capfile that already includes our tasks. Great!"
48
+ else
49
+ say "I'm going to add a line to your existing Capfile. Sorry if I break anything!"
50
+ needs_append = true
51
+ end
52
+
53
+ File.open(CAPFILE_PATH, 'a') do |f|
54
+ say "Appending a line that loads our Capistrano tasks to your Capfile."
55
+ f.write "\n# Added by switches gem #{Time.now}\nload '#{CAPISTRANO_LOAD_PATH}'\n"
56
+ end if needs_append
34
57
 
35
58
  say "Don't forget to:"
36
59
  say "* git add #{DEFAULT_PATH}"
37
- say "* git add #{TASKS_DIR}/switches.rake"
60
+ say "* git add #{RAKE_PATH}"
61
+ say "* git add #{CAPISTRANO_PATH}"
38
62
  say "* put 'config/switches/current.yml' to your .gitignore"
39
63
  say "You can refresh the gem tasks with Switches.setup. It won't touch anything else."
40
64
  end
@@ -0,0 +1,71 @@
1
+ # assumes you've got roles, deploy_to, rails_env
2
+
3
+ require 'pp'
4
+ require 'activesupport'
5
+ require 'zlib'
6
+
7
+ # basically { :a => 1, :b => 2 }.eql?({ :b => 2, :a => 1}) => true
8
+ class StrictComparableHash < Hash
9
+ def eql?(other)
10
+ self == other
11
+ end
12
+
13
+ # must return an integer or #eql? will be ignored by Comparable
14
+ # assumes hash elements are strings
15
+ # will break if a value is something else whose to_s varies randomly
16
+ def hash
17
+ Zlib.crc32(to_a.map(&:to_s).sort.to_s)
18
+ end
19
+ end
20
+
21
+ # Hash.from_xml(a.gsub(/\n+/, ''))['hash']
22
+
23
+ namespace :s do
24
+ %w{ c d on off clear reset backup restore default }.each do |cmd|
25
+ task cmd.to_sym, :roles => lambda { roles.keys.map(&:to_s).grep(/app/).map(&:to_sym) } do
26
+
27
+ # download switches xml from servers
28
+ raw_input = Hash.new
29
+ run "cd #{deploy_to}/current; rake --silent s:#{cmd}#{"[#{ENV['ARG']}]" if ENV['ARG'].present?} RAILS_ENV=#{rails_env}; true" do |channel, stream, data|
30
+ server = channel[:server]
31
+ server_identifier = gfs ? server.host : "#{server.host}:#{server.port}".chomp(':')
32
+ raw_input[server_identifier] ||= Array.new
33
+ raw_input[server_identifier] << data
34
+ end
35
+
36
+ grouped_by_server = raw_input.inject(Hash.new) do |memo, ary|
37
+ server_identifier, chunks = ary
38
+ server_hash = Hash.from_xml(chunks.join.gsub(/\n+/, ''))['hash']
39
+ memo[server_identifier] = server_hash.is_a?(Hash) ? StrictComparableHash[server_hash] : StrictComparableHash.new
40
+ memo
41
+ end
42
+
43
+ first_server_switches = grouped_by_server.values.first
44
+
45
+ # If GFS, all server switches will be the same---just output them
46
+ if gfs
47
+ pp first_server_switches
48
+ else
49
+ grouped_by_switches = grouped_by_server.inject(Hash.new) do |memo, ary|
50
+ server_identifier, comparable_switches = ary
51
+ memo[comparable_switches] ||= Array.new
52
+ memo[comparable_switches] << server_identifier
53
+ memo
54
+ end
55
+ if grouped_by_switches.keys.length > 1
56
+ puts "Servers are different"
57
+ pp grouped_by_switches.invert
58
+ if grouped_by_switches.length == 2
59
+ a, b = grouped_by_switches.invert.values
60
+ puts
61
+ puts "Difference is"
62
+ pp a.diff(b)
63
+ end
64
+ else
65
+ puts "Servers are the same"
66
+ pp first_server_switches
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,88 +1,54 @@
1
1
  require 'switches'
2
2
 
3
- task :switches do
4
- Rake::Task['switches:default'].execute
5
- end
6
-
7
3
  namespace :s do
8
- task :c => 'switches:list_current'
9
- task :d => 'switches:diff'
10
- task :on, :name do |t, args|
11
- Rake::Task['switches:turn_on'].execute args
12
- end
13
- task :off, :name do |t, args|
14
- Rake::Task['switches:turn_off'].execute args
4
+ desc "List current"
5
+ task :c do
6
+ puts Switches.current.to_xml
15
7
  end
16
- end
17
8
 
18
- namespace :switches do
19
- desc "List current and show instructions"
20
- task :default do
21
- puts <<-EOS
22
-
23
- Here's a bunch of ugly instructions:
24
-
25
- List current settings:
26
- rake s:c
27
- Show difference between current and default settings:
28
- rake s:d
29
- Clear with:
30
- rake switches:clear[FOOBAR]
31
-
32
- ... now listing current settings ...
33
- EOS
34
- Rake::Task['switches:list_current'].execute
35
- end
36
-
37
- # not called :default so it doesn't look like the default task
38
- desc "List default"
39
- task :list_default do
40
- puts Switches.default.to_yaml
41
- end
42
-
43
- desc "List current"
44
- task :list_current do
45
- puts Switches.current.to_yaml
9
+ desc "Diff current vs. default switches"
10
+ task :d do
11
+ puts Switches.diff.to_xml
46
12
  end
47
-
13
+
48
14
  desc "Turn on switch"
49
- task :turn_on, :name do |t, args|
15
+ task :on, :name do |t, args|
50
16
  Switches.turn_on args.name
51
- puts Switches.current.to_yaml
17
+ puts Switches.current.to_xml
52
18
  end
53
19
 
54
20
  desc "Turn off switch"
55
- task :turn_off, :name do |t, args|
21
+ task :off, :name do |t, args|
56
22
  Switches.turn_off args.name
57
- puts Switches.current.to_yaml
23
+ puts Switches.current.to_xml
58
24
  end
59
-
25
+
60
26
  desc "Clear switch"
61
27
  task :clear, :name do |t, args|
62
28
  Switches.clear args.name
63
- puts Switches.current.to_yaml
64
- end
65
-
66
- desc "Diff current vs. default switches"
67
- task :diff do
68
- puts Switches.diff.to_yaml
29
+ puts Switches.current.to_xml
69
30
  end
70
-
31
+
71
32
  desc "Reset all switches to defaults"
72
33
  task :reset do
73
34
  Switches.reset
74
- puts Switches.current.to_yaml
35
+ puts Switches.current.to_xml
75
36
  end
76
-
37
+
77
38
  desc "Backup all switches to defaults"
78
39
  task :backup do
79
40
  Switches.backup
80
- puts Switches.current.to_yaml
41
+ puts Switches.current.to_xml
81
42
  end
82
-
43
+
83
44
  desc "Restore all switches to defaults"
84
45
  task :restore do
85
46
  Switches.restore
86
- puts Switches.current.to_yaml
47
+ puts Switches.current.to_xml
48
+ end
49
+
50
+ desc "List default"
51
+ task :default do
52
+ puts Switches.default.to_xml
87
53
  end
88
54
  end
data/switches.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{switches}
8
- s.version = "0.1.0"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Seamus Abshere"]
12
- s.date = %q{2009-10-30}
12
+ s.date = %q{2009-11-02}
13
13
  s.description = %q{
14
14
  Switches lets you turn on and off parts of your code from the commandline. There's a defaults.yml and a current.yml in the background.
15
15
 
@@ -37,16 +37,20 @@ It's inspired by ActiveSupport's StringInquirer (e.g. Rails.development?) and tr
37
37
  s.email = %q{seamus@abshere.net}
38
38
  s.extra_rdoc_files = [
39
39
  "LICENSE",
40
- "README.rdoc"
40
+ "README",
41
+ "README.html",
42
+ "README.markdown"
41
43
  ]
42
44
  s.files = [
43
45
  ".document",
44
46
  ".gitignore",
45
47
  "LICENSE",
46
- "README.rdoc",
48
+ "README",
49
+ "README.markdown",
47
50
  "Rakefile",
48
51
  "VERSION",
49
52
  "lib/switches.rb",
53
+ "lib/tasks/capistrano_tasks.rb",
50
54
  "lib/tasks/switches.rake",
51
55
  "spec/spec.opts",
52
56
  "spec/spec_helper.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switches
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seamus Abshere
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-30 00:00:00 -04:00
12
+ date: 2009-11-02 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -55,20 +55,25 @@ extensions: []
55
55
 
56
56
  extra_rdoc_files:
57
57
  - LICENSE
58
- - README.rdoc
58
+ - README
59
+ - README.html
60
+ - README.markdown
59
61
  files:
60
62
  - .document
61
63
  - .gitignore
62
64
  - LICENSE
63
- - README.rdoc
65
+ - README
66
+ - README.markdown
64
67
  - Rakefile
65
68
  - VERSION
66
69
  - lib/switches.rb
70
+ - lib/tasks/capistrano_tasks.rb
67
71
  - lib/tasks/switches.rake
68
72
  - spec/spec.opts
69
73
  - spec/spec_helper.rb
70
74
  - spec/switches_spec.rb
71
75
  - switches.gemspec
76
+ - README.html
72
77
  has_rdoc: true
73
78
  homepage: http://github.com/seamusabshere/switches
74
79
  licenses: []
data/README.rdoc DELETED
@@ -1,81 +0,0 @@
1
- = switches
2
-
3
- Switches lets you turn on and off sections of your code with <tt>Switches.foobar?</tt> booleans.
4
-
5
- == Quick start
6
-
7
- Add 2 lines to <tt>config/environment.rb</tt>:
8
-
9
- require File.join(File.dirname(__FILE__), 'boot')
10
- [...]
11
- require 'switches' # AFTER boot, BEFORE initializer
12
- [...]
13
- Rails::Initializer.run do |config|
14
- [...]
15
- config.gem 'switches', :lib => false # INSIDE initializer
16
-
17
- Now run this:
18
-
19
- my-macbook:~/my_app $ ./script/runner 'Switches.setup'
20
-
21
- Add your defaults to <tt>config/switches/defaults.yml</tt>:
22
-
23
- ---
24
- ssl: true # ssl support is on by default
25
- campaign_monitor: true # campaign monitor integration is on by default
26
-
27
- == Rake tasks
28
-
29
- rake s:c # show current switches
30
- rake s:d # show difference between current and default switches
31
- rake s:on[foobar] # turn on "foobar"
32
- rake s:off[foobar] # turn off "foobar"
33
-
34
- rake switches:clear[foobar] # clear any switch for "foobar"
35
- rake switches:reset # go back to defaults
36
- rake switches:backup # backup current switches
37
- rake switches:restore # restore backed-up switches
38
-
39
- == Usage
40
-
41
- You can do stuff like (in <tt>app/models/user.rb</tt>):
42
-
43
- after_create :subscribe_email if Switches.campaign_monitor?
44
- def subscribe_email
45
- CampaignMonitor.subscribe email
46
- end
47
-
48
- Uhh ohh! Campaign Monitor's API is down and you need to shut off those failing after_creates, like, NOW.
49
-
50
- production-server:/var/www/apps/my_app $ rake s:off[campaign_monitor]
51
- production-server:/var/www/apps/my_app $ sudo monit restart all -g my_app
52
-
53
- Or let's say you're a developer who doesn't have a self-signed certificate:
54
-
55
- my-macbook:~/my_app $ rake s:off[ssl]
56
-
57
- Those changes get persisted in <tt>config/switches/current.yml</tt>.
58
-
59
- If you want to see your switches vis-a-vis the defaults:
60
-
61
- my-macbook:~/my_app $ rake s:d
62
- ---
63
- ssl: true => false
64
-
65
- And if you want to go back to the defaults:
66
-
67
- my-macbook:~/my_app $ rake switches:reset
68
-
69
- Remember, you should version control <tt>config/switches/defaults.yml</tt> and ignore <tt>config/switches/current.yml</tt>.
70
-
71
- == Rationale
72
-
73
- Sometimes you just need an easy way to "turn off" code.
74
-
75
- == Wishlist
76
-
77
- * capistrano task like "cap production s:off[campaign_monitor]"
78
-
79
- == Copyright
80
-
81
- Copyright (c) 2009 Seamus Abshere. See LICENSE for details.