heroku_config_vars 0.0.2.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +1 -0
  3. data/Rakefile +30 -0
  4. data/app/assets/javascripts/heroku_config_vars/application.js +15 -0
  5. data/app/assets/javascripts/heroku_config_vars/var_form.js +28 -0
  6. data/app/assets/stylesheets/heroku_config_vars/application.css +31 -0
  7. data/app/assets/stylesheets/heroku_config_vars/button.css.scss +54 -0
  8. data/app/assets/stylesheets/heroku_config_vars/menu.css.scss +36 -0
  9. data/app/assets/stylesheets/heroku_config_vars/table.css.scss +28 -0
  10. data/app/controllers/heroku_config_vars/application_controller.rb +46 -0
  11. data/app/controllers/heroku_config_vars/heroku_apps_controller.rb +50 -0
  12. data/app/helpers/heroku_config_vars/application_helper.rb +8 -0
  13. data/app/models/heroku_config_vars/heroku_app.rb +138 -0
  14. data/app/views/heroku_config_vars/application/env.html.erb +14 -0
  15. data/app/views/heroku_config_vars/application/recommend_https.html.erb +6 -0
  16. data/app/views/heroku_config_vars/heroku_apps/_errors.html.erb +5 -0
  17. data/app/views/heroku_config_vars/heroku_apps/confirmation.html.erb +73 -0
  18. data/app/views/heroku_config_vars/heroku_apps/edit.html.erb +37 -0
  19. data/app/views/heroku_config_vars/heroku_apps/new.html.erb +21 -0
  20. data/app/views/heroku_config_vars/heroku_apps/show.html.erb +20 -0
  21. data/app/views/layouts/heroku_config_vars/application.html.erb +20 -0
  22. data/config/routes.rb +5 -0
  23. data/lib/heroku_config_vars/engine.rb +12 -0
  24. data/lib/heroku_config_vars/version.rb +3 -0
  25. data/lib/heroku_config_vars.rb +5 -0
  26. data/lib/tasks/heroku_config_vars_tasks.rake +4 -0
  27. data/spec/dummy/README.rdoc +261 -0
  28. data/spec/dummy/Rakefile +7 -0
  29. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  30. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  31. data/spec/dummy/app/controllers/application_controller.rb +8 -0
  32. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  33. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  34. data/spec/dummy/config/application.rb +49 -0
  35. data/spec/dummy/config/boot.rb +10 -0
  36. data/spec/dummy/config/environment.rb +5 -0
  37. data/spec/dummy/config/environments/development.rb +29 -0
  38. data/spec/dummy/config/environments/test.rb +29 -0
  39. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/spec/dummy/config/initializers/inflections.rb +15 -0
  41. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  42. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  43. data/spec/dummy/config/initializers/session_store.rb +8 -0
  44. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  45. data/spec/dummy/config/locales/en.yml +5 -0
  46. data/spec/dummy/config/routes.rb +4 -0
  47. data/spec/dummy/config.ru +4 -0
  48. data/spec/dummy/log/test.log +6002 -0
  49. data/spec/dummy/public/404.html +26 -0
  50. data/spec/dummy/public/422.html +26 -0
  51. data/spec/dummy/public/500.html +25 -0
  52. data/spec/dummy/public/favicon.ico +0 -0
  53. data/spec/dummy/script/rails +6 -0
  54. data/spec/dummy/tmp/cache/assets/C20/540/sprockets%2F9f7052464a49612e023f9088fa442053 +0 -0
  55. data/spec/dummy/tmp/cache/assets/C97/2C0/sprockets%2F3945d527e8ea800568e77dd095c67391 +0 -0
  56. data/spec/dummy/tmp/cache/assets/C9B/060/sprockets%2Fa20484759fec963d3f51839934c3848f +0 -0
  57. data/spec/dummy/tmp/cache/assets/CAC/5E0/sprockets%2F425b0f4900e04b7736af6e64c4f46902 +0 -0
  58. data/spec/dummy/tmp/cache/assets/CBA/440/sprockets%2F8291695bea92c347d6a509dfa2379810 +0 -0
  59. data/spec/dummy/tmp/cache/assets/CBF/C20/sprockets%2Fdd57917c8268e99fe0a17332550ef632 +0 -0
  60. data/spec/dummy/tmp/cache/assets/CD9/8E0/sprockets%2F0681932233ab9af521f58c34cbf091f5 +0 -0
  61. data/spec/dummy/tmp/cache/assets/CF0/D20/sprockets%2F6997e8f2f3f34b07c51d6689d3ec5120 +0 -0
  62. data/spec/dummy/tmp/cache/assets/CFA/C10/sprockets%2Fca5852e7ebc212e12405091c0ca7a449 +0 -0
  63. data/spec/dummy/tmp/cache/assets/D0C/BD0/sprockets%2F7e5673b3d2e6c24b8a290ff222a4843d +0 -0
  64. data/spec/dummy/tmp/cache/assets/D0F/CF0/sprockets%2Fa54d897b366850d6aff2583dcd21141b +0 -0
  65. data/spec/dummy/tmp/cache/assets/D11/650/sprockets%2Fa79c1979284b6712e0a09bdde83c711b +0 -0
  66. data/spec/dummy/tmp/cache/assets/D2A/5E0/sprockets%2F8f9f3295a33983dafe83cbc75895a346 +0 -0
  67. data/spec/dummy/tmp/cache/assets/D2F/830/sprockets%2Fd6b510595130dc04df17a75eae623ee0 +0 -0
  68. data/spec/dummy/tmp/cache/assets/D3A/0E0/sprockets%2F12c0c4243a0757c81af09989ece2d4dc +0 -0
  69. data/spec/dummy/tmp/cache/assets/D3A/B10/sprockets%2Fa6033a4a85f3d8f4f5a68c3215bc64b6 +0 -0
  70. data/spec/dummy/tmp/cache/assets/D44/BB0/sprockets%2F6f8a1b369f3e20121751ff9356bebf8c +0 -0
  71. data/spec/dummy/tmp/cache/assets/D70/B70/sprockets%2Fa1415c8ffaef728e4e786c0e48061ac2 +0 -0
  72. data/spec/dummy/tmp/cache/assets/D94/CA0/sprockets%2Fb36822d80e1aee3257c5cf2f4ddeb043 +0 -0
  73. data/spec/dummy/tmp/cache/assets/DAA/850/sprockets%2F7e2ddb2b088e0c526941fef48af19ff5 +0 -0
  74. data/spec/dummy/tmp/cache/assets/DC3/970/sprockets%2Fd5d2e0cc86cec4e411aa31c549fcb870 +0 -0
  75. data/spec/dummy/tmp/capybara/capybara-201302162255389397158257.html +56 -0
  76. data/spec/dummy/tmp/capybara/capybara-201302162255545698499887.html +56 -0
  77. data/spec/dummy/tmp/capybara/capybara-201302162256212974676040.html +56 -0
  78. data/spec/dummy/tmp/capybara/capybara-201302162256278576954261.html +56 -0
  79. data/spec/dummy/tmp/capybara/capybara-201302200948319507521481.html +26 -0
  80. data/spec/dummy/tmp/capybara/capybara-20130220094839395273181.html +46 -0
  81. data/spec/dummy/tmp/pids/server.pid +1 -0
  82. data/spec/features/config_vars/edit_spec.rb +102 -0
  83. data/spec/features/config_vars/show_spec.rb +39 -0
  84. data/spec/features/env/show_spec.rb +16 -0
  85. data/spec/features/security/requires_admin_spec.rb +9 -0
  86. data/spec/features/security/suggests_ssl_spec.rb +28 -0
  87. data/spec/features/setup/new_app_spec.rb +42 -0
  88. data/spec/helpers/application_helper_spec.rb +15 -0
  89. data/spec/lib/heroku_config_vars_spec.rb +6 -0
  90. data/spec/models/heroku_config_vars/heroku_app_spec.rb +185 -0
  91. data/spec/spec_helper.rb +39 -0
  92. data/spec/support/heroku_responses.rb +35 -0
  93. data/spec/views/heroku_config_vars/application/recommend_https_spec.rb +14 -0
  94. metadata +336 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
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 @@
1
+ TODO
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'HerokuConfigVars'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ load "rspec/rails/tasks/rspec.rake"
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,28 @@
1
+
2
+ $(function(){
3
+
4
+ // Make deleting nice
5
+ $('td.delete').each(function(){
6
+ var $td = $(this);
7
+ var $tr = $td.parents('tr');
8
+ var $box = $td.find('[type="checkbox"]');
9
+ //$box.hide()
10
+ $box.change(function(){
11
+ $box.get(0).checked ? $tr.addClass('deleted') : $tr.removeClass('deleted');
12
+ });
13
+ });
14
+
15
+ // Dynamically add new text fields as they're used
16
+ $('tr.new input.new_key').blur(function(){
17
+ var $input = $(this)
18
+ var input = this;
19
+ var $tr = $input.parents('tr');
20
+
21
+ if ( $input.val() == '' || $tr.next().length > 0 ) return;
22
+
23
+ $tr.clone(true).insertAfter($tr).find('input').each(function(){
24
+ $(this).val('');
25
+ });
26
+ });
27
+
28
+ });
@@ -0,0 +1,31 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
14
+
15
+ body {
16
+ font-family: helvetica;
17
+ font-size: smaller;
18
+ }
19
+
20
+ a {
21
+ color: #66f;
22
+ text-decoration: none;
23
+ }
24
+
25
+ a:hover {
26
+ color: #fff;
27
+ background-color: #66f; }
28
+
29
+ label {
30
+ cursor: pointer;
31
+ }
@@ -0,0 +1,54 @@
1
+ .button {
2
+ color: #fff;
3
+ border: 1px solid;
4
+ border-radius: 3px;
5
+ text-align: center;
6
+ padding: 5px 16px;
7
+ font-size: 13px;
8
+ font-weight: 600;
9
+ cursor: pointer;
10
+ overflow: visible;
11
+ text-decoration: none;
12
+
13
+ &.happy {
14
+ $hue-adjustment: 260;
15
+ border-top-color: adjust-hue(#2270ab, $hue-adjustment);
16
+ border-right-color: adjust-hue(#18639a, $hue-adjustment);
17
+ border-bottom-color: adjust-hue(#0f568b, $hue-adjustment);
18
+ border-left-color: adjust-hue(#18639a, $hue-adjustment);
19
+ background: adjust-hue(#2180ce, $hue-adjustment);
20
+ background: -webkit-gradient(linear, left top, left bottom, from(adjust-hue(#33a0e8, $hue-adjustment)), to(adjust-hue(#2180ce, $hue-adjustment)));
21
+ text-shadow: adjust-hue(#355782, $hue-adjustment) 0 1px 2px;
22
+ box-shadow: 0 1px 1px rgba(0,0,0,0.3),inset 0px 1px 0px adjust-hue(#83c5f1, $hue-adjustment);
23
+
24
+ &:hover {
25
+ border-top-color: adjust-hue(#0070cc, $hue-adjustment);
26
+ border-right-color: adjust-hue(#006ac1, $hue-adjustment);
27
+ border-bottom-color: adjust-hue(#005ca6, $hue-adjustment);
28
+ border-left-color: adjust-hue(#006ac1, $hue-adjustment);
29
+ box-shadow: 0 1px 1px rgba(0,0,0,0.3),inset 0 0 3px adjust-hue(#35bff4, $hue-adjustment);
30
+ background: -webkit-gradient(linear, left top, left bottom, from(adjust-hue(#43b0f8, $hue-adjustment)), to(adjust-hue(#3190de, $hue-adjustment)));
31
+ }
32
+ }
33
+
34
+ &.sad {
35
+ $hue-adjustment: 150;
36
+ border-top-color: adjust-hue(#2270ab, $hue-adjustment);
37
+ border-right-color: adjust-hue(#18639a, $hue-adjustment);
38
+ border-bottom-color: adjust-hue(#0f568b, $hue-adjustment);
39
+ border-left-color: adjust-hue(#18639a, $hue-adjustment);
40
+ background: adjust-hue(#2180ce, $hue-adjustment);
41
+ background: -webkit-gradient(linear, left top, left bottom, from(adjust-hue(#33a0e8, $hue-adjustment)), to(adjust-hue(#2180ce, $hue-adjustment)));
42
+ text-shadow: adjust-hue(#355782, $hue-adjustment) 0 1px 2px;
43
+ box-shadow: 0 1px 1px rgba(0,0,0,0.3),inset 0px 1px 0px adjust-hue(#83c5f1, $hue-adjustment);
44
+
45
+ &:hover {
46
+ border-top-color: adjust-hue(#0070cc, $hue-adjustment);
47
+ border-right-color: adjust-hue(#006ac1, $hue-adjustment);
48
+ border-bottom-color: adjust-hue(#005ca6, $hue-adjustment);
49
+ border-left-color: adjust-hue(#006ac1, $hue-adjustment);
50
+ box-shadow: 0 1px 1px rgba(0,0,0,0.3),inset 0 0 3px adjust-hue(#35bff4, $hue-adjustment);
51
+ background: -webkit-gradient(linear, left top, left bottom, from(adjust-hue(#43b0f8, $hue-adjustment)), to(adjust-hue(#3190de, $hue-adjustment)));
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,36 @@
1
+ ul.submenu {
2
+ height: 32px;
3
+ list-style: none;
4
+ margin: 0 -20px;
5
+ padding: 0 20px;
6
+ background: #eee;
7
+ border-bottom: 1px #ccc solid;
8
+ clear: both;
9
+
10
+ li.admin {
11
+ float: right;
12
+ }
13
+ .menu-option {
14
+ font-size: 16px;
15
+ float: left;
16
+ padding: 0.25em 1em;
17
+ margin: 5px 10px -1px 0;
18
+ border: 1px solid #ccc;
19
+ height: 18px;
20
+ border-bottom-color: #fff;
21
+ background: #fff;
22
+ }
23
+
24
+ a {
25
+ padding: 0;
26
+ }
27
+
28
+ a .menu-option {
29
+ border-bottom: none;
30
+ }
31
+
32
+ a:hover .menu-option {
33
+ background: #aaa;
34
+ }
35
+
36
+ }
@@ -0,0 +1,28 @@
1
+ table {
2
+ border-collapse: collapse;
3
+
4
+ th {
5
+ text-align: left;
6
+ }
7
+
8
+ td, th {
9
+ border: 1px #ccc;
10
+ border-style: solid none;
11
+ padding: 0.5em;
12
+ }
13
+
14
+ input {
15
+ padding: 0.25em;
16
+ }
17
+
18
+ input.new_value,
19
+ input.var_value {
20
+ width: 50em;
21
+ }
22
+
23
+ tr.deleted, tr.deleted input {
24
+ text-decoration: line-through;
25
+ color: #bbb;
26
+ }
27
+ }
28
+
@@ -0,0 +1,46 @@
1
+ class HerokuConfigVars::ApplicationController < ApplicationController
2
+
3
+ before_filter :require_authenticated
4
+ before_filter :recommend_https, :if => :insecure?
5
+
6
+ AUTH_METHOD = :heroku_config_authorized?
7
+
8
+ def env
9
+ @env = ENV
10
+ end
11
+
12
+ private
13
+
14
+ def require_authenticated
15
+ # This is where we shell out to ApplicationController
16
+ # raising RoutingError will render 404 in production
17
+ if not respond_to? AUTH_METHOD
18
+ raise ActionController::RoutingError.new <<-ERROR.strip_heredoc
19
+ `#{AUTH_METHOD}` must be implemented in ApplicationController and return true for authorized users.
20
+
21
+ e.g.
22
+ class ApplicationController < ActionController::Base
23
+ ...
24
+
25
+ def #{AUTH_METHOD}
26
+ current_user.admin?
27
+ end
28
+
29
+ ...
30
+ end
31
+ ERROR
32
+ elsif not heroku_config_authorized?
33
+ raise ActionController::RoutingError.new ':heroku_config_authorized? returned false'
34
+ end
35
+ end
36
+
37
+
38
+ def recommend_https
39
+ render :recommend_https
40
+ end
41
+
42
+ def insecure?
43
+ not request.ssl? and (session[:insecure] ||= params[:insecure]) != 'ok'
44
+ end
45
+
46
+ end
@@ -0,0 +1,50 @@
1
+ module HerokuConfigVars
2
+ class HerokuAppsController < ApplicationController
3
+
4
+ before_filter :require_heroku_app, :only => [:show, :edit, :update]
5
+
6
+ def new
7
+ @heroku_app = HerokuApp.find
8
+ end
9
+
10
+ def create
11
+ @heroku_app = HerokuApp.new params[:heroku_app]
12
+ if @heroku_app.save
13
+ redirect_to heroku_app_path
14
+ else
15
+ render :new
16
+ end
17
+ end
18
+
19
+ def update
20
+ vars = params[:heroku_app][:vars]
21
+ vars.merge! hashify_new_vars
22
+ vars.except! *params[:remove].keys if params[:remove]
23
+ heroku_app.vars = vars
24
+ return render :confirmation unless params[:confirmation]
25
+ if heroku_app.save
26
+ redirect_to edit_heroku_app_path, notice: 'Configuration updated'
27
+ else
28
+ heroku_app.load_vars
29
+ render :edit
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def require_heroku_app
36
+ redirect_to new_heroku_app_path unless heroku_app && heroku_app.loaded?
37
+ end
38
+
39
+ def heroku_app
40
+ @heroku_app ||= HerokuApp.find
41
+ end
42
+
43
+ def hashify_new_vars
44
+ {}.tap do |vars|
45
+ params[:add].each {|h| vars.store h['key'], h['value'] if h['key'].present? } if params[:add]
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,8 @@
1
+ module HerokuConfigVars
2
+ module ApplicationHelper
3
+ def menu_link(text, url, html_options={})
4
+ text = content_tag :span, text, :class => 'menu-option'
5
+ link_to_unless_current text, url, html_options
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,138 @@
1
+ module HerokuConfigVars
2
+ class HerokuApp
3
+ extend ActiveModel::Naming
4
+ include ActiveModel::Conversion
5
+ include ActiveModel::Validations
6
+
7
+ attr_accessor :vars
8
+
9
+ attr_reader :loaded
10
+ attr_reader :current_vars
11
+
12
+ alias_method :loaded?, :loaded
13
+ alias_method :persisted?, :loaded
14
+
15
+ validates_presence_of :app_name
16
+ validates_presence_of :api_key
17
+
18
+ def self.find
19
+ attrs = {
20
+ app_name: ENV['HEROKU_APP_NAME'],
21
+ api_key: ENV['HEROKU_API_KEY']
22
+ }
23
+
24
+ new(attrs) do |app|
25
+ app.load_vars if app.valid?
26
+ end
27
+ end
28
+
29
+ def initialize(attrs={})
30
+ @vars = {}
31
+ @current_vars = {}
32
+ @loaded = false
33
+
34
+ self.attributes = attrs
35
+
36
+ yield self if block_given?
37
+ end
38
+
39
+ def attributes=(attrs={})
40
+ attrs.each do |key, value|
41
+ self.send "#{key}=", value
42
+ end
43
+ end
44
+
45
+ def app_name
46
+ @app_name ||= @vars['HEROKU_APP_NAME'].presence
47
+ end
48
+
49
+ def app_name=(value)
50
+ @app_name = @vars['HEROKU_APP_NAME'] = value
51
+ end
52
+
53
+ def api_key
54
+ @api_key ||= @vars['HEROKU_API_KEY'].presence
55
+ end
56
+
57
+ def api_key=(value)
58
+ @api_key = @vars['HEROKU_API_KEY'] = value
59
+ end
60
+
61
+ def id
62
+ app_name
63
+ end
64
+
65
+ def load_vars
66
+ catch_heroku_errors do
67
+ Rails.logger.debug 'Loading config'
68
+ @vars = connection.get_config_vars(app_name).body
69
+ @current_vars = @vars.dup.freeze
70
+ @loaded = true
71
+ end
72
+ end
73
+
74
+ def save
75
+ save_to_heroku and save_to_env if valid?
76
+ end
77
+
78
+ def removed_vars
79
+ current_vars ? current_vars.except(*vars.keys) : {}
80
+ end
81
+
82
+ def added_vars
83
+ vars.except *current_vars.keys
84
+ end
85
+
86
+ def kept_vars
87
+ current_vars.slice *vars.keys
88
+ end
89
+
90
+ def updated_vars
91
+ changes.slice(*kept_vars.keys)
92
+ end
93
+
94
+ def updated_and_added_vars
95
+ changes.slice(*vars.keys)
96
+ end
97
+
98
+ def changes
99
+ @changes ||= vars.diff current_vars
100
+ end
101
+
102
+ private
103
+
104
+ def connection
105
+ Heroku::API.new api_key: api_key
106
+ end
107
+
108
+ def catch_heroku_errors
109
+ Rails.logger.debug "Connecting to Heroku"
110
+ begin
111
+ yield
112
+ rescue Heroku::API::Errors::Unauthorized => e
113
+ errors.add :api_key, "is invalid (#{e.class.name})"
114
+ rescue Heroku::API::Errors::Forbidden, Heroku::API::Errors::NotFound => e
115
+ errors.add :app_name, "is invalid (#{e.class.name})"
116
+ rescue Heroku::API::Errors::RequestFailed => e
117
+ errors.add :base, "Request failed: " << e.response.body
118
+ end
119
+ errors.blank?
120
+ end
121
+
122
+ def save_to_heroku
123
+ Rails.logger.debug 'Updating config'
124
+ catch_heroku_errors { connection.put_config_vars(app_name, updated_and_added_vars) }
125
+ removed_vars.each do |key, _|
126
+ Rails.logger.debug "deleting #{key}"
127
+ catch_heroku_errors { connection.delete_config_var(app_name, key) }
128
+ end
129
+ errors.blank?
130
+ end
131
+
132
+ def save_to_env
133
+ ENV.update vars
134
+ removed_vars.each { |key, _| ENV.delete key }
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,14 @@
1
+ <h2>ENV</h2>
2
+
3
+ <table>
4
+ <%- @env.sort.each do |key, value| -%>
5
+ <tr>
6
+ <th>
7
+ <%= key %>
8
+ </th>
9
+ <td>
10
+ <%= value %>
11
+ </td>
12
+ </tr>
13
+ <%- end -%>
14
+ </table>
@@ -0,0 +1,6 @@
1
+ <h2>WARNING</h2>
2
+
3
+ <p>You are trying to view sensitive application configuration over an <strong>insecure connection</strong>.</p>
4
+ <p>It is highly recommended that you view this over https.</p>
5
+ <%= link_to 'Switch to secure connection', {only_path: false, protocol: 'https'}, class: 'happy button' %>
6
+ <%= link_to 'Continue insecurely', {insecure: :ok}, class: 'sad button' %>
@@ -0,0 +1,5 @@
1
+ <ul>
2
+ <%- heroku_app.errors.full_messages.each do |msg| -%>
3
+ <li><%= msg %></li>
4
+ <%- end -%>
5
+ </ul>
@@ -0,0 +1,73 @@
1
+ <h2>Please confirm...</h2>
2
+
3
+ <%- if @heroku_app.changes.any? %>
4
+ <%- if @heroku_app.added_vars.any? -%>
5
+ <h3>These configuration variables will be added:</h3>
6
+ <table>
7
+ <%- @heroku_app.added_vars.each do |key, value| -%>
8
+ <tr>
9
+ <th>
10
+ <%= key %>
11
+ </th>
12
+ <td>
13
+ <%= value %>
14
+ </td>
15
+ </tr>
16
+ <%- end -%>
17
+ </table>
18
+ <%- end -%>
19
+
20
+ <%- if @heroku_app.updated_vars.any? -%>
21
+ <h3>These configuration variables will be updated:</h3>
22
+ <table>
23
+ <%- @heroku_app.updated_vars.each do |key, value| -%>
24
+ <tr>
25
+ <th>
26
+ <%= key %>
27
+ </th>
28
+ <td>
29
+ <%= @heroku_app.current_vars[key] %>
30
+ </td>
31
+ <td>
32
+ &rarr;
33
+ </td>
34
+ <td>
35
+ <%= value %>
36
+ </td>
37
+ </tr>
38
+ <%- end -%>
39
+ </table>
40
+ <%- end -%>
41
+
42
+ <%- if @heroku_app.removed_vars.any? -%>
43
+ <h3>These configuration variables will be removed:</h3>
44
+ <table>
45
+ <%- @heroku_app.removed_vars.each do |key, value| -%>
46
+ <tr class="deleted">
47
+ <th>
48
+ <%= key %>
49
+ </th>
50
+ <td>
51
+ <%= value %>
52
+ </td>
53
+ </tr>
54
+ <%- end -%>
55
+ </table>
56
+ <%- end -%>
57
+
58
+ <%= form_for @heroku_app, url: {confirmation: true} do |f| %>
59
+ <%- @heroku_app.vars.each do |key, value| -%>
60
+ <%= hidden_field_tag "heroku_app[vars][#{key}]", value %>
61
+ <%- end -%>
62
+
63
+ <p>
64
+ <%= submit_tag "Update and restart app" %>
65
+ </p>
66
+ <%- end -%>
67
+
68
+ <%- else -%>
69
+ <p>No changes made.</p>
70
+ <%- end -%>
71
+
72
+
73
+ <p><%= link_to 'Cancel', edit_heroku_app_path %></p>
@@ -0,0 +1,37 @@
1
+ <h2>
2
+ <%= link_to @heroku_app.app_name, heroku_app_path %>
3
+ &rarr;
4
+ Edit Configuration
5
+ </h2>
6
+
7
+ <%= render 'errors', heroku_app: @heroku_app if @heroku_app.errors.any? %>
8
+
9
+ <%= form_for @heroku_app, url: heroku_app_path do |f| %>
10
+ <table>
11
+ <%- @heroku_app.vars.sort.each do |key, value| -%>
12
+ <tr>
13
+ <th><%= label_tag "heroku_app[vars][#{key}]", key %></th>
14
+ <td><%= text_field_tag "heroku_app[vars][#{key}]", value, class: 'var_value' %></td>
15
+ <td class="delete">
16
+ <%= label_tag "remove[#{key}]" do %>
17
+ <%= check_box_tag "remove[#{key}]" %>
18
+ Delete
19
+ <%- end -%>
20
+ </td>
21
+ </tr>
22
+ <%- end -%>
23
+ <tr class="new">
24
+ <th><%= text_field_tag "add[][key]", nil, id: nil, class: 'new_key' %></th>
25
+ <td><%= text_field_tag "add[][value]", nil, id: nil, class: 'new_value' %></td>
26
+ <td></td>
27
+ </tr>
28
+ </table>
29
+
30
+ <p>
31
+ <%= f.submit "Prepare..." %>
32
+ </p>
33
+ <%- end -%>
34
+
35
+ <p>
36
+ <%= link_to 'Cancel', heroku_app_path %>
37
+ </p>
@@ -0,0 +1,21 @@
1
+ <h2>One Time Setup</h2>
2
+
3
+ <%= render 'errors', heroku_app: @heroku_app if @heroku_app.errors.any? %>
4
+
5
+ <%= form_for @heroku_app, url: heroku_app_path do |f| %>
6
+ <h3>Connection Details</h3>
7
+ <table>
8
+ <tr>
9
+ <th><%= f.label :app_name, "App Name" %></th>
10
+ <td><%= f.text_field :app_name %></td>
11
+ </tr>
12
+ <tr>
13
+ <th><%= f.label :api_key, "API Key" %></th>
14
+ <td><%= f.text_field :api_key %></td>
15
+ </tr>
16
+ </table>
17
+
18
+ <p>
19
+ <%= f.submit "Save" %>
20
+ </p>
21
+ <%- end -%>
@@ -0,0 +1,20 @@
1
+ <h2>
2
+ <%= @heroku_app.app_name %>
3
+ </h2>
4
+
5
+ <table>
6
+ <%- @heroku_app.vars.sort.each do |key, value| -%>
7
+ <tr>
8
+ <th>
9
+ <%= key %>
10
+ </th>
11
+ <td>
12
+ <%= value %>
13
+ </td>
14
+ </tr>
15
+ <%- end -%>
16
+ </table>
17
+
18
+ <p>
19
+ <%= link_to 'Edit', edit_heroku_app_path %>
20
+ </p>