radiant 1.1.0.beta → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of radiant might be problematic. Click here for more details.

Files changed (77) hide show
  1. data/CHANGELOG.md +8 -0
  2. data/CONTRIBUTORS.md +6 -0
  3. data/Gemfile +6 -5
  4. data/Gemfile.lock +2 -2
  5. data/GemfileRails3 +37 -0
  6. data/app/controllers/site_controller.rb +31 -7
  7. data/app/helpers/application_helper.rb +4 -4
  8. data/app/models/deprecated_tags.rb +8 -1
  9. data/app/models/page.rb +21 -9
  10. data/app/models/radiant/page_response_cache_director.rb +43 -0
  11. data/app/models/standard_tags.rb +34 -32
  12. data/app/views/admin/pages/_fields.html.haml +1 -1
  13. data/app/views/admin/pages/_node.html.haml +1 -1
  14. data/config/compass.config +0 -3
  15. data/config/initializers/tmp.rb +2 -0
  16. data/config/preinitializer.rb +15 -16
  17. data/db/migrate/20110902203823_add_allowed_children_cache_to_pages.rb +1 -1
  18. data/lib/generators/instance/templates/instance_gemfile +9 -5
  19. data/lib/radiant.rb +1 -1
  20. data/lib/radiant/engine.rb +10 -0
  21. data/lib/radiant/extension.rb +1 -10
  22. data/lib/radiant/initializer.rb +1 -0
  23. data/lib/radiant/task_support.rb +66 -0
  24. data/lib/tasks/framework.rake +1 -1
  25. data/lib/tasks/radiant_config.rake +3 -3
  26. data/lib/tasks/release.rake +1 -1
  27. data/public/javascripts/admin/page_preview.js +1 -1
  28. data/public/javascripts/admin/pagefield.js +1 -1
  29. data/public/javascripts/admin/shortcuts.js +7 -1
  30. data/radiant.gemspec +2 -1
  31. data/rails/init.rb +1 -0
  32. data/spec/dummy/Rakefile +7 -0
  33. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  34. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  35. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  36. data/spec/dummy/config.ru +4 -0
  37. data/spec/dummy/config/application.rb +45 -0
  38. data/spec/dummy/config/boot.rb +10 -0
  39. data/spec/dummy/config/database.yml +22 -0
  40. data/spec/dummy/config/environment.rb +5 -0
  41. data/spec/dummy/config/environments/development.rb +26 -0
  42. data/spec/dummy/config/environments/production.rb +49 -0
  43. data/spec/dummy/config/environments/test.rb +35 -0
  44. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  45. data/spec/dummy/config/initializers/inflections.rb +10 -0
  46. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  47. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  48. data/spec/dummy/config/initializers/session_store.rb +8 -0
  49. data/spec/dummy/config/locales/en.yml +5 -0
  50. data/spec/dummy/config/routes.rb +3 -0
  51. data/spec/dummy/log/development.log +0 -0
  52. data/spec/dummy/log/production.log +0 -0
  53. data/spec/dummy/log/server.log +0 -0
  54. data/spec/dummy/log/test.log +0 -0
  55. data/spec/dummy/public/404.html +26 -0
  56. data/spec/dummy/public/422.html +26 -0
  57. data/spec/dummy/public/500.html +26 -0
  58. data/spec/dummy/public/favicon.ico +0 -0
  59. data/spec/dummy/public/javascripts/application.js +2 -0
  60. data/spec/dummy/public/javascripts/controls.js +965 -0
  61. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  62. data/spec/dummy/public/javascripts/effects.js +1123 -0
  63. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  64. data/spec/dummy/public/javascripts/rails.js +202 -0
  65. data/spec/dummy/script/rails +6 -0
  66. data/spec/fixtures/example_extension/example_extension.rb +0 -4
  67. data/spec/lib/radiant/extension_spec.rb +0 -8
  68. data/spec/lib/task_support_spec.rb +13 -13
  69. data/spec/models/deprecated_tags_spec.rb +8 -1
  70. data/spec/models/radiant/page_response_cache_director_spec.rb +83 -0
  71. data/spec/models/standard_tags_spec.rb +10 -7
  72. data/spec/spec_helper_rails_3.rb +15 -0
  73. metadata +105 -53
  74. data/config/initializers/compass.rb +0 -6
  75. data/lib/plugins/extension_patches/init.rb +0 -1
  76. data/lib/plugins/extension_patches/lib/routing_extension.rb +0 -31
  77. data/lib/task_support.rb +0 -64
@@ -0,0 +1,202 @@
1
+ (function() {
2
+ Ajax.Responders.register({
3
+ onCreate: function(request) {
4
+ var token = $$('meta[name=csrf-token]')[0];
5
+ if (token) {
6
+ if (!request.options.requestHeaders) request.options.requestHeaders = {};
7
+ request.options.requestHeaders['X-CSRF-Token'] = token.readAttribute('content');
8
+ }
9
+ }
10
+ });
11
+
12
+ // Technique from Juriy Zaytsev
13
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
14
+ function isEventSupported(eventName) {
15
+ var el = document.createElement('div');
16
+ eventName = 'on' + eventName;
17
+ var isSupported = (eventName in el);
18
+ if (!isSupported) {
19
+ el.setAttribute(eventName, 'return;');
20
+ isSupported = typeof el[eventName] == 'function';
21
+ }
22
+ el = null;
23
+ return isSupported;
24
+ }
25
+
26
+ function isForm(element) {
27
+ return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM';
28
+ }
29
+
30
+ function isInput(element) {
31
+ if (Object.isElement(element)) {
32
+ var name = element.nodeName.toUpperCase();
33
+ return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA';
34
+ }
35
+ else return false;
36
+ }
37
+
38
+ var submitBubbles = isEventSupported('submit'),
39
+ changeBubbles = isEventSupported('change');
40
+
41
+ if (!submitBubbles || !changeBubbles) {
42
+ // augment the Event.Handler class to observe custom events when needed
43
+ Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
44
+ function(init, element, eventName, selector, callback) {
45
+ init(element, eventName, selector, callback);
46
+ // is the handler being attached to an element that doesn't support this event?
47
+ if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
48
+ (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
49
+ // "submit" => "emulated:submit"
50
+ this.eventName = 'emulated:' + this.eventName;
51
+ }
52
+ }
53
+ );
54
+ }
55
+
56
+ if (!submitBubbles) {
57
+ // discover forms on the page by observing focus events which always bubble
58
+ document.on('focusin', 'form', function(focusEvent, form) {
59
+ // special handler for the real "submit" event (one-time operation)
60
+ if (!form.retrieve('emulated:submit')) {
61
+ form.on('submit', function(submitEvent) {
62
+ var emulated = form.fire('emulated:submit', submitEvent, true);
63
+ // if custom event received preventDefault, cancel the real one too
64
+ if (emulated.returnValue === false) submitEvent.preventDefault();
65
+ });
66
+ form.store('emulated:submit', true);
67
+ }
68
+ });
69
+ }
70
+
71
+ if (!changeBubbles) {
72
+ // discover form inputs on the page
73
+ document.on('focusin', 'input, select, textarea', function(focusEvent, input) {
74
+ // special handler for real "change" events
75
+ if (!input.retrieve('emulated:change')) {
76
+ input.on('change', function(changeEvent) {
77
+ input.fire('emulated:change', changeEvent, true);
78
+ });
79
+ input.store('emulated:change', true);
80
+ }
81
+ });
82
+ }
83
+
84
+ function handleRemote(element) {
85
+ var method, url, params;
86
+
87
+ var event = element.fire("ajax:before");
88
+ if (event.stopped) return false;
89
+
90
+ if (element.tagName.toLowerCase() === 'form') {
91
+ method = element.readAttribute('method') || 'post';
92
+ url = element.readAttribute('action');
93
+ // serialize the form with respect to the submit button that was pressed
94
+ params = element.serialize({ submit: element.retrieve('rails:submit-button') });
95
+ // clear the pressed submit button information
96
+ element.store('rails:submit-button', null);
97
+ } else {
98
+ method = element.readAttribute('data-method') || 'get';
99
+ url = element.readAttribute('href');
100
+ params = {};
101
+ }
102
+
103
+ new Ajax.Request(url, {
104
+ method: method,
105
+ parameters: params,
106
+ evalScripts: true,
107
+
108
+ onCreate: function(response) { element.fire("ajax:create", response); },
109
+ onComplete: function(response) { element.fire("ajax:complete", response); },
110
+ onSuccess: function(response) { element.fire("ajax:success", response); },
111
+ onFailure: function(response) { element.fire("ajax:failure", response); }
112
+ });
113
+
114
+ element.fire("ajax:after");
115
+ }
116
+
117
+ function insertHiddenField(form, name, value) {
118
+ form.insert(new Element('input', { type: 'hidden', name: name, value: value }));
119
+ }
120
+
121
+ function handleMethod(element) {
122
+ var method = element.readAttribute('data-method'),
123
+ url = element.readAttribute('href'),
124
+ csrf_param = $$('meta[name=csrf-param]')[0],
125
+ csrf_token = $$('meta[name=csrf-token]')[0];
126
+
127
+ var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
128
+ $(element.parentNode).insert(form);
129
+
130
+ if (method !== 'post') {
131
+ insertHiddenField(form, '_method', method);
132
+ }
133
+
134
+ if (csrf_param) {
135
+ insertHiddenField(form, csrf_param.readAttribute('content'), csrf_token.readAttribute('content'));
136
+ }
137
+
138
+ form.submit();
139
+ }
140
+
141
+ function disableFormElements(form) {
142
+ form.select('input[type=submit][data-disable-with]').each(function(input) {
143
+ input.store('rails:original-value', input.getValue());
144
+ input.setValue(input.readAttribute('data-disable-with')).disable();
145
+ });
146
+ }
147
+
148
+ function enableFormElements(form) {
149
+ form.select('input[type=submit][data-disable-with]').each(function(input) {
150
+ input.setValue(input.retrieve('rails:original-value')).enable();
151
+ });
152
+ }
153
+
154
+ function allowAction(element) {
155
+ var message = element.readAttribute('data-confirm');
156
+ return !message || confirm(message);
157
+ }
158
+
159
+ document.on('click', 'a[data-confirm], a[data-remote], a[data-method]', function(event, link) {
160
+ if (!allowAction(link)) {
161
+ event.stop();
162
+ return false;
163
+ }
164
+
165
+ if (link.readAttribute('data-remote')) {
166
+ handleRemote(link);
167
+ event.stop();
168
+ } else if (link.readAttribute('data-method')) {
169
+ handleMethod(link);
170
+ event.stop();
171
+ }
172
+ });
173
+
174
+ document.on("click", "form input[type=submit], form button[type=submit], form button:not([type])", function(event, button) {
175
+ // register the pressed submit button
176
+ event.findElement('form').store('rails:submit-button', button.name || false);
177
+ });
178
+
179
+ document.on("submit", function(event) {
180
+ var form = event.findElement();
181
+
182
+ if (!allowAction(form)) {
183
+ event.stop();
184
+ return false;
185
+ }
186
+
187
+ if (form.readAttribute('data-remote')) {
188
+ handleRemote(form);
189
+ event.stop();
190
+ } else {
191
+ disableFormElements(form);
192
+ }
193
+ });
194
+
195
+ document.on('ajax:create', 'form', function(event, form) {
196
+ if (form == event.findElement()) disableFormElements(form);
197
+ });
198
+
199
+ document.on('ajax:complete', 'form', function(event, form) {
200
+ if (form == event.findElement()) enableFormElements(form);
201
+ });
202
+ })();
@@ -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'
@@ -6,10 +6,6 @@ class ExampleExtension < Radiant::Extension
6
6
  description "Describe your extension here"
7
7
  url "http://example.com/example"
8
8
 
9
- # define_routes do |map|
10
- # map.connect 'admin/example/:action', :controller => 'admin/example'
11
- # end
12
-
13
9
  def activate
14
10
  # admin.tabs.add "Example", "/admin/example", :after => "Layouts", :visibility => [:all]
15
11
  end
@@ -46,14 +46,6 @@ describe Radiant::Extension do
46
46
  SuperExtension.extension_name.should == "Super"
47
47
  end
48
48
 
49
- it "should store route definitions defined in a block" do
50
- Radiant::Extension.should respond_to(:define_routes)
51
- my_block = proc {|map| map.stuff "stuff", :controller => "admin/pages" }
52
- Radiant::Extension.define_routes(&my_block)
53
- Radiant::Extension.route_definitions.should be_instance_of(Array)
54
- Radiant::Extension.route_definitions.first.should == my_block
55
- end
56
-
57
49
  it "should expose configuration object" do
58
50
  SuperExtension.extension_config do |config|
59
51
  config.should eql(Rails.configuration)
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + "/../spec_helper"
2
2
 
3
- describe TaskSupport do
3
+ describe Radiant::TaskSupport do
4
4
  describe "self.config_export" do
5
5
  before do
6
6
  @yaml_file = "#{Rails.root}/tmp/config/radiant_config.yml"
@@ -9,11 +9,11 @@ describe TaskSupport do
9
9
  File.exist?(@yaml_file).should be_false
10
10
  end
11
11
  it "should create a YAML file in config/radiant_config.yml" do
12
- TaskSupport.config_export(@yaml_file)
12
+ described_class.config_export(@yaml_file)
13
13
  File.exist?(@yaml_file).should be_true
14
14
  end
15
15
  it "should create YAML equal to Radiant::Config.to_hash" do
16
- TaskSupport.config_export(@yaml_file)
16
+ described_class.config_export(@yaml_file)
17
17
  YAML.load_file(@yaml_file).should == Radiant::Config.to_hash.to_yaml
18
18
  end
19
19
  end
@@ -24,7 +24,7 @@ describe TaskSupport do
24
24
  end
25
25
  it "should delete all Radiant::Config when the clear parameter is set to true" do
26
26
  Radiant::Config['testing_clear'] = 'true'
27
- TaskSupport.config_import(@yaml_file, true)
27
+ described_class.config_import(@yaml_file, true)
28
28
  Radiant::Config['testing_clear'].should be_nil
29
29
  end
30
30
  it "should load from the given YAML path" do
@@ -32,17 +32,17 @@ describe TaskSupport do
32
32
  @hash = {}
33
33
  YAML.stub!(:load_file).and_return(@yaml)
34
34
  YAML.should_receive(:load).with(@yaml).and_return(@hash)
35
- TaskSupport.config_import(@yaml_file)
35
+ described_class.config_import(@yaml_file)
36
36
  end
37
37
  it "should update Radiant::Config with the settings from the given YAML" do
38
38
  Radiant::Config.delete_all
39
- TaskSupport.config_import(@yaml_file)
39
+ described_class.config_import(@yaml_file)
40
40
  Radiant::Config.to_hash.should == YAML.load(YAML.load_file(@yaml_file))
41
41
  end
42
42
  it "should roll back if an invalid config setting is imported" do
43
43
  Radiant.config_definitions['defaults.page.status'].stub!(:select_from).and_return(['Draft'])
44
44
  Radiant::Config['defaults.page.status'] = "Draft"
45
- lambda{TaskSupport.config_import(@bad_yaml_file)}.should_not raise_error
45
+ lambda{described_class.config_import(@bad_yaml_file)}.should_not raise_error
46
46
  Radiant::Config['defaults.page.status'].should == "Draft"
47
47
  end
48
48
  end
@@ -63,7 +63,7 @@ describe TaskSupport do
63
63
  end
64
64
 
65
65
  it "should create a cache file containing the contents of the specified files" do
66
- TaskSupport.cache_files(@dir, @files, @cache_file)
66
+ described_class.cache_files(@dir, @files, @cache_file)
67
67
  cache_path = File.join(@dir, @cache_file)
68
68
  File.should exist(cache_path)
69
69
  File.read(cache_path).should == "Contents of 'a.txt'\n\nContents of 'b.txt'"
@@ -72,7 +72,7 @@ describe TaskSupport do
72
72
 
73
73
  describe "self.find_admin_js" do
74
74
  it "should return an array of JS files" do
75
- js_files = TaskSupport.find_admin_js
75
+ js_files = described_class.find_admin_js
76
76
  js_files.should_not be_empty
77
77
  js_files.each { |f| f.should =~ /^[^\/]+.js$/ }
78
78
  end
@@ -81,14 +81,14 @@ describe TaskSupport do
81
81
  describe "self.cache_admin_js" do
82
82
  before do
83
83
  @js_files = [ 'a.js','b.js' ]
84
- TaskSupport.stub!(:find_admin_js).and_return(@js_files)
85
- TaskSupport.stub!(:cache_files)
84
+ described_class.stub!(:find_admin_js).and_return(@js_files)
85
+ described_class.stub!(:cache_files)
86
86
  end
87
87
 
88
88
  it "should cache all admin JS files as 'all.js'" do
89
- TaskSupport.should_receive(:cache_files).with(
89
+ described_class.should_receive(:cache_files).with(
90
90
  "#{Rails.root}/public/javascripts/admin", @js_files, 'all.js')
91
- TaskSupport.cache_admin_js
91
+ described_class.cache_admin_js
92
92
  end
93
93
  end
94
94
  end
@@ -105,6 +105,13 @@ describe DeprecatedTags do
105
105
  end
106
106
  end
107
107
  end
108
+
109
+ describe "<r:rfc1123_date>" do
110
+ it 'should render an RFC1123-compatible date' do
111
+ @page.published_at = DateTime.new 2006, 1, 11
112
+ @page.should render('<r:rfc1123_date />').as('Wed, 11 Jan 2006 00:00:00 GMT')
113
+ end
114
+ end
108
115
 
109
116
  describe "<r:navigation>" do
110
117
  it "should render with deprecated url attribute" do
@@ -121,4 +128,4 @@ describe DeprecatedTags do
121
128
  end
122
129
  end
123
130
  end
124
- end
131
+ end
@@ -0,0 +1,83 @@
1
+ require 'ostruct'
2
+
3
+ require File.dirname(__FILE__) + "/../../spec_helper"
4
+
5
+ describe Radiant::PageResponseCacheDirector do
6
+ it 'initializes with a page and a listener' do
7
+ lambda { described_class.new(Object.new, Object.new) }.should_not raise_error
8
+ end
9
+
10
+ it 'errors without a page and a listener' do
11
+ lambda { described_class.new }.should raise_error
12
+ end
13
+
14
+ it 'has a default cache timeout' do
15
+ described_class.cache_timeout.should == 5.minutes
16
+ end
17
+
18
+ it 'sets the cache timeout' do
19
+ old_timeout = described_class.cache_timeout
20
+ described_class.cache_timeout = 30.days
21
+ described_class.cache_timeout.should == 30.days
22
+ described_class.cache_timeout.should_not == old_timeout
23
+ end
24
+
25
+ let(:non_cacheable_params){ {:private => true, "no-cache" => true} }
26
+ let(:cacheable_params){ {:public => true, :private => false} }
27
+ let(:listener){ l = OpenStruct.new()
28
+ l.stub!(:set_etag)
29
+ l.stub!(:set_expiry)
30
+ l
31
+ }
32
+ let(:page){ OpenStruct.new }
33
+
34
+ it 'sets the non-cacheable response' do
35
+ director = described_class.new(page, listener)
36
+ listener.should_receive(:set_expiry).with(nil, non_cacheable_params)
37
+ director.set_cache_control
38
+ end
39
+
40
+ it 'clears the etag' do
41
+ director = described_class.new(page, listener)
42
+ listener.should_receive(:set_etag).with('')
43
+ director.set_cache_control
44
+ end
45
+
46
+ it 'sets the cacheable response to the default timeout' do
47
+ listener.stub!(:cacheable_request?).and_return(true)
48
+ page.stub!(:cache?).and_return(true)
49
+
50
+ director = described_class.new(page, listener)
51
+
52
+ listener.should_receive(:set_expiry).with(described_class.cache_timeout, cacheable_params)
53
+ director.set_cache_control
54
+ end
55
+
56
+ it 'sets the cacheable response to the page timeout' do
57
+ listener.stub!(:cacheable_request?).and_return(true)
58
+ page.stub!(:cache?).and_return(true)
59
+ page.stub!(:cache_timeout).and_return(14.days)
60
+
61
+ director = described_class.new(page, listener)
62
+
63
+ listener.should_receive(:set_expiry).with(14.days, cacheable_params)
64
+ director.set_cache_control
65
+ end
66
+
67
+ it 'is not cacheable if the listener request is not cacheable' do
68
+ listener.stub!(:cacheable_request?).and_return(false)
69
+ director = described_class.new(page, listener)
70
+
71
+ listener.should_receive(:set_expiry).with(nil, non_cacheable_params)
72
+ director.set_cache_control
73
+ end
74
+
75
+ it 'is not cacheable if the page is not cacheable' do
76
+ listener.stub!(:cacheable_request?).and_return(true)
77
+ page.stub!(:cache?).and_return(false)
78
+ director = described_class.new(page, listener)
79
+
80
+ listener.should_receive(:set_expiry).with(nil, non_cacheable_params)
81
+ director.set_cache_control
82
+ end
83
+ end
@@ -580,11 +580,11 @@ describe "Standard Tags" do
580
580
 
581
581
  describe "<r:gravatar>" do
582
582
  it "should render the Gravatar URL of author of the current page" do
583
- page.should render('<r:gravatar />').as('http://www.gravatar.com/avatar.php?gravatar_id=e64c7d89f26bd1972efa854d13d7dd61&rating=G&size=32&default=http://testhost.tld/images/admin/avatar_32x32.png')
583
+ page.should render('<r:gravatar />').as('//gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?rating=G&size=32&default=http://testhost.tld/images/admin/avatar_32x32.png')
584
584
  end
585
585
 
586
586
  it "should render the Gravatar URL of the name user" do
587
- page.should render('<r:gravatar name="Admin" />').as('http://www.gravatar.com/avatar.php?gravatar_id=e64c7d89f26bd1972efa854d13d7dd61&rating=G&size=32&default=http://testhost.tld/images/admin/avatar_32x32.png')
587
+ page.should render('<r:gravatar name="Admin" />').as('//gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?rating=G&size=32&default=http://testhost.tld/images/admin/avatar_32x32.png')
588
588
  end
589
589
 
590
590
  it "should render the default avatar when the user has not set an email address" do
@@ -596,7 +596,7 @@ describe "Standard Tags" do
596
596
  end
597
597
 
598
598
  it "should render the specified rating" do
599
- page.should render('<r:gravatar rating="X" />').as('http://www.gravatar.com/avatar.php?gravatar_id=e64c7d89f26bd1972efa854d13d7dd61&rating=X&size=32&default=http://testhost.tld/images/admin/avatar_32x32.png')
599
+ page.should render('<r:gravatar rating="X" />').as('//gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?rating=X&size=32&default=http://testhost.tld/images/admin/avatar_32x32.png')
600
600
  end
601
601
  end
602
602
 
@@ -822,10 +822,6 @@ describe "Standard Tags" do
822
822
  page.should render('<r:escape_html><strong>a bold move</strong></r:escape_html>').as('&lt;strong&gt;a bold move&lt;/strong&gt;')
823
823
  end
824
824
 
825
- it '<r:rfc1123_date> should render an RFC1123-compatible date' do
826
- page(:dated).should render('<r:rfc1123_date />').as('Wed, 11 Jan 2006 00:00:00 GMT')
827
- end
828
-
829
825
  describe "<r:breadcrumbs>" do
830
826
  it "should render a series of breadcrumb links separated by &gt;" do
831
827
  expected = %{<a href="/">Home</a> &gt; <a href="/parent/">Parent</a> &gt; <a href="/parent/child/">Child</a> &gt; <a href="/parent/child/grandchild/">Grandchild</a> &gt; Great Grandchild}
@@ -1098,6 +1094,13 @@ describe "Standard Tags" do
1098
1094
  end
1099
1095
 
1100
1096
  end
1097
+
1098
+ describe "Site tags" do
1099
+ subject { page(:home) }
1100
+ it { should render('<r:site:title />').as('Your site title')}
1101
+ it { should render('<r:site:host />').as('www.example.com')}
1102
+ it { should render('<r:site:dev_host />').as('')}
1103
+ end
1101
1104
 
1102
1105
  private
1103
1106