active_theme 0.5.0.pre

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 (99) hide show
  1. data/.gitignore +8 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +2 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +91 -0
  7. data/LICENSE +21 -0
  8. data/README.textile +3 -0
  9. data/Rakefile +11 -0
  10. data/active_theme.gemspec +25 -0
  11. data/doc/README.textile +332 -0
  12. data/doc/REVIEW_NOTES +0 -0
  13. data/doc/TODO.textile +3 -0
  14. data/lib/active_theme.rb +58 -0
  15. data/lib/active_theme/action_controller.rb +32 -0
  16. data/lib/active_theme/action_mailer.rb +22 -0
  17. data/lib/active_theme/action_view.rb +59 -0
  18. data/lib/active_theme/assets_controller.rb +66 -0
  19. data/lib/active_theme/common_methods.rb +66 -0
  20. data/lib/active_theme/config.rb +74 -0
  21. data/lib/active_theme/interpolation.rb +11 -0
  22. data/lib/active_theme/railtie.rb +32 -0
  23. data/lib/active_theme/routes.rb +27 -0
  24. data/lib/active_theme/url_helpers.rb +27 -0
  25. data/lib/active_theme/version.rb +4 -0
  26. data/lib/generators/active_theme/create_generator.rb +14 -0
  27. data/lib/generators/active_theme/install_generator.rb +16 -0
  28. data/lib/generators/active_theme/templates/theme/images/.gitkeep +0 -0
  29. data/lib/generators/active_theme/templates/theme/javascripts/.gitkeep +0 -0
  30. data/lib/generators/active_theme/templates/theme/stylesheets/.gitkeep +0 -0
  31. data/lib/generators/active_theme/templates/theme/views/layouts/.gitkeep +0 -0
  32. data/lib/tasks/active_theme.rake +22 -0
  33. data/test/dummy_app/.gitignore +4 -0
  34. data/test/dummy_app/Gemfile +30 -0
  35. data/test/dummy_app/Rakefile +7 -0
  36. data/test/dummy_app/another_themes/another_default/images/logo.png +0 -0
  37. data/test/dummy_app/another_themes/another_default/images/nested/logo.png +0 -0
  38. data/test/dummy_app/another_themes/another_default/javascripts/app.js +1 -0
  39. data/test/dummy_app/another_themes/another_default/stylesheets/style.css +0 -0
  40. data/test/dummy_app/another_themes/another_default/stylesheets/style2.css +3 -0
  41. data/test/dummy_app/another_themes/another_default/views/layouts/default.html.erb +10 -0
  42. data/test/dummy_app/another_themes/another_default/views/products/index.html.erb +0 -0
  43. data/test/dummy_app/app/controllers/application_controller.rb +3 -0
  44. data/test/dummy_app/app/helpers/application_helper.rb +2 -0
  45. data/test/dummy_app/app/views/layouts/application.html.erb +14 -0
  46. data/test/dummy_app/config.ru +4 -0
  47. data/test/dummy_app/config/application.rb +42 -0
  48. data/test/dummy_app/config/boot.rb +13 -0
  49. data/test/dummy_app/config/database.yml +18 -0
  50. data/test/dummy_app/config/environment.rb +5 -0
  51. data/test/dummy_app/config/environments/development.rb +26 -0
  52. data/test/dummy_app/config/environments/production.rb +49 -0
  53. data/test/dummy_app/config/environments/test.rb +35 -0
  54. data/test/dummy_app/config/initializers/backtrace_silencers.rb +7 -0
  55. data/test/dummy_app/config/initializers/inflections.rb +10 -0
  56. data/test/dummy_app/config/initializers/mime_types.rb +5 -0
  57. data/test/dummy_app/config/initializers/secret_token.rb +7 -0
  58. data/test/dummy_app/config/initializers/session_store.rb +8 -0
  59. data/test/dummy_app/config/locales/en.yml +5 -0
  60. data/test/dummy_app/config/routes.rb +4 -0
  61. data/test/dummy_app/db/seeds.rb +7 -0
  62. data/test/dummy_app/empty_themes/.gitkeep +0 -0
  63. data/test/dummy_app/lib/tasks/.gitkeep +0 -0
  64. data/test/dummy_app/public/404.html +26 -0
  65. data/test/dummy_app/public/422.html +26 -0
  66. data/test/dummy_app/public/500.html +26 -0
  67. data/test/dummy_app/public/favicon.ico +0 -0
  68. data/test/dummy_app/public/images/rails.png +0 -0
  69. data/test/dummy_app/public/index.html +239 -0
  70. data/test/dummy_app/public/javascripts/application.js +2 -0
  71. data/test/dummy_app/public/javascripts/controls.js +965 -0
  72. data/test/dummy_app/public/javascripts/dragdrop.js +974 -0
  73. data/test/dummy_app/public/javascripts/effects.js +1123 -0
  74. data/test/dummy_app/public/javascripts/prototype.js +6001 -0
  75. data/test/dummy_app/public/javascripts/rails.js +175 -0
  76. data/test/dummy_app/public/robots.txt +5 -0
  77. data/test/dummy_app/public/stylesheets/.gitkeep +0 -0
  78. data/test/dummy_app/script/rails +6 -0
  79. data/test/dummy_app/themes/default/images/logo.png +0 -0
  80. data/test/dummy_app/themes/default/images/nested/logo.png +0 -0
  81. data/test/dummy_app/themes/default/javascripts/app.js +1 -0
  82. data/test/dummy_app/themes/default/javascripts/app.min.js +0 -0
  83. data/test/dummy_app/themes/default/stylesheets/images/logo.png +0 -0
  84. data/test/dummy_app/themes/default/stylesheets/style.css +0 -0
  85. data/test/dummy_app/themes/default/stylesheets/style2.css +3 -0
  86. data/test/dummy_app/themes/default/views/layouts/default.html.erb +10 -0
  87. data/test/dummy_app/themes/default/views/products/index.html.erb +0 -0
  88. data/test/lib/action_controller_test.rb +170 -0
  89. data/test/lib/action_mailer_test.rb +35 -0
  90. data/test/lib/action_view_test.rb +54 -0
  91. data/test/lib/assets_controller_test.rb +73 -0
  92. data/test/lib/common_methods_test.rb +28 -0
  93. data/test/lib/config_test.rb +26 -0
  94. data/test/lib/integration_test.rb +12 -0
  95. data/test/lib/routes_test.rb +40 -0
  96. data/test/lib/themes_for_rails_test.rb +18 -0
  97. data/test/support/extensions.rb +19 -0
  98. data/test/test_helper.rb +12 -0
  99. metadata +276 -0
@@ -0,0 +1,175 @@
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
+ })();
@@ -0,0 +1,5 @@
1
+ # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2
+ #
3
+ # To ban all spiders from the entire site uncomment the next two lines:
4
+ # User-Agent: *
5
+ # Disallow: /
@@ -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,3 @@
1
+ a {
2
+ color: green;
3
+ }
@@ -0,0 +1,10 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+ <html>
4
+ <head>
5
+ <title>App!</title>
6
+ </head>
7
+ <body>
8
+
9
+ </body>
10
+ </html>
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+ require File.expand_path("test/test_helper.rb")
3
+
4
+ class MyController < ActionController::Base
5
+
6
+ def hello
7
+ render :text => "Just a test"
8
+ end
9
+
10
+ end
11
+
12
+ class CustomThemeController < ActionController::Base
13
+
14
+ def hello
15
+ render :text => "Just a test"
16
+ end
17
+
18
+ def theme_selector
19
+ 'custom'
20
+ end
21
+
22
+ end
23
+
24
+ class PrivateCustomThemeController < ActionController::Base
25
+
26
+ def hello
27
+ render :text => "Just a test"
28
+ end
29
+
30
+ private
31
+
32
+ def private_theme_selector
33
+ 'private_custom'
34
+ end
35
+
36
+ end
37
+
38
+ class ActionMailerInclusionTest < Test::Unit::TestCase
39
+
40
+ should "include the ActionController module" do
41
+ assert ActionMailer::Base.included_modules.include?(ActiveTheme::ActionController)
42
+ end
43
+
44
+ end
45
+
46
+ class ActionControllerInclusionTest < Test::Unit::TestCase
47
+ should "include the ActionController module" do
48
+ assert ActionController::Base.included_modules.include?(ActiveTheme::ActionController)
49
+ end
50
+ end
51
+
52
+ class ApplicationControllerInclusionTest < Test::Unit::TestCase
53
+ should "include the ActionController module" do
54
+ assert ApplicationController.included_modules.include?(ActiveTheme::ActionController)
55
+ end
56
+ end
57
+
58
+ module ActiveTheme
59
+ class ActionControllerTest < ::ActionController::TestCase
60
+ context "at class level" do
61
+
62
+ should "respond_to theme" do
63
+ assert ApplicationController.respond_to?(:theme)
64
+ end
65
+
66
+ context "setting the theme with a String" do
67
+
68
+ tests MyController
69
+
70
+ should "set the selected theme for all actions" do
71
+ MyController.theme 'default'
72
+ @controller.expects(:set_theme).with('default')
73
+ assert_equal nil, @controller.theme_name
74
+ get :hello
75
+ end
76
+ end
77
+
78
+ context "setting the theme with a Symbol" do
79
+
80
+ tests CustomThemeController
81
+
82
+ should "call the selected private method" do
83
+ CustomThemeController.theme :theme_selector
84
+ get :hello
85
+ assert_equal 'custom', @controller.theme_name
86
+ end
87
+ end
88
+
89
+ context "setting the theme with a Symbol (private)" do
90
+
91
+ tests PrivateCustomThemeController
92
+
93
+ should "call the selected private method" do
94
+ PrivateCustomThemeController.theme :private_theme_selector
95
+ get :hello
96
+ assert_equal 'private_custom', @controller.theme_name
97
+ end
98
+ end
99
+ end
100
+
101
+ context "at instance level" do
102
+
103
+ tests ApplicationController
104
+
105
+ should "respond_to theme" do
106
+ assert @controller.respond_to?(:theme)
107
+ end
108
+
109
+ should "should store the theme's name" do
110
+ @controller.theme 'default'
111
+ assert_equal @controller.theme_name, 'default'
112
+ end
113
+
114
+ context "when a theme has been set" do
115
+
116
+ tests ApplicationController
117
+
118
+ should "add the theme's view path to the front of the general view paths" do
119
+ antes = @controller.view_paths.size
120
+ @controller.theme 'default'
121
+ assert_equal antes + 1, @controller.view_paths.size
122
+ end
123
+
124
+ should "have a proper view path" do
125
+ @controller.theme 'default'
126
+ view_path = @controller.view_paths.first
127
+ theme_view_path = File.expand_path(File.join("test", "dummy_app", "themes", "default", "views"))
128
+ assert_equal view_path.to_s, theme_view_path
129
+ end
130
+
131
+ end
132
+ end
133
+ context "using url helpers" do
134
+
135
+ tests ApplicationController
136
+
137
+ should "provide url method to access a given stylesheet file in the current theme" do
138
+ @controller.theme 'default'
139
+ assert_equal @controller.send(:current_theme_stylesheet_path, "style"), "/themes/default/stylesheets/style.css"
140
+ end
141
+
142
+ should "provide url method to access a given javascript file in the current theme" do
143
+ @controller.theme 'default'
144
+ assert_equal @controller.send(:current_theme_javascript_path, "app"), "/themes/default/javascripts/app.js"
145
+ end
146
+
147
+ should "provide url method to access a given image file in the current theme" do
148
+ @controller.theme 'default'
149
+ assert_equal @controller.send(:current_theme_image_path, "logo.png"), "/themes/default/images/logo.png"
150
+ end
151
+
152
+ context "with multiple dots" do
153
+
154
+ tests ApplicationController
155
+
156
+ should "provide url method to access a given stylesheet file in the current theme" do
157
+ @controller.theme 'default'
158
+ assert_equal @controller.send(:current_theme_stylesheet_path, "style.compact"), "/themes/default/stylesheets/style.compact.css"
159
+ end
160
+
161
+ should "provide url method to access a given javascript file in the current theme" do
162
+ @controller.theme 'default'
163
+ assert_equal @controller.send(:current_theme_javascript_path, "app.min"), "/themes/default/javascripts/app.min.js"
164
+ end
165
+
166
+ end
167
+
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ require File.expand_path("test/test_helper.rb")
3
+
4
+ THEME = 'pink'
5
+
6
+ class Notifier < ActionMailer::Base
7
+ default :theme => THEME
8
+
9
+ def welcome(user)
10
+ mail(:theme => user.theme)
11
+ end
12
+
13
+ def thanks(user)
14
+ mail
15
+ end
16
+ end
17
+
18
+ module ActiveTheme
19
+ class ActionMailerTest < ::ActionController::TestCase
20
+
21
+ should "set theme using mail headers" do
22
+ Notifier.any_instance.expects(:theme).with("purple")
23
+
24
+ user = mock("User", :theme => "purple")
25
+ Notifier.welcome(user)
26
+ end
27
+
28
+ should "set theme using mail default opts" do
29
+ Notifier.any_instance.expects(:theme).with("pink")
30
+
31
+ user = mock("User")
32
+ Notifier.thanks(user)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ require File.expand_path("test/test_helper.rb")
3
+
4
+ class ViewHelpersTest < ::ActionController::IntegrationTest
5
+
6
+ include ::ActiveTheme::ActionView
7
+ include ::ActionView::Helpers::AssetTagHelper
8
+ include ::ERB::Util
9
+ include ::ActionView::Helpers::TagHelper
10
+ include ::ActionView::Helpers::FormTagHelper
11
+ def theme_name
12
+ 'default'
13
+ end
14
+
15
+ def config
16
+ @config ||= stub({:perform_caching => false, :asset_path => "/assets", :asset_host => ''})
17
+ end
18
+ end
19
+
20
+ module ActiveTheme
21
+ class CommonViewHelpersTest < ViewHelpersTest
22
+
23
+ should "provide path helpers for a given theme name" do
24
+
25
+ assert_equal "/themes/sometheme/stylesheets/style.css", theme_stylesheet_path('style', "sometheme")
26
+ assert_equal "/themes/sometheme/javascripts/app.js", theme_javascript_path('app', "sometheme")
27
+ assert_equal "/themes/sometheme/images/logo.png", theme_image_path('logo.png', "sometheme")
28
+
29
+ end
30
+
31
+ should "provide path helpers for a given theme name with dots" do
32
+
33
+ assert_equal "/themes/some.theme/stylesheets/style.css", theme_stylesheet_path('style', "some.theme")
34
+ assert_equal "/themes/some.theme/javascripts/app.js", theme_javascript_path('app', "some.theme")
35
+ assert_equal "/themes/some.theme/images/logo.png", theme_image_path('logo.png', "some.theme")
36
+
37
+ end
38
+
39
+ should 'delegate to stylesheet_link_tag' do
40
+ assert_match /media=.screen/, theme_stylesheet_link_tag('cuac.css')
41
+ end
42
+
43
+ should 'delegate options (lazy testing, I know)' do
44
+ assert_match /media=.print/, theme_stylesheet_link_tag('cuac.css', :media => 'print')
45
+ end
46
+ should 'delegate options in image_tag' do
47
+ assert_match /width=.40/, theme_image_tag('image.css', :size => '40x50')
48
+ end
49
+
50
+ should 'delegate options in image_submit_tag' do
51
+ assert_match /class=.search_button/, theme_image_submit_tag('image.png', :class => 'search_button')
52
+ end
53
+ end
54
+ end