high_voltage 1.0.0 → 2.0.0

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.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +8 -0
  4. data/Appraisals +16 -0
  5. data/CONTRIBUTING.md +38 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +80 -0
  8. data/MIT-LICENSE +1 -1
  9. data/NEWS.md +23 -0
  10. data/README.md +213 -60
  11. data/Rakefile +19 -0
  12. data/app/controllers/concerns/high_voltage/static_page.rb +41 -0
  13. data/app/controllers/high_voltage/pages_controller.rb +1 -26
  14. data/config/routes.rb +3 -1
  15. data/high_voltage.gemspec +24 -0
  16. data/lib/high_voltage/constraints/root_route.rb +22 -0
  17. data/lib/high_voltage/engine.rb +9 -2
  18. data/lib/high_voltage/page_finder.rb +37 -0
  19. data/lib/high_voltage/route_drawers/default.rb +15 -0
  20. data/lib/high_voltage/route_drawers/root.rb +16 -0
  21. data/lib/high_voltage/version.rb +3 -0
  22. data/lib/high_voltage.rb +29 -0
  23. data/spec/constraints/root_route_spec.rb +25 -0
  24. data/spec/controllers/action_caching_controller_spec.rb +23 -0
  25. data/spec/controllers/alternative_finder_controller_spec.rb +12 -0
  26. data/spec/controllers/page_caching_controller_spec.rb +20 -0
  27. data/spec/controllers/pages_controller_spec.rb +128 -0
  28. data/spec/controllers/subclassed_pages_controller_spec.rb +40 -0
  29. data/spec/dummy/Rakefile +7 -0
  30. data/spec/dummy/app/controllers/alternative_finder_controller.rb +14 -0
  31. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  32. data/spec/dummy/app/controllers/subclassed_pages_controller.rb +3 -0
  33. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  34. data/spec/dummy/app/views/layouts/alternate.html.erb +14 -0
  35. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  36. data/spec/dummy/app/views/other/wrong.html.erb +1 -0
  37. data/spec/dummy/app/views/other_pages/also_dir/also_nested.html.erb +1 -0
  38. data/spec/dummy/app/views/other_pages/also_exists.html.erb +1 -0
  39. data/spec/dummy/app/views/other_pages/also_exists_but_references_nonexistent_partial.html.erb +2 -0
  40. data/spec/dummy/app/views/pages/also_dir/also_nested.html.erb +1 -0
  41. data/spec/dummy/app/views/pages/also_exists.html.erb +1 -0
  42. data/spec/dummy/app/views/pages/also_exists_but_references_nonexistent_partial.html.erb +2 -0
  43. data/spec/dummy/app/views/pages/dir/nested.html.erb +1 -0
  44. data/spec/dummy/app/views/pages/exists.html.erb +1 -0
  45. data/spec/dummy/app/views/pages/exists_but_references_nonexistent_partial.html.erb +2 -0
  46. data/spec/dummy/app/views/pages/rot13.html.erb +1 -0
  47. data/spec/dummy/config/application.rb +43 -0
  48. data/spec/dummy/config/boot.rb +10 -0
  49. data/spec/dummy/config/environment.rb +5 -0
  50. data/spec/dummy/config/environments/development.rb +25 -0
  51. data/spec/dummy/config/environments/production.rb +49 -0
  52. data/spec/dummy/config/environments/test.rb +29 -0
  53. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  54. data/spec/dummy/config/initializers/inflections.rb +10 -0
  55. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  56. data/spec/dummy/config/initializers/secret_key_base.rb +1 -0
  57. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  58. data/spec/dummy/config/initializers/session_store.rb +8 -0
  59. data/spec/dummy/config/locales/en.yml +5 -0
  60. data/spec/dummy/config/routes.rb +4 -0
  61. data/spec/dummy/config.ru +4 -0
  62. data/spec/dummy/public/404.html +26 -0
  63. data/spec/dummy/public/422.html +26 -0
  64. data/spec/dummy/public/500.html +26 -0
  65. data/spec/dummy/public/favicon.ico +0 -0
  66. data/spec/dummy/public/javascripts/application.js +2 -0
  67. data/spec/dummy/public/javascripts/controls.js +965 -0
  68. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  69. data/spec/dummy/public/javascripts/effects.js +1123 -0
  70. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  71. data/spec/dummy/public/javascripts/rails.js +191 -0
  72. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  73. data/spec/dummy/script/rails +6 -0
  74. data/spec/high_voltage/page_finder_spec.rb +52 -0
  75. data/spec/high_voltage_spec.rb +11 -0
  76. data/spec/integration/navigation_spec.rb +9 -0
  77. data/spec/minimal_spec_helper.rb +5 -0
  78. data/spec/routing/routes_spec.rb +142 -0
  79. data/spec/spec_helper.rb +19 -0
  80. data/spec/support/caching.rb +12 -0
  81. data/spec/support/concern_reload.rb +11 -0
  82. metadata +236 -42
@@ -0,0 +1,191 @@
1
+ (function() {
2
+ // Technique from Juriy Zaytsev
3
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
4
+ function isEventSupported(eventName) {
5
+ var el = document.createElement('div');
6
+ eventName = 'on' + eventName;
7
+ var isSupported = (eventName in el);
8
+ if (!isSupported) {
9
+ el.setAttribute(eventName, 'return;');
10
+ isSupported = typeof el[eventName] == 'function';
11
+ }
12
+ el = null;
13
+ return isSupported;
14
+ }
15
+
16
+ function isForm(element) {
17
+ return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
18
+ }
19
+
20
+ function isInput(element) {
21
+ if (Object.isElement(element)) {
22
+ var name = element.nodeName.toUpperCase()
23
+ return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
24
+ }
25
+ else return false
26
+ }
27
+
28
+ var submitBubbles = isEventSupported('submit'),
29
+ changeBubbles = isEventSupported('change')
30
+
31
+ if (!submitBubbles || !changeBubbles) {
32
+ // augment the Event.Handler class to observe custom events when needed
33
+ Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
34
+ function(init, element, eventName, selector, callback) {
35
+ init(element, eventName, selector, callback)
36
+ // is the handler being attached to an element that doesn't support this event?
37
+ if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
38
+ (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
39
+ // "submit" => "emulated:submit"
40
+ this.eventName = 'emulated:' + this.eventName
41
+ }
42
+ }
43
+ )
44
+ }
45
+
46
+ if (!submitBubbles) {
47
+ // discover forms on the page by observing focus events which always bubble
48
+ document.on('focusin', 'form', function(focusEvent, form) {
49
+ // special handler for the real "submit" event (one-time operation)
50
+ if (!form.retrieve('emulated:submit')) {
51
+ form.on('submit', function(submitEvent) {
52
+ var emulated = form.fire('emulated:submit', submitEvent, true)
53
+ // if custom event received preventDefault, cancel the real one too
54
+ if (emulated.returnValue === false) submitEvent.preventDefault()
55
+ })
56
+ form.store('emulated:submit', true)
57
+ }
58
+ })
59
+ }
60
+
61
+ if (!changeBubbles) {
62
+ // discover form inputs on the page
63
+ document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
64
+ // special handler for real "change" events
65
+ if (!input.retrieve('emulated:change')) {
66
+ input.on('change', function(changeEvent) {
67
+ input.fire('emulated:change', changeEvent, true)
68
+ })
69
+ input.store('emulated:change', true)
70
+ }
71
+ })
72
+ }
73
+
74
+ function handleRemote(element) {
75
+ var method, url, params;
76
+
77
+ var event = element.fire("ajax:before");
78
+ if (event.stopped) return false;
79
+
80
+ if (element.tagName.toLowerCase() === 'form') {
81
+ method = element.readAttribute('method') || 'post';
82
+ url = element.readAttribute('action');
83
+ params = element.serialize();
84
+ } else {
85
+ method = element.readAttribute('data-method') || 'get';
86
+ url = element.readAttribute('href');
87
+ params = {};
88
+ }
89
+
90
+ new Ajax.Request(url, {
91
+ method: method,
92
+ parameters: params,
93
+ evalScripts: true,
94
+
95
+ onComplete: function(request) { element.fire("ajax:complete", request); },
96
+ onSuccess: function(request) { element.fire("ajax:success", request); },
97
+ onFailure: function(request) { element.fire("ajax:failure", request); }
98
+ });
99
+
100
+ element.fire("ajax:after");
101
+ }
102
+
103
+ function handleMethod(element) {
104
+ var method = element.readAttribute('data-method'),
105
+ url = element.readAttribute('href'),
106
+ csrf_param = $$('meta[name=csrf-param]')[0],
107
+ csrf_token = $$('meta[name=csrf-token]')[0];
108
+
109
+ var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
110
+ element.parentNode.insert(form);
111
+
112
+ if (method !== 'post') {
113
+ var field = new Element('input', { type: 'hidden', name: '_method', value: method });
114
+ form.insert(field);
115
+ }
116
+
117
+ if (csrf_param) {
118
+ var param = csrf_param.readAttribute('content'),
119
+ token = csrf_token.readAttribute('content'),
120
+ field = new Element('input', { type: 'hidden', name: param, value: token });
121
+ form.insert(field);
122
+ }
123
+
124
+ form.submit();
125
+ }
126
+
127
+
128
+ document.on("click", "*[data-confirm]", function(event, element) {
129
+ var message = element.readAttribute('data-confirm');
130
+ if (!confirm(message)) event.stop();
131
+ });
132
+
133
+ document.on("click", "a[data-remote]", function(event, element) {
134
+ if (event.stopped) return;
135
+ handleRemote(element);
136
+ event.stop();
137
+ });
138
+
139
+ document.on("click", "a[data-method]", function(event, element) {
140
+ if (event.stopped) return;
141
+ handleMethod(element);
142
+ event.stop();
143
+ });
144
+
145
+ document.on("submit", function(event) {
146
+ var element = event.findElement(),
147
+ message = element.readAttribute('data-confirm');
148
+ if (message && !confirm(message)) {
149
+ event.stop();
150
+ return false;
151
+ }
152
+
153
+ var inputs = element.select("input[type=submit][data-disable-with]");
154
+ inputs.each(function(input) {
155
+ input.disabled = true;
156
+ input.writeAttribute('data-original-value', input.value);
157
+ input.value = input.readAttribute('data-disable-with');
158
+ });
159
+
160
+ var element = event.findElement("form[data-remote]");
161
+ if (element) {
162
+ handleRemote(element);
163
+ event.stop();
164
+ }
165
+ });
166
+
167
+ document.on("ajax:after", "form", function(event, element) {
168
+ var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
169
+ inputs.each(function(input) {
170
+ input.value = input.readAttribute('data-original-value');
171
+ input.removeAttribute('data-original-value');
172
+ input.disabled = false;
173
+ });
174
+ });
175
+
176
+ Ajax.Responders.register({
177
+ onCreate: function(request) {
178
+ var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
179
+
180
+ if (csrf_meta_tag) {
181
+ var header = 'X-CSRF-Token',
182
+ token = csrf_meta_tag.readAttribute('content');
183
+
184
+ if (!request.options.requestHeaders) {
185
+ request.options.requestHeaders = {};
186
+ }
187
+ request.options.requestHeaders[header] = token;
188
+ }
189
+ }
190
+ });
191
+ })();
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe HighVoltage::PageFinder do
4
+ it 'produces the name of an existing template' do
5
+ find('existing').should eq 'pages/existing'
6
+ end
7
+
8
+ it 'produces the name of a nested template' do
9
+ find('dir/nested').should eq 'pages/dir/nested'
10
+ end
11
+
12
+ it 'uses a custom content path' do
13
+ with_content_path('other_pages/') do
14
+ find('also_exists').should eq 'other_pages/also_exists'
15
+ end
16
+ end
17
+
18
+ it 'exposes the content path' do
19
+ with_content_path('another_thing/') do
20
+ page_finder.content_path.should eq 'another_thing/'
21
+ end
22
+ end
23
+
24
+ it 'provides the page_id' do
25
+ subclass = Class.new(HighVoltage::PageFinder) do
26
+ def page_name
27
+ "the page is #{page_id}"
28
+ end
29
+ end
30
+
31
+ subclass.new('sweet page').page_name.should eq 'the page is sweet page'
32
+ end
33
+
34
+ private
35
+
36
+ def find(page_id)
37
+ page_finder(page_id).find
38
+ end
39
+
40
+ def page_finder(page_id = 'whatever')
41
+ HighVoltage::PageFinder.new(page_id)
42
+ end
43
+
44
+ def with_content_path(path)
45
+ original_content_path = HighVoltage.content_path
46
+ HighVoltage.content_path = path
47
+
48
+ yield
49
+
50
+ HighVoltage.content_path = original_content_path
51
+ end
52
+ end
@@ -0,0 +1,11 @@
1
+ require 'minimal_spec_helper'
2
+
3
+ describe HighVoltage do
4
+ it 'should be valid' do
5
+ HighVoltage.should be_a(Module)
6
+ end
7
+
8
+ it 'should be loadable without preloading rails' do
9
+ expect { require 'high_voltage' }.not_to raise_error
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Navigation' do
4
+ include Capybara::DSL
5
+
6
+ it 'should be a valid app' do
7
+ ::Rails.application.should be_a(Dummy::Application)
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.configure do |config|
2
+ require 'rspec/expectations'
3
+ config.include RSpec::Matchers
4
+ config.mock_with :rspec
5
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'routes' do
4
+ context 'using default configuration' do
5
+ it 'should generate normal resource route with id' do
6
+ page_path('one').should eq '/pages/one'
7
+ end
8
+
9
+ it 'should generate normal resource route with string' do
10
+ page_path('one').should eq '/pages/one'
11
+ end
12
+
13
+ it 'should generate nested route with string' do
14
+ page_path('one/two').should eq '/pages/one/two'
15
+ end
16
+
17
+ it 'should recognize nested route' do
18
+ assert_recognizes(
19
+ {
20
+ :controller => 'high_voltage/pages',
21
+ :action => 'show',
22
+ :id => 'one/two'
23
+ },
24
+ '/pages/one/two'
25
+ )
26
+ end
27
+
28
+ it 'should recognize normal route' do
29
+ assert_recognizes(
30
+ {
31
+ :controller => 'high_voltage/pages',
32
+ :action => 'show',
33
+ :id => 'one'
34
+ },
35
+ '/pages/one'
36
+ )
37
+ end
38
+
39
+ it 'should recognize normal route with dots' do
40
+ assert_recognizes(
41
+ {
42
+ :controller => 'high_voltage/pages',
43
+ :action => 'show',
44
+ :id => 'one.two.three'
45
+ },
46
+ '/pages/one.two.three'
47
+ )
48
+ end
49
+ end
50
+
51
+ context 'using top-level routing configuration' do
52
+ around do |example|
53
+ cached_high_voltage_route_drawer = HighVoltage.route_drawer
54
+ HighVoltage.route_drawer = HighVoltage::RouteDrawers::Root
55
+ Rails.application.reload_routes!
56
+
57
+ example.run
58
+
59
+ HighVoltage.route_drawer = cached_high_voltage_route_drawer
60
+ Rails.application.reload_routes!
61
+ end
62
+
63
+ it 'should generate normal resource route with string' do
64
+ page_path('one').should eq '/one'
65
+ end
66
+
67
+ it 'should generate nested route with string' do
68
+ page_path('one/two').should eq '/one/two'
69
+ end
70
+ end
71
+
72
+ context 'using a custom content_path' do
73
+ around do |example|
74
+ cached_high_voltage_content_path = HighVoltage.content_path
75
+ HighVoltage.content_path = 'other_pages/'
76
+ Rails.application.reload_routes!
77
+
78
+ example.run
79
+
80
+ HighVoltage.content_path = cached_high_voltage_content_path
81
+ Rails.application.reload_routes!
82
+ end
83
+
84
+ it 'should generate normal resource route with string' do
85
+ page_path('one').should eq '/other_pages/one'
86
+ end
87
+
88
+ it 'should generate nested route with string' do
89
+ page_path('one/two').should eq '/other_pages/one/two'
90
+ end
91
+
92
+ it 'should recognize nested route' do
93
+ assert_recognizes(
94
+ {
95
+ :controller => 'high_voltage/pages',
96
+ :action => 'show',
97
+ :id => 'one/two'
98
+ },
99
+ '/other_pages/one/two'
100
+ )
101
+ end
102
+
103
+ it 'should recognize normal route' do
104
+ assert_recognizes(
105
+ {
106
+ :controller => 'high_voltage/pages',
107
+ :action => 'show',
108
+ :id => 'one'
109
+ },
110
+ '/other_pages/one'
111
+ )
112
+ end
113
+
114
+ it 'should recognize normal route with dots' do
115
+ assert_recognizes(
116
+ {
117
+ :controller => 'high_voltage/pages',
118
+ :action => 'show',
119
+ :id => 'one.two.three'
120
+ },
121
+ '/other_pages/one.two.three'
122
+ )
123
+ end
124
+ end
125
+
126
+ context 'with default configuration disabled' do
127
+ around do |example|
128
+ cached_high_voltage_routes = HighVoltage.routes
129
+ HighVoltage.routes = false
130
+ Rails.application.reload_routes!
131
+
132
+ example.run
133
+
134
+ HighVoltage.routes = cached_high_voltage_routes
135
+ Rails.application.reload_routes!
136
+ end
137
+
138
+ it 'should not recognize routes' do
139
+ { :get => '/pages/one/two' }.should_not be_routable
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,19 @@
1
+ ENV['RAILS_ENV'] = 'test'
2
+
3
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
4
+ require 'pry'
5
+ require 'rails/test_help'
6
+ require 'rspec/rails'
7
+ require 'capybara/rails'
8
+
9
+ Rails.backtrace_cleaner.remove_silencers!
10
+ Capybara.default_driver = :rack_test
11
+ Capybara.default_selector = :css
12
+
13
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each {|f| require f}
14
+
15
+ RSpec.configure do |config|
16
+ require 'rspec/expectations'
17
+ config.include RSpec::Matchers
18
+ config.mock_with :rspec
19
+ end
@@ -0,0 +1,12 @@
1
+ RSpec.configure do |config|
2
+ config.around(:each, enable_caching: true) do |example|
3
+ ActionController::Base.perform_caching = true
4
+ old_cache_store = ActionController::Base.cache_store
5
+ ActionController::Base.cache_store = :memory_store
6
+
7
+ example.run
8
+
9
+ ActionController::Base.cache_store = old_cache_store
10
+ ActionController::Base.perform_caching = false
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ def concern_reload
2
+ HighVoltage::PagesController.class_eval do
3
+ if respond_to?(:caches_action)
4
+ caches_action :show, if: -> { HighVoltage.action_caching }
5
+ end
6
+
7
+ if respond_to?(:caches_page)
8
+ caches_page :show, if: -> { HighVoltage.page_caching }
9
+ end
10
+ end
11
+ end