refinerycms-pages 2.0.10 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/refinery/admin/pages_controller.rb +23 -17
  3. data/app/controllers/refinery/admin/pages_dialogs_controller.rb +8 -45
  4. data/app/controllers/refinery/pages/admin/preview_controller.rb +38 -0
  5. data/app/controllers/refinery/pages_controller.rb +26 -43
  6. data/app/helpers/refinery/admin/pages_helper.rb +19 -14
  7. data/app/models/refinery/page.rb +133 -194
  8. data/app/models/refinery/page_part.rb +1 -1
  9. data/{lib → app/presenters}/refinery/pages/content_page_presenter.rb +1 -5
  10. data/{lib → app/presenters}/refinery/pages/content_presenter.rb +1 -15
  11. data/app/presenters/refinery/pages/menu_presenter.rb +118 -0
  12. data/{lib → app/presenters}/refinery/pages/page_part_section_presenter.rb +0 -0
  13. data/{lib → app/presenters}/refinery/pages/section_presenter.rb +0 -0
  14. data/{lib → app/presenters}/refinery/pages/title_section_presenter.rb +0 -0
  15. data/app/sweepers/refinery/pages/page_sweeper.rb +29 -0
  16. data/app/views/refinery/admin/pages/_form.html.erb +4 -4
  17. data/app/views/refinery/admin/pages/_form_advanced_options.html.erb +3 -18
  18. data/app/views/refinery/admin/pages/_form_page_parts.html.erb +2 -2
  19. data/app/views/refinery/admin/pages/_page.html.erb +5 -5
  20. data/app/views/refinery/admin/pages/_records.html.erb +1 -3
  21. data/app/views/refinery/admin/pages/_sortable_list.html.erb +1 -1
  22. data/app/views/refinery/admin/pages/index.html.erb +1 -1
  23. data/app/views/refinery/admin/pages_dialogs/link_to.html.erb +0 -2
  24. data/config/locales/bg.yml +0 -11
  25. data/config/locales/cs.yml +1 -3
  26. data/config/locales/da.yml +15 -5
  27. data/config/locales/de.yml +16 -5
  28. data/config/locales/el.yml +0 -3
  29. data/config/locales/en.yml +1 -12
  30. data/config/locales/es.yml +0 -3
  31. data/config/locales/fi.yml +0 -3
  32. data/config/locales/fr.yml +0 -11
  33. data/config/locales/hu.yml +85 -0
  34. data/config/locales/it.yml +0 -10
  35. data/config/locales/ja.yml +0 -3
  36. data/config/locales/ko.yml +0 -11
  37. data/config/locales/lt.yml +0 -3
  38. data/config/locales/lv.yml +0 -3
  39. data/config/locales/nb.yml +0 -3
  40. data/config/locales/nl.yml +51 -40
  41. data/config/locales/pl.yml +23 -4
  42. data/config/locales/pt-BR.yml +0 -3
  43. data/config/locales/pt.yml +85 -0
  44. data/config/locales/rs.yml +0 -3
  45. data/config/locales/ru.yml +0 -3
  46. data/config/locales/sk.yml +0 -11
  47. data/config/locales/sl.yml +0 -3
  48. data/config/locales/sv.yml +0 -3
  49. data/config/locales/tr.yml +85 -0
  50. data/config/locales/uk.yml +82 -0
  51. data/config/locales/vi.yml +0 -3
  52. data/config/locales/zh-CN.yml +8 -11
  53. data/config/locales/zh-TW.yml +0 -3
  54. data/config/routes.rb +11 -5
  55. data/db/seeds.rb +16 -14
  56. data/lib/generators/refinery/pages/templates/config/initializers/refinery/pages.rb.erb +8 -4
  57. data/lib/refinery/pages.rb +5 -9
  58. data/lib/refinery/pages/caching.rb +60 -0
  59. data/lib/refinery/pages/configuration.rb +11 -7
  60. data/lib/refinery/pages/engine.rb +7 -8
  61. data/lib/refinery/pages/instance_methods.rb +4 -11
  62. data/lib/refinery/pages/render_options.rb +27 -0
  63. data/lib/refinery/pages/tab.rb +15 -4
  64. data/lib/refinery/pages/url.rb +74 -0
  65. data/refinerycms-pages.gemspec +4 -3
  66. data/spec/controllers/refinery/pages_controller_spec.rb +24 -0
  67. data/spec/factories/pages.rb +1 -1
  68. data/spec/{requests → features}/refinery/admin/pages_spec.rb +125 -42
  69. data/spec/{requests → features}/refinery/pages_spec.rb +139 -23
  70. data/spec/helpers/refinery/pages/admin/pages_helper_spec.rb +25 -25
  71. data/spec/lib/refinery/pages/caching_spec.rb +90 -0
  72. data/spec/lib/refinery/pages/tab_spec.rb +89 -0
  73. data/spec/lib/refinery/pages/url_spec.rb +74 -0
  74. data/spec/models/refinery/page_spec.rb +196 -71
  75. data/spec/{lib → presenters/refinery}/pages/content_page_presenter_spec.rb +0 -0
  76. data/spec/{lib → presenters/refinery}/pages/content_presenter_spec.rb +0 -0
  77. data/spec/presenters/refinery/pages/menu_presenter_spec.rb +58 -0
  78. data/spec/{lib → presenters/refinery}/pages/page_part_section_presenter_spec.rb +0 -0
  79. data/spec/{lib → presenters/refinery}/pages/section_presenter_spec.rb +0 -0
  80. data/spec/{lib → presenters/refinery}/pages/title_section_presenter_spec.rb +0 -0
  81. data/spec/support/refinery/pages/caching.rb +19 -0
  82. data/spec/support/refinery/pages/caching_helpers.rb +22 -0
  83. metadata +77 -33
  84. data/app/controllers/refinery/page_sweeper.rb +0 -34
  85. data/app/helpers/refinery/pages_helper.rb +0 -20
  86. data/app/views/refinery/admin/pages/_locale_picker.html.erb +0 -11
  87. data/config/locales/pt-PT.yml +0 -75
  88. data/spec/controllers/refinery/admin/pages_controller_spec.rb +0 -17
  89. data/spec/helpers/refinery/pages/pages_helper_spec.rb +0 -30
@@ -7,11 +7,8 @@ module Refinery
7
7
  let(:about_page) { Page.create :title => 'About' }
8
8
  let(:draft_page) { Page.create :title => 'Draft', :draft => true }
9
9
  before do
10
- # So that we can use Refinery.
11
- Refinery::PagesController.any_instance.stub(:refinery_user_required?).and_return(false)
12
-
13
10
  # Stub the menu pages we're expecting
14
- Refinery::Page.stub(:fast_menu).and_return([home_page, about_page])
11
+ Page.stub(:fast_menu).and_return([home_page, about_page])
15
12
  end
16
13
 
17
14
  def standard_page_menu_items_exist?
@@ -24,17 +21,17 @@ module Refinery
24
21
 
25
22
  describe 'when marketable urls are' do
26
23
  describe 'enabled' do
27
- before { Refinery::Pages.stub(:marketable_urls).and_return(true) }
24
+ before { Pages.stub(:marketable_urls).and_return(true) }
28
25
 
29
26
  it 'shows the homepage' do
30
- Refinery::PagesController.any_instance.stub(:find_page).and_return(:home_page)
27
+ PagesController.any_instance.stub(:find_page).and_return(:home_page)
31
28
  visit '/'
32
29
 
33
30
  standard_page_menu_items_exist?
34
31
  end
35
32
 
36
33
  it 'shows a show page' do
37
- Refinery::PagesController.any_instance.stub(:find_page).and_return(:about_page)
34
+ PagesController.any_instance.stub(:find_page).and_return(:about_page)
38
35
  visit refinery.page_path(about_page)
39
36
 
40
37
  standard_page_menu_items_exist?
@@ -42,10 +39,10 @@ module Refinery
42
39
  end
43
40
 
44
41
  describe 'disabled' do
45
- before { Refinery::Pages.stub(:marketable_urls).and_return(false) }
42
+ before { Pages.stub(:marketable_urls).and_return(false) }
46
43
 
47
44
  it 'shows the homepage' do
48
- Refinery::PagesController.any_instance.stub(:find_page).and_return(:home_page)
45
+ PagesController.any_instance.stub(:find_page).and_return(:home_page)
49
46
  visit '/'
50
47
 
51
48
  standard_page_menu_items_exist?
@@ -56,7 +53,7 @@ module Refinery
56
53
  end
57
54
 
58
55
  it 'shows the about page' do
59
- Refinery::PagesController.any_instance.stub(:find_page).and_return(:about_page)
56
+ PagesController.any_instance.stub(:find_page).and_return(:about_page)
60
57
  visit refinery.page_path(about_page)
61
58
 
62
59
  standard_page_menu_items_exist?
@@ -71,12 +68,18 @@ module Refinery
71
68
  find("#body_content_title").text.should == about_page.title
72
69
  end
73
70
 
71
+ it "should hide title when config is set" do
72
+ Pages.stub(:show_title_in_body).and_return(false)
73
+ visit '/about'
74
+ page.should_not have_selector("#body_content_title")
75
+ end
76
+
74
77
  it "uses title in the menu" do
75
78
  find(".selected").text.strip.should == about_page.title
76
79
  end
77
80
 
78
81
  it "uses title in browser title" do
79
- find("title").should have_content(about_page.title)
82
+ page.has_title?(about_page.title)
80
83
  end
81
84
  end
82
85
 
@@ -84,7 +87,7 @@ module Refinery
84
87
  let(:page_mt) { Page.create :title => 'Company News' }
85
88
 
86
89
  before do
87
- Refinery::Page.stub(:fast_menu).and_return([page_mt])
90
+ Page.stub(:fast_menu).and_return([page_mt])
88
91
  end
89
92
 
90
93
  describe 'set' do
@@ -102,7 +105,7 @@ module Refinery
102
105
  it "does not effect browser title and page title" do
103
106
  visit "/news"
104
107
 
105
- find("title").should have_content(page_mt.title)
108
+ page.has_title?(page_mt.title)
106
109
  find("#body_content_title").text.should == page_mt.title
107
110
  end
108
111
  end
@@ -129,12 +132,12 @@ module Refinery
129
132
  Page.create :title => 'About Us', :browser_title => 'About Our Company'
130
133
  }
131
134
  before do
132
- Refinery::Page.stub(:fast_menu).and_return([page_bt])
135
+ Page.stub(:fast_menu).and_return([page_bt])
133
136
  end
134
137
  it 'should have the browser_title in the title tag' do
135
138
  visit '/about-us'
136
139
 
137
- page.find("title").text == page_bt.title
140
+ page.has_title?(page_bt.title)
138
141
  end
139
142
 
140
143
  it 'should not effect page title and menu title' do
@@ -148,7 +151,7 @@ module Refinery
148
151
  describe 'custom_slug' do
149
152
  let(:page_cs) { Page.create :title => 'About Us' }
150
153
  before do
151
- Refinery::Page.stub(:fast_menu).and_return([page_cs])
154
+ Page.stub(:fast_menu).and_return([page_cs])
152
155
  end
153
156
 
154
157
  describe 'not set' do
@@ -181,6 +184,23 @@ module Refinery
181
184
  page_cs.reload
182
185
  end
183
186
  end
187
+
188
+ describe 'set with slashes' do
189
+ before do
190
+ Pages.stub(:scope_slug_by_parent).and_return(false)
191
+ page_cs.custom_slug = "products/my product/cool one!"
192
+ page_cs.save
193
+ end
194
+
195
+ after do
196
+ Pages.stub(:scope_slug_by_parent).and_return(true)
197
+ end
198
+
199
+ it 'should make and use a new friendly_id' do
200
+ visit '/products/my-product/cool-one'
201
+ current_path.should == '/products/my-product/cool-one'
202
+ end
203
+ end
184
204
  end
185
205
 
186
206
  # Following specs are converted from one of the cucumber features.
@@ -211,7 +231,9 @@ module Refinery
211
231
  let(:submenu_page) { about_page.children.create :title => 'Sample Submenu' }
212
232
 
213
233
  before do
214
- Refinery::Page.stub(:fast_menu).and_return([home_page, submenu_page, about_page.reload].sort_by(&:lft))
234
+ Page.stub(:fast_menu).and_return(
235
+ [home_page, submenu_page, about_page.reload].sort_by(&:lft)
236
+ )
215
237
  end
216
238
 
217
239
  it "succeeds" do
@@ -227,7 +249,9 @@ module Refinery
227
249
  describe "special characters title" do
228
250
  let(:special_page) { Page.create :title => 'ä ö ü spéciål chåråctÉrs' }
229
251
  before do
230
- Refinery::Page.stub(:fast_menu).and_return([home_page, about_page, special_page])
252
+ Page.stub(:fast_menu).and_return(
253
+ [home_page, about_page, special_page]
254
+ )
231
255
  end
232
256
 
233
257
  it "succeeds" do
@@ -247,7 +271,7 @@ module Refinery
247
271
  }
248
272
 
249
273
  before do
250
- Refinery::Page.stub(:fast_menu).and_return(
274
+ Page.stub(:fast_menu).and_return(
251
275
  [home_page, special_page, about_page.reload].sort_by &:lft
252
276
  )
253
277
  end
@@ -267,7 +291,7 @@ module Refinery
267
291
  let(:hidden_page) { Page.create :title => "Hidden", :show_in_menu => false }
268
292
 
269
293
  before do
270
- Refinery::Page.stub(:fast_menu).and_return([home_page, about_page])
294
+ Page.stub(:fast_menu).and_return([home_page, about_page])
271
295
  end
272
296
 
273
297
  it "succeeds" do
@@ -283,14 +307,13 @@ module Refinery
283
307
  end
284
308
 
285
309
  describe "skip to first child" do
286
- let(:child_page) { about_page.children.create :title => "Child Page" }
310
+ let!(:child_page) { about_page.children.create :title => "Child Page" }
287
311
  before do
288
- child_page
289
312
  about = about_page.reload
290
313
  about.skip_to_first_child = true
291
314
  about.save!
292
315
 
293
- Refinery::Page.stub(:fast_menu).and_return([home_page, about, child_page].sort_by(&:lft))
316
+ Page.stub(:fast_menu).and_return([home_page, about, child_page].sort_by(&:lft))
294
317
  end
295
318
 
296
319
  it "succeeds" do
@@ -301,5 +324,98 @@ module Refinery
301
324
  end
302
325
  end
303
326
  end
327
+
328
+ describe "full page caching" do
329
+ include CachingHelpers
330
+ let(:cached_page) { Page.create :title => 'Another Cached page' }
331
+
332
+ context "is enabled", :caching do
333
+ it "should create a cached file when none exists" do
334
+ cached_page.should_not be_cached
335
+
336
+ visit refinery.page_path(cached_page)
337
+
338
+ cached_page.should be_cached
339
+ end
340
+ end
341
+
342
+ context "is disabled" do
343
+ it "should not cache a page" do
344
+ page.should_not be_cached
345
+ visit refinery.page_path(cached_page)
346
+ cached_page.should_not be_cached
347
+ end
348
+ end
349
+ end
350
+
351
+ context "with multiple locales" do
352
+
353
+ describe "redirects" do
354
+ before { Refinery::I18n.stub(:frontend_locales).and_return([:en, :ru]) }
355
+ after { Refinery::I18n.unstub(:frontend_locales) }
356
+ let(:en_page_title) { 'News' }
357
+ let(:en_page_slug) { 'news' }
358
+ let(:ru_page_title) { 'Новости' }
359
+ let(:ru_page_slug_encoded) { '%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8' }
360
+ let!(:news_page) do
361
+ _page = Globalize.with_locale(:en) {
362
+ Page.create :title => en_page_title
363
+ }
364
+ Globalize.with_locale(:ru) do
365
+ _page.title = ru_page_title
366
+ _page.save
367
+ end
368
+
369
+ _page
370
+ end
371
+
372
+ it "should recognise when default locale is in the path" do
373
+ visit "/en/#{en_page_slug}"
374
+
375
+ current_path.should == "/en/#{en_page_slug}"
376
+ end
377
+
378
+ it "should redirect to default locale slug" do
379
+ visit "/#{ru_page_slug_encoded}"
380
+
381
+ current_path.should == "/#{en_page_slug}"
382
+ end
383
+
384
+ it "should redirect to second locale slug" do
385
+ visit "/ru/#{en_page_slug}"
386
+
387
+ current_path.should == "/ru/#{ru_page_slug_encoded}"
388
+
389
+ visit "/en/#{en_page_slug}"
390
+ end
391
+
392
+ describe "nested page" do
393
+ let(:nested_page_title) { '2012' }
394
+ let(:nested_page_slug) { '2012' }
395
+
396
+ let!(:nested_page) do
397
+ _page = Globalize.with_locale(:en) {
398
+ news_page.children.create :title => nested_page_title
399
+ }
400
+
401
+ Globalize.with_locale(:ru) do
402
+ _page.title = nested_page_title
403
+ _page.save
404
+ end
405
+
406
+ _page
407
+ end
408
+
409
+ it "should redirect to localized url" do
410
+ visit "/ru/#{en_page_slug}/#{nested_page_slug}"
411
+
412
+ current_path.should == "/ru/#{ru_page_slug_encoded}/#{nested_page_slug}"
413
+
414
+ visit "/en/#{en_page_slug}/#{nested_page_slug}"
415
+ end
416
+ end
417
+ end
418
+ end
419
+
304
420
  end
305
421
  end
@@ -4,27 +4,35 @@ module Refinery
4
4
  module Admin
5
5
  describe PagesHelper do
6
6
  describe "#template_options" do
7
- context "when page layout/view templte is set" do
8
- it "returns empty hash" do
7
+ context "when page layout/view template is set" do
8
+ it "returns those templates as selected" do
9
9
  page = FactoryGirl.create(:page)
10
10
 
11
11
  page.view_template = "rspec_template"
12
- helper.template_options(:view_template, page).should eq({})
12
+ helper.template_options(:view_template, page).should eq(:selected => "rspec_template")
13
13
 
14
14
  page.layout_template = "rspec_layout"
15
- helper.template_options(:layout_template, page).should eq({})
15
+ helper.template_options(:layout_template, page).should eq(:selected => "rspec_layout")
16
16
  end
17
17
  end
18
18
 
19
- context "when page layout/view template isn't set" do
20
- context "when page has parent" do
21
- it "returns option hash based on parent page" do
22
- parent = FactoryGirl.create(:page, :view_template => "rspec_view",
23
- :layout_template => "rspec_layout")
24
- page = FactoryGirl.create(:page, :parent_id => parent.id)
19
+ context "when page layout template is set using symbols" do
20
+ before do
21
+ Pages.config.stub(:layout_template_whitelist).and_return [:three, :one, :two]
22
+ end
23
+
24
+ it "works as expected" do
25
+ page = FactoryGirl.create(:page, :layout_template => "three")
26
+
27
+ helper.template_options(:layout_template, page).should eq(:selected => 'three')
28
+ end
29
+ end
25
30
 
26
- expected_view = { :selected => parent.view_template }
27
- helper.template_options(:view_template, page).should eq(expected_view)
31
+ context "when page layout template isn't set" do
32
+ context "when page has parent and parent has layout_template set" do
33
+ it "returns parent layout_template as selected" do
34
+ parent = FactoryGirl.create(:page, :layout_template => "rspec_layout")
35
+ page = FactoryGirl.create(:page, :parent_id => parent.id)
28
36
 
29
37
  expected_layout = { :selected => parent.layout_template }
30
38
  helper.template_options(:layout_template, page).should eq(expected_layout)
@@ -32,18 +40,10 @@ module Refinery
32
40
  end
33
41
 
34
42
  context "when page doesn't have parent page" do
35
- before do
36
- Refinery::Pages.stub(:view_template_whitelist).and_return(%w(one two))
37
- Refinery::Pages.stub(:layout_template_whitelist).and_return(%w(two one))
38
- end
39
-
40
- it "returns option hash with first item from configured whitelist" do
43
+ it "returns default application template" do
41
44
  page = FactoryGirl.create(:page)
42
45
 
43
- expected_view = { :selected => "one" }
44
- helper.template_options(:view_template, page).should eq(expected_view)
45
-
46
- expected_layout = { :selected => "two" }
46
+ expected_layout = { :selected => "application" }
47
47
  helper.template_options(:layout_template, page).should eq(expected_layout)
48
48
  end
49
49
  end
@@ -57,7 +57,7 @@ module Refinery
57
57
  it "adds 'hidden' label" do
58
58
  page.show_in_menu = false
59
59
 
60
- helper.page_meta_information(page).should eq("<span class=\"label\">hidden</span>")
60
+ helper.page_meta_information(page).should eq(%q{<span class="label">hidden</span>})
61
61
  end
62
62
  end
63
63
 
@@ -65,7 +65,7 @@ module Refinery
65
65
  it "adds 'draft' label" do
66
66
  page.draft = true
67
67
 
68
- helper.page_meta_information(page).should eq("<span class=\"label notice\">draft</span>")
68
+ helper.page_meta_information(page).should eq(%q{<span class="label notice">draft</span>})
69
69
  end
70
70
  end
71
71
  end
@@ -93,7 +93,7 @@ module Refinery
93
93
 
94
94
  context "when title for current locale isn't available" do
95
95
  it "returns existing title from translations" do
96
- Refinery::Page::Translation.where(:locale => :en).first.delete
96
+ Page.translation_class.where(:locale => :en).first.destroy
97
97
  helper.page_title_with_translations(page).should eq("melnraksts")
98
98
  end
99
99
  end
@@ -0,0 +1,90 @@
1
+ require "spec_helper"
2
+ require 'refinery/pages/caching'
3
+
4
+ module Refinery
5
+ module Pages
6
+ describe Caching, :caching do
7
+ let(:cache_path) { "/tmp"}
8
+ let(:refinery_cache_path) { "/tmp/refinery/cache/pages"}
9
+ let(:refinery_cache_file) { refinery_cache_path + ".html"}
10
+ before do
11
+ FileUtils.mkpath refinery_cache_path
12
+ FileUtils.touch refinery_cache_file
13
+ end
14
+
15
+ after do
16
+ FileUtils.rm_rf refinery_cache_path
17
+ FileUtils.rm_rf refinery_cache_file
18
+ end
19
+
20
+ let(:cache) { Caching.new(cache_path) }
21
+
22
+ describe "#expire!" do
23
+ it "should call #clear_caching! and #delete_static_files!" do
24
+ cache.should_receive(:clear_caching!)
25
+ cache.should_receive(:delete_static_files!)
26
+ cache.expire!
27
+ end
28
+
29
+ describe "#clear_caching!" do
30
+ context "rails cache store supports #delete_matched" do
31
+ before { Rails.cache.stub(:delete_matched)}
32
+
33
+ it "should clear rails cache that matched namespace" do
34
+ Rails.cache.should_receive(:delete_matched).with(/.*pages.*/)
35
+ cache.expire!
36
+ end
37
+ end
38
+
39
+ context "rails cache store does not supports #delete_matched" do
40
+ before { Rails.cache.stub(:delete_matched).and_raise(NotImplementedError) }
41
+
42
+ it "should clear rails cache that matched namespace" do
43
+ Rails.cache.should_receive(:clear)
44
+ silence_warnings do
45
+ cache.expire!
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#delete_static_files!" do
52
+ context "valid cache directory" do
53
+ before { cache.should_receive(:cache_dir_valid?).and_return(true) }
54
+
55
+ it "should call #delete_page_cache_directory! and #delete_page_cache_index_file!" do
56
+ cache.should_receive(:delete_page_cache_directory!)
57
+ cache.should_receive(:delete_page_cache_index_file!)
58
+ cache.expire!
59
+ end
60
+
61
+ it "should remove page cache directory" do
62
+ File.exists?(refinery_cache_path).should be_true
63
+ cache.expire!
64
+ File.exists?(refinery_cache_path).should be_false
65
+ end
66
+
67
+ it "should remove home page cache file" do
68
+ File.exists?(refinery_cache_file).should be_true
69
+ cache.expire!
70
+ File.exists?(refinery_cache_file).should be_false
71
+ end
72
+
73
+ end
74
+
75
+ context "invalid cache directory" do
76
+ let(:cache) { Caching.new() }
77
+ before { cache.should_receive(:cache_dir_valid?).and_return(false) }
78
+
79
+ it "should not call #delete_page_cache_directory! and #delete_page_cache_index_file!" do
80
+ cache.should_not_receive(:delete_page_cache_directory!)
81
+ cache.should_not_receive(:delete_page_cache_index_file!)
82
+ cache.expire!
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+ end