alchemy_cms 2.6.3 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (246) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +14 -0
  3. data/.travis.yml +1 -1
  4. data/Gemfile +7 -6
  5. data/README.md +15 -5
  6. data/alchemy_cms.gemspec +3 -2
  7. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +9 -17
  8. data/app/assets/javascripts/alchemy/alchemy.dirty.js.coffee +70 -0
  9. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +80 -0
  10. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +43 -19
  11. data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +3 -1
  12. data/app/assets/javascripts/alchemy/alchemy.js +4 -2
  13. data/app/assets/javascripts/alchemy/alchemy.onload.js.coffee +1 -1
  14. data/app/assets/javascripts/alchemy/alchemy.spinner.js.coffee +14 -0
  15. data/app/assets/javascripts/alchemy/alchemy.tinymce.js.coffee.erb +96 -0
  16. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +22 -0
  17. data/app/assets/javascripts/alchemy/alchemy.windows.js.coffee +28 -17
  18. data/app/assets/stylesheets/alchemy/base.scss +6 -0
  19. data/app/assets/stylesheets/alchemy/elements.scss +2 -28
  20. data/app/assets/stylesheets/alchemy/errors.scss +1 -1
  21. data/app/assets/stylesheets/alchemy/menubar.css.scss +2 -0
  22. data/app/assets/stylesheets/alchemy/sitemap.scss +21 -34
  23. data/app/assets/stylesheets/alchemy/tables.scss +13 -3
  24. data/app/controllers/alchemy/admin/attachments_controller.rb +10 -5
  25. data/app/controllers/alchemy/admin/base_controller.rb +19 -0
  26. data/app/controllers/alchemy/admin/contents_controller.rb +1 -4
  27. data/app/controllers/alchemy/admin/dashboard_controller.rb +2 -1
  28. data/app/controllers/alchemy/admin/elements_controller.rb +1 -1
  29. data/app/controllers/alchemy/admin/essence_files_controller.rb +1 -1
  30. data/app/controllers/alchemy/admin/essence_pictures_controller.rb +70 -56
  31. data/app/controllers/alchemy/admin/pages_controller.rb +37 -114
  32. data/app/controllers/alchemy/admin/pictures_controller.rb +5 -12
  33. data/app/controllers/alchemy/admin/resources_controller.rb +3 -1
  34. data/app/controllers/alchemy/admin/trash_controller.rb +1 -1
  35. data/app/controllers/alchemy/attachments_controller.rb +1 -1
  36. data/app/controllers/alchemy/base_controller.rb +3 -15
  37. data/app/controllers/alchemy/messages_controller.rb +4 -10
  38. data/app/controllers/alchemy/pages_controller.rb +6 -6
  39. data/app/controllers/alchemy/passwords_controller.rb +1 -1
  40. data/app/controllers/alchemy/user_sessions_controller.rb +1 -1
  41. data/app/helpers/alchemy/admin/base_helper.rb +49 -230
  42. data/app/helpers/alchemy/admin/contents_helper.rb +5 -1
  43. data/app/helpers/alchemy/admin/elements_helper.rb +19 -47
  44. data/app/helpers/alchemy/admin/essences_helper.rb +59 -17
  45. data/app/helpers/alchemy/admin/navigation_helper.rb +204 -0
  46. data/app/helpers/alchemy/admin/pages_helper.rb +22 -79
  47. data/app/helpers/alchemy/admin/pictures_helper.rb +1 -1
  48. data/app/helpers/alchemy/admin/tags_helper.rb +42 -0
  49. data/app/helpers/alchemy/base_helper.rb +0 -11
  50. data/app/helpers/alchemy/elements_helper.rb +48 -25
  51. data/app/helpers/alchemy/essences_helper.rb +0 -20
  52. data/app/helpers/alchemy/pages_helper.rb +18 -14
  53. data/app/helpers/alchemy/url_helper.rb +1 -0
  54. data/app/mailers/alchemy/messages.rb +4 -6
  55. data/app/models/alchemy/attachment.rb +3 -0
  56. data/app/models/alchemy/cell.rb +33 -35
  57. data/app/models/alchemy/content.rb +20 -111
  58. data/app/models/alchemy/content/factory.rb +188 -0
  59. data/app/models/alchemy/element.rb +51 -200
  60. data/app/models/alchemy/element/definitions.rb +52 -0
  61. data/app/models/alchemy/element/presenters.rb +87 -0
  62. data/app/models/alchemy/essence_date.rb +1 -1
  63. data/app/models/alchemy/essence_file.rb +6 -7
  64. data/app/models/alchemy/essence_picture.rb +19 -4
  65. data/app/models/alchemy/message.rb +18 -14
  66. data/app/models/alchemy/page.rb +120 -214
  67. data/app/models/alchemy/page/elements.rb +145 -36
  68. data/app/models/alchemy/page/natures.rb +90 -0
  69. data/app/models/alchemy/page/scopes.rb +93 -0
  70. data/app/models/alchemy/page/users.rb +25 -0
  71. data/app/models/alchemy/picture.rb +15 -0
  72. data/app/models/alchemy/site.rb +15 -1
  73. data/app/models/alchemy/site/layout.rb +38 -0
  74. data/app/models/alchemy/user.rb +13 -3
  75. data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +7 -7
  76. data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +8 -8
  77. data/app/views/alchemy/admin/attachments/_tag_list.html.erb +1 -16
  78. data/app/views/alchemy/admin/attachments/destroy.js.erb +1 -4
  79. data/app/views/alchemy/admin/contents/create.js.erb +1 -1
  80. data/app/views/alchemy/admin/dashboard/index.html.erb +14 -13
  81. data/app/views/alchemy/admin/elements/_element_head.html.erb +7 -7
  82. data/app/views/alchemy/admin/elements/_refresh_editor.js.erb +10 -0
  83. data/app/views/alchemy/admin/elements/create.js.erb +44 -44
  84. data/app/views/alchemy/admin/elements/fold.js.erb +22 -26
  85. data/app/views/alchemy/admin/elements/trash.js.erb +1 -1
  86. data/app/views/alchemy/admin/elements/update.js.erb +22 -25
  87. data/app/views/alchemy/admin/essence_files/assign.js.erb +8 -3
  88. data/app/views/alchemy/admin/essence_pictures/crop.html.erb +14 -12
  89. data/app/views/alchemy/admin/essence_pictures/edit.html.erb +22 -39
  90. data/app/views/alchemy/admin/pages/_page.html.erb +73 -80
  91. data/app/views/alchemy/admin/pages/destroy.js.erb +2 -2
  92. data/app/views/alchemy/admin/pages/edit.html.erb +21 -18
  93. data/app/views/alchemy/admin/pages/fold.js.erb +1 -0
  94. data/app/views/alchemy/admin/pages/info.html.erb +32 -0
  95. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +11 -13
  96. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +20 -20
  97. data/app/views/alchemy/admin/partials/_sub_navigation.html.erb +8 -0
  98. data/app/views/alchemy/admin/partials/_toolbar_button.html.erb +25 -0
  99. data/app/views/alchemy/admin/partials/_upload_form.html.erb +15 -15
  100. data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +39 -39
  101. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +10 -10
  102. data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -16
  103. data/app/views/alchemy/admin/resources/destroy.js.erb +1 -1
  104. data/app/views/alchemy/base/500.html.erb +1 -1
  105. data/app/views/alchemy/base/permission_denied.js.erb +1 -1
  106. data/app/views/alchemy/base/redirect.js.erb +1 -1
  107. data/app/views/alchemy/essences/_essence_link_editor.html.erb +1 -1
  108. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +1 -1
  109. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +1 -1
  110. data/app/views/alchemy/essences/_essence_text_editor.html.erb +1 -1
  111. data/app/views/alchemy/essences/{_essence_picture_tools.html.erb → shared/_essence_picture_tools.html.erb} +5 -5
  112. data/app/views/alchemy/essences/{_linkable_essence_tools.html.erb → shared/_linkable_essence_tools.html.erb} +0 -0
  113. data/app/views/alchemy/messages/contact_form_mail.de.text.erb +12 -0
  114. data/app/views/alchemy/messages/contact_form_mail.en.text.erb +12 -0
  115. data/app/views/alchemy/notifications/reset_password_instructions.de.text.erb +1 -1
  116. data/app/views/alchemy/notifications/reset_password_instructions.en.text.erb +2 -2
  117. data/app/views/alchemy/pages/sitemap.xml.erb +3 -5
  118. data/app/views/alchemy/user_sessions/leave.html.erb +1 -1
  119. data/app/views/layouts/alchemy/admin.html.erb +4 -2
  120. data/app/views/layouts/alchemy/sitemap.xml.erb +1 -1
  121. data/bin/alchemy +7 -13
  122. data/config/alchemy/config.yml +1 -0
  123. data/config/authorization_rules.rb +2 -3
  124. data/config/initializers/dragonfly.rb +2 -0
  125. data/config/locales/alchemy.de.yml +8 -9
  126. data/config/locales/alchemy.en.yml +7 -4
  127. data/config/routes.rb +3 -0
  128. data/db/migrate/{20130214233001_alchemy_two_point_five.rb → 20130827094554_alchemy_two_point_six.rb} +29 -6
  129. data/lib/alchemy/auth/engine.rb +9 -0
  130. data/lib/alchemy/capistrano.rb +37 -12
  131. data/lib/alchemy/config.rb +48 -35
  132. data/lib/alchemy/engine.rb +35 -6
  133. data/lib/alchemy/essence.rb +25 -29
  134. data/lib/alchemy/ferret/search.rb +86 -0
  135. data/lib/alchemy/{scoped_pagination_url_helper.rb → kaminari/scoped_pagination_url_helper.rb} +0 -0
  136. data/lib/alchemy/logger.rb +3 -4
  137. data/lib/alchemy/page_layout.rb +124 -55
  138. data/lib/alchemy/resource.rb +0 -10
  139. data/lib/alchemy/resources_helper.rb +0 -5
  140. data/lib/alchemy/seeder.rb +1 -32
  141. data/lib/alchemy/shell.rb +6 -1
  142. data/lib/alchemy/tinymce.rb +41 -32
  143. data/lib/alchemy/upgrader.rb +3 -1
  144. data/lib/alchemy/upgrader/two_point_five.rb +15 -8
  145. data/lib/alchemy/upgrader/two_point_one.rb +10 -10
  146. data/lib/alchemy/upgrader/two_point_two.rb +96 -51
  147. data/lib/alchemy/version.rb +1 -1
  148. data/lib/alchemy_cms.rb +5 -46
  149. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +1 -1
  150. data/lib/rails/generators/alchemy/devise/devise_generator.rb +9 -4
  151. data/lib/rails/generators/alchemy/essence/essence_generator.rb +7 -6
  152. data/lib/rails/generators/alchemy/essence/templates/editor.html.erb +1 -1
  153. data/lib/rails/generators/alchemy/scaffold/files/_standard.html.erb +1 -0
  154. data/lib/rails/generators/alchemy/scaffold/scaffold_generator.rb +1 -0
  155. data/lib/rails/generators/alchemy/site_layouts/site_layouts_generator.rb +23 -0
  156. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.erb +1 -0
  157. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.haml +1 -0
  158. data/lib/rails/generators/alchemy/site_layouts/templates/layout.html.slim +1 -0
  159. data/lib/rails/templates/alchemy.rb +2 -2
  160. data/lib/tasks/alchemy/db.rake +3 -1
  161. data/lib/tasks/alchemy/tidy.rake +82 -0
  162. data/lib/tasks/alchemy/upgrade.rake +2 -1
  163. data/spec/controllers/admin/attachments_controller_spec.rb +124 -0
  164. data/spec/controllers/admin/base_controller_spec.rb +35 -0
  165. data/spec/controllers/admin/clipboard_controller_spec.rb +1 -1
  166. data/spec/controllers/admin/contents_controller_spec.rb +17 -26
  167. data/spec/controllers/admin/dashboard_controller_spec.rb +121 -0
  168. data/spec/controllers/admin/elements_controller_spec.rb +1 -1
  169. data/spec/controllers/admin/essence_files_controller_spec.rb +67 -0
  170. data/spec/controllers/admin/essence_pictures_controller_spec.rb +161 -0
  171. data/spec/controllers/admin/languages_controller_spec.rb +1 -1
  172. data/spec/controllers/admin/layoutpages_controller_spec.rb +28 -0
  173. data/spec/controllers/admin/pages_controller_spec.rb +164 -118
  174. data/spec/controllers/admin/pictures_controller_spec.rb +89 -0
  175. data/spec/controllers/admin/trash_controller_spec.rb +21 -31
  176. data/spec/controllers/admin/users_controller_spec.rb +114 -85
  177. data/spec/controllers/attachments_controller_spec.rb +6 -2
  178. data/spec/controllers/base_controller_spec.rb +22 -0
  179. data/spec/controllers/elements_controller_spec.rb +1 -1
  180. data/spec/controllers/messages_controller_spec.rb +200 -0
  181. data/spec/controllers/pictures_controller_spec.rb +1 -1
  182. data/spec/controllers/user_sessions_controller_spec.rb +7 -6
  183. data/spec/controllers/users_controller_spec.rb +2 -2
  184. data/spec/dummy/config/alchemy/cells.yml +2 -0
  185. data/spec/dummy/config/application.rb +19 -8
  186. data/spec/dummy/db/migrate/{20130214233001_alchemy_two_point_five.rb → 20130827094554_alchemy_two_point_six.rb} +29 -6
  187. data/spec/dummy/db/schema.rb +1 -1
  188. data/spec/fast_specs.rb +15 -0
  189. data/spec/helpers/admin/base_helper_spec.rb +53 -34
  190. data/spec/helpers/admin/contents_helper_spec.rb +15 -7
  191. data/spec/helpers/admin/elements_helper_spec.rb +79 -34
  192. data/spec/helpers/admin/essences_helper_spec.rb +45 -31
  193. data/spec/helpers/admin/navigation_helper_spec.rb +204 -0
  194. data/spec/helpers/admin/pages_helper_spec.rb +25 -15
  195. data/spec/helpers/admin/tags_helper_spec.rb +62 -2
  196. data/spec/helpers/elements_helper_spec.rb +202 -138
  197. data/spec/helpers/pages_helper_spec.rb +48 -0
  198. data/spec/helpers/url_helper_spec.rb +7 -0
  199. data/spec/libraries/config_spec.rb +110 -3
  200. data/spec/libraries/essence_spec.rb +29 -9
  201. data/spec/libraries/page_layout_spec.rb +134 -0
  202. data/spec/libraries/resource_spec.rb +3 -16
  203. data/spec/libraries/resources_helper_spec.rb +4 -8
  204. data/spec/libraries/shell_spec.rb +1 -0
  205. data/spec/libraries/tinymce_spec.rb +61 -0
  206. data/spec/mailers/messages_spec.rb +23 -0
  207. data/spec/models/attachment_spec.rb +45 -0
  208. data/spec/models/cell_spec.rb +62 -9
  209. data/spec/models/content_spec.rb +110 -28
  210. data/spec/models/element_spec.rb +275 -253
  211. data/spec/models/essence_date_spec.rb +25 -0
  212. data/spec/models/essence_file_spec.rb +23 -0
  213. data/spec/models/essence_html_spec.rb +13 -0
  214. data/spec/models/essence_picture_spec.rb +16 -0
  215. data/spec/models/essence_text_spec.rb +29 -0
  216. data/spec/models/language_spec.rb +34 -0
  217. data/spec/models/message_spec.rb +43 -0
  218. data/spec/models/page_spec.rb +726 -567
  219. data/spec/models/picture_spec.rb +98 -0
  220. data/spec/models/site_spec.rb +60 -2
  221. data/spec/models/tag_spec.rb +31 -0
  222. data/spec/models/user_spec.rb +4 -4
  223. data/spec/spec_helper.rb +49 -58
  224. data/spec/support/alchemy/controller_helpers.rb +35 -0
  225. data/spec/support/alchemy/{specs_helpers.rb → integration_helpers.rb} +4 -8
  226. data/spec/{factories.rb → support/factories.rb} +11 -1
  227. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.nestedSortable.js +2 -8
  228. metadata +166 -106
  229. data/Guardfile +0 -16
  230. data/app/assets/javascripts/alchemy/alchemy.dirty.js +0 -93
  231. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +0 -122
  232. data/app/models/alchemy/tree_node.rb +0 -4
  233. data/app/views/alchemy/admin/pages/_page_infos.html.erb +0 -3
  234. data/app/views/alchemy/admin/partials/_sub_navigation_tab.html.erb +0 -8
  235. data/app/views/alchemy/messages/contact_form_mail.text.erb +0 -12
  236. data/config/initializers/kaminari_config.rb +0 -9
  237. data/db/migrate/20130221200514_migrate_attachments_to_dragonfly.rb +0 -21
  238. data/db/migrate/20130312205327_change_alchemy_users_role_to_roles.rb +0 -11
  239. data/lib/alchemy/auth_engine.rb +0 -7
  240. data/lib/alchemy/authentication_helpers.rb +0 -9
  241. data/lib/alchemy/ferret_search.rb +0 -84
  242. data/lib/extensions/array.rb +0 -25
  243. data/lib/extensions/hash.rb +0 -34
  244. data/spec/dummy/db/migrate/20130221200514_migrate_attachments_to_dragonfly.rb +0 -21
  245. data/spec/dummy/db/migrate/20130312205327_change_alchemy_users_role_to_roles.rb +0 -11
  246. data/spec/models/page_layout_spec.rb +0 -60
@@ -1,305 +1,331 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
3
 
3
4
  module Alchemy
4
5
  describe Element do
6
+ # to prevent memoization
7
+ before { Element.instance_variable_set("@definitions", nil) }
5
8
 
6
- describe '#new_from_scratch' do
9
+ # ClassMethods
7
10
 
8
- it "should initialize an element by name from scratch" do
9
- el = Element.new_from_scratch({:name => 'article'})
10
- el.should be_valid
11
+ describe '.copy' do
12
+ let(:element) { FactoryGirl.create(:element, :create_contents_after_create => true, :tag_list => 'red, yellow') }
13
+
14
+ it "should not create contents from scratch" do
15
+ copy = Element.copy(element)
16
+ copy.contents.count.should == element.contents.count
11
17
  end
12
18
 
13
- it "should raise an error if the given name is not defined in the elements.yml" do
14
- expect { Element.new_from_scratch({:name => 'foobar'}) }.to raise_error
19
+ it "should create a new record with all attributes of source except given differences" do
20
+ copy = Element.copy(element, {:name => 'foobar'})
21
+ copy.name.should == 'foobar'
15
22
  end
16
23
 
17
- it "should take the first part of an given name containing a hash (#)" do
18
- el = Element.new_from_scratch({:name => 'article#header'})
19
- el.name.should == "article"
24
+ it "should make copies of all contents of source" do
25
+ copy = Element.copy(element)
26
+ copy.contents.collect(&:id).should_not == element.contents.collect(&:id)
20
27
  end
21
28
 
22
- it "should merge given attributes into defined ones" do
23
- el = Element.new_from_scratch({:name => 'article', :page_id => 1})
24
- el.page_id.should == 1
29
+ it "the copy should include source element tags" do
30
+ copy = Element.copy(element)
31
+ copy.tag_list.should == element.tag_list
25
32
  end
33
+ end
26
34
 
27
- it "should not have forbidden attributes from definition" do
28
- el = Element.new_from_scratch({:name => 'article'})
29
- el.contents.should == []
35
+ describe '.definitions' do
36
+ context "without existing yml files" do
37
+ before { File.stub!(:exists?).and_return(false) }
38
+
39
+ it "should raise an error" do
40
+ expect { Element.definitions }.to raise_error(LoadError)
41
+ end
30
42
  end
31
43
 
32
- end
44
+ context "without any definitions in elements.yml" do
45
+ before { YAML.stub!(:load_file).and_return(false) } # Yes, YAML.load_file returns false if an empty file exists.
33
46
 
34
- context "scoped" do
47
+ it "should return an empty array" do
48
+ Element.definitions.should == []
49
+ end
50
+ end
51
+ end
35
52
 
36
- it "should return all public elements" do
37
- element_1 = FactoryGirl.create(:element, :public => true)
38
- element_2 = FactoryGirl.create(:element, :public => true)
39
- elements = Element.published.all
40
- elements.should include(element_1)
41
- elements.should include(element_2)
53
+ describe '.display_name_for' do
54
+ it "should return the translation for the given name" do
55
+ I18n.should_receive(:t).with('subheadline', scope: "element_names", default: 'Subheadline').and_return('Überschrift')
56
+ expect(Element.display_name_for('subheadline')).to eq('Überschrift')
42
57
  end
43
58
 
44
- it "should return all elements by name" do
45
- element_1 = FactoryGirl.create(:element, :name => 'article')
46
- element_2 = FactoryGirl.create(:element, :name => 'article')
47
- elements = Element.named(['article']).all
48
- elements.should include(element_1)
49
- elements.should include(element_2)
59
+ it "should return the humanized name if no translation found" do
60
+ expect(Element.display_name_for('not_existing_one')).to eq('Not existing one')
50
61
  end
62
+ end
51
63
 
64
+ describe '.excluded' do
52
65
  it "should return all elements but excluded ones" do
53
66
  FactoryGirl.create(:element, :name => 'article')
54
67
  FactoryGirl.create(:element, :name => 'article')
55
68
  excluded = FactoryGirl.create(:element, :name => 'claim')
56
69
  Element.excluded(['claim']).all.should_not include(excluded)
57
70
  end
71
+ end
58
72
 
59
- context "not_in_cell" do
60
-
61
- it "should return all elements that are not in a cell" do
62
- Element.delete_all
63
- FactoryGirl.create(:element, :cell_id => 6)
64
- FactoryGirl.create(:element, :cell_id => nil)
65
- Element.not_in_cell.should have(1).element
66
- end
67
-
73
+ describe '.named' do
74
+ it "should return all elements by name" do
75
+ element_1 = FactoryGirl.create(:element, :name => 'article')
76
+ element_2 = FactoryGirl.create(:element, :name => 'article')
77
+ elements = Element.named(['article']).all
78
+ elements.should include(element_1)
79
+ elements.should include(element_2)
68
80
  end
69
-
70
81
  end
71
82
 
72
- describe '.all_definitions_for' do
73
-
74
- it "should return a list of element definitions for a list of element names" do
75
- element_names = ["article"]
76
- definitions = Element.all_definitions_for(element_names)
77
- definitions.first.fetch("name").should == 'article'
83
+ describe '.not_in_cell' do
84
+ it "should return all elements that are not in a cell" do
85
+ Element.delete_all
86
+ FactoryGirl.create(:element, :cell_id => 6)
87
+ FactoryGirl.create(:element, :cell_id => nil)
88
+ Element.not_in_cell.should have(1).element
78
89
  end
90
+ end
79
91
 
80
- context "given 'all' as element name" do
92
+ describe '.published' do
93
+ it "should return all public elements" do
94
+ element_1 = FactoryGirl.create(:element, :public => true)
95
+ element_2 = FactoryGirl.create(:element, :public => true)
96
+ elements = Element.published.all
97
+ elements.should include(element_1)
98
+ elements.should include(element_2)
99
+ end
100
+ end
81
101
 
82
- before do
83
- @element_definition = [
84
- {'name' => 'article'},
85
- {'name' => 'headline'}
86
- ]
87
- Element.stub!(:definitions).and_return @element_definition
102
+ context 'trash' do
103
+ describe '.not_trashed' do
104
+ let(:element) do
105
+ FactoryGirl.create(:element, :page_id => 1)
88
106
  end
89
107
 
90
- it "should return all element definitions" do
91
- Element.all_definitions_for('all').should == @element_definition
108
+ it "should return a collection of not trashed elements" do
109
+ Element.not_trashed.should include(element)
92
110
  end
93
-
94
111
  end
95
112
 
96
- it "should always return an array" do
97
- definitions = Element.all_definitions_for(nil)
98
- definitions.should == []
99
- end
113
+ describe ".trashed" do
114
+ let(:element) do
115
+ FactoryGirl.create(:element, :page_id => 1)
116
+ end
100
117
 
118
+ it "should return a collection of trashed elements" do
119
+ not_trashed_element = FactoryGirl.create(:element)
120
+ element.trash
121
+ Element.trashed.should include(element)
122
+ end
123
+ end
101
124
  end
102
125
 
103
- context "no description files are found" do
126
+ describe '.all_from_clipboard_for_page' do
127
+ let(:element_1) { FactoryGirl.build_stubbed(:element) }
128
+ let(:element_2) { FactoryGirl.build_stubbed(:element, name: 'news') }
129
+ let(:page) { FactoryGirl.build_stubbed(:public_page) }
130
+ let(:clipboard) { [{id: element_1.id}, {id: element_2.id}] }
131
+ before {
132
+ Element.stub(:find_all_by_id).and_return([element_1, element_2])
133
+ }
104
134
 
105
- before do
106
- FileUtils.mv(File.join(File.dirname(__FILE__), '../dummy/config/alchemy/elements.yml'), File.join(File.dirname(__FILE__), '../dummy/config/alchemy/elements.yml.bak'))
135
+ it "return all elements from clipboard that could be placed on page" do
136
+ elements = Element.all_from_clipboard_for_page(clipboard, page)
137
+ elements.should == [element_1]
138
+ elements.should_not == [element_2]
107
139
  end
108
140
 
109
- it "should raise an error" do
110
- expect { Element.descriptions }.to raise_error(LoadError)
111
- end
112
-
113
- after do
114
- FileUtils.mv(File.join(File.dirname(__FILE__), '../dummy/config/alchemy/elements.yml.bak'), File.join(File.dirname(__FILE__), '../dummy/config/alchemy/elements.yml'))
141
+ context "page nil" do
142
+ it "returns empty array" do
143
+ Element.all_from_clipboard_for_page(clipboard, nil).should == []
144
+ end
115
145
  end
116
146
 
117
- end
118
-
119
- context "without any descriptions in elements.yml file" do
120
-
121
- it "should return an empty array" do
122
- YAML.stub(:load_file).and_return(false) # Yes, YAML.load_file returns false if an empty file exists.
123
- Element.descriptions.should == []
147
+ context "clipboard nil" do
148
+ it "returns empty array" do
149
+ Element.all_from_clipboard_for_page(nil, page).should == []
150
+ end
124
151
  end
125
-
126
152
  end
127
153
 
128
- context "retrieving contents, essences and ingredients" do
129
-
130
- let(:element) { FactoryGirl.create(:element, :name => 'news', :create_contents_after_create => true) }
154
+ # InstanceMethods
131
155
 
132
- it "should return an ingredient by name" do
133
- element.ingredient('news_headline').should == EssenceText.first.ingredient
134
- end
156
+ describe '#all_contents_by_type' do
157
+ let(:element) { FactoryGirl.create(:element, create_contents_after_create: true) }
158
+ let(:expected_contents) { element.contents.essence_texts }
135
159
 
136
- it "should return the content for rss title" do
137
- element.content_for_rss_title.should == element.contents.find_by_name('news_headline')
160
+ context "with namespaced essence type" do
161
+ subject { element.all_contents_by_type('Alchemy::EssenceText') }
162
+ it { should_not be_empty }
163
+ it('should return the correct list of essences') { should == expected_contents }
138
164
  end
139
165
 
140
- it "should return the content for rss description" do
141
- element.content_for_rss_description.should == element.contents.find_by_name('body')
166
+ context "without namespaced essence type" do
167
+ subject { element.all_contents_by_type('EssenceText') }
168
+ it { should_not be_empty }
169
+ it('should return the correct list of essences') { should == expected_contents }
142
170
  end
143
-
144
171
  end
145
172
 
146
- context "limited amount" do
147
-
173
+ describe '#belonging_cellnames' do
148
174
  before do
149
- defs = [
150
- {
151
- 'name' => 'column_headline',
152
- 'amount' => 3,
153
- 'contents' => [{'name' => 'headline', 'type' => 'EssenceText'}]
154
- },
155
- {
156
- 'name' => 'unique_headline',
157
- 'unique' => true,
158
- 'amount' => 3,
159
- 'contents' => [{'name' => 'headline', 'type' => 'EssenceText'}]
160
- }
161
- ]
162
- # F&%#ing alias methods
163
- Element.stub!(:definitions).and_return(defs)
164
- Element.stub!(:descriptions).and_return(defs)
165
- PageLayout.stub!(:get).and_return({
166
- 'name' => 'columns',
167
- 'elements' => ['column_headline', 'unique_headline'],
168
- 'autogenerate' => ['unique_headline', 'column_headline', 'column_headline', 'column_headline']
169
- })
170
- @page = FactoryGirl.create(:page, :page_layout => 'columns', :do_not_autogenerate => false)
171
- end
172
-
173
- it "should be readable" do
174
- element = Element.all_definitions_for(['column_headline']).first
175
- element['amount'].should be 3
176
- end
177
-
178
- it "should limit elements" do
179
- Element.all_for_page(@page).each { |e| e['name'].should_not == 'column_headline' }
180
- end
181
-
182
- it "should be ignored if unique" do
183
- Element.all_for_page(@page).each { |e| e['name'].should_not == 'unique_headline' }
175
+ @page = FactoryGirl.create(:public_page)
176
+ @element = FactoryGirl.create(:element, :page => @page)
184
177
  end
185
178
 
186
- end
187
-
188
- context "collections" do
189
-
190
- context "for trashed elements" do
191
-
192
- let(:element) do
193
- FactoryGirl.create(:element, :page_id => 1)
179
+ context "with page having cells defining the correct elements" do
180
+ before do
181
+ Cell.stub!(:definitions).and_return([
182
+ {'name' => 'header', 'elements' => ['article', 'headline']},
183
+ {'name' => 'footer', 'elements' => ['article', 'text']},
184
+ {'name' => 'sidebar', 'elements' => ['teaser']}
185
+ ])
194
186
  end
195
187
 
196
- it "should return a collection of trashed elements" do
197
- not_trashed_element = FactoryGirl.create(:element)
198
- element.trash
199
- Element.trashed.should include(element)
188
+ it "should return a list of all cells from given page this element could be placed in" do
189
+ @header_cell = FactoryGirl.create(:cell, :name => 'header', :page => @page)
190
+ @footer_cell = FactoryGirl.create(:cell, :name => 'footer', :page => @page)
191
+ @sidebar_cell = FactoryGirl.create(:cell, :name => 'sidebar', :page => @page)
192
+ @element.belonging_cellnames(@page).should include('header')
193
+ @element.belonging_cellnames(@page).should include('footer')
200
194
  end
201
195
 
202
- it "should return a collection of not trashed elements" do
203
- Element.not_trashed.should include(element)
196
+ context "but without any cells" do
197
+ it "should return the 'nil cell'" do
198
+ @element.belonging_cellnames(@page).should == ['for_other_elements']
199
+ end
204
200
  end
205
201
 
206
202
  end
207
203
 
208
- end
209
-
210
- describe "#trash" do
211
- let(:element) { FactoryGirl.create(:element, :page_id => 1, :cell_id => 1) }
212
- let(:trashed_element) { element.trash ; element }
213
- subject { trashed_element }
214
-
215
- it { should_not be_public }
216
- its(:position) { should be_nil }
217
- specify { expect { element.trash }.to_not change(element, :page_id) }
218
- specify { expect { element.trash }.to_not change(element, :cell_id) }
204
+ context "with page having cells defining the wrong elements" do
205
+ before do
206
+ Cell.stub!(:definitions).and_return([
207
+ {'name' => 'header', 'elements' => ['download', 'headline']},
208
+ {'name' => 'footer', 'elements' => ['contactform', 'text']},
209
+ {'name' => 'sidebar', 'elements' => ['teaser']}
210
+ ])
211
+ end
219
212
 
220
- it "it should be possible to trash more than one element from the same page" do
221
- trashed_element_2 = FactoryGirl.create(:element, :page_id => 1)
222
- trashed_element_2.trash
223
- Element.trashed.should include(trashed_element, trashed_element_2)
213
+ it "should return the 'nil cell'" do
214
+ @header_cell = FactoryGirl.create(:cell, :name => 'header', :page => @page)
215
+ @footer_cell = FactoryGirl.create(:cell, :name => 'footer', :page => @page)
216
+ @sidebar_cell = FactoryGirl.create(:cell, :name => 'sidebar', :page => @page)
217
+ @element.belonging_cellnames(@page).should == ['for_other_elements']
218
+ end
224
219
  end
225
-
226
- end
227
-
228
- it "should raise error if all_for_page method has no page" do
229
- expect { Element.all_for_page(nil) }.to raise_error(TypeError)
230
220
  end
231
221
 
232
- describe "#content_by_type" do
233
-
222
+ describe '#content_by_type' do
234
223
  before(:each) do
235
224
  @element = FactoryGirl.create(:element, :name => 'headline')
236
225
  @content = @element.contents.first
237
226
  end
238
227
 
239
228
  context "with namespaced essence type" do
240
-
241
229
  it "should return content by passing a essence type" do
242
230
  @element.content_by_type('Alchemy::EssenceText').should == @content
243
231
  end
244
-
245
232
  end
246
233
 
247
234
  context "without namespaced essence type" do
248
-
249
235
  it "should return content by passing a essence type" do
250
236
  @element.content_by_type('EssenceText').should == @content
251
237
  end
252
-
253
238
  end
254
-
255
239
  end
256
240
 
257
- describe "#all_contents_by_type" do
258
- let(:element) { FactoryGirl.create(:element, create_contents_after_create: true) }
259
- let(:expected_contents) { element.contents.essence_texts }
241
+ describe '#display_name' do
242
+ let(:element) { Element.new(name: 'article') }
260
243
 
261
- context "with namespaced essence type" do
262
- subject { element.all_contents_by_type('Alchemy::EssenceText') }
263
- it { should_not be_empty }
264
- it('should return the correct list of essences') { should == expected_contents }
244
+ it "should call .display_name_for" do
245
+ Element.should_receive(:display_name_for).with(element.name)
246
+ element.display_name
265
247
  end
248
+ end
266
249
 
267
- context "without namespaced essence type" do
268
- subject { element.all_contents_by_type('EssenceText') }
269
- it { should_not be_empty }
270
- it('should return the correct list of essences') { should == expected_contents }
271
- end
250
+ describe '#display_name_with_preview_text' do
251
+ let(:element) { FactoryGirl.build_stubbed(:element, name: 'Foo') }
272
252
 
253
+ it "returns a string with display name and preview text" do
254
+ element.stub(:preview_text).and_return('Fula')
255
+ element.display_name_with_preview_text.should == "Foo: Fula"
256
+ end
273
257
  end
274
258
 
275
- describe '.copy' do
259
+ describe '#dom_id' do
260
+ let(:element) { FactoryGirl.build_stubbed(:element) }
276
261
 
277
- let(:element) { FactoryGirl.create(:element, :create_contents_after_create => true, :tag_list => 'red, yellow') }
262
+ it "returns an string from element name and id" do
263
+ element.dom_id.should == "#{element.name}_#{element.id}"
264
+ end
265
+ end
278
266
 
279
- it "should not create contents from scratch" do
280
- copy = Element.copy(element)
281
- copy.contents.count.should == element.contents.count
267
+ describe '#new_from_scratch' do
268
+ it "should initialize an element by name from scratch" do
269
+ el = Element.new_from_scratch({:name => 'article'})
270
+ el.should be_valid
282
271
  end
283
272
 
284
- it "should create a new record with all attributes of source except given differences" do
285
- copy = Element.copy(element, {:name => 'foobar'})
286
- copy.name.should == 'foobar'
273
+ it "should raise an error if the given name is not defined in the elements.yml" do
274
+ expect { Element.new_from_scratch({:name => 'foobar'}) }.to raise_error
287
275
  end
288
276
 
289
- it "should make copies of all contents of source" do
290
- copy = Element.copy(element)
291
- copy.contents.collect(&:id).should_not == element.contents.collect(&:id)
277
+ it "should take the first part of an given name containing a hash (#)" do
278
+ el = Element.new_from_scratch({:name => 'article#header'})
279
+ el.name.should == "article"
292
280
  end
293
281
 
294
- it "the copy should include source element tags" do
295
- copy = Element.copy(element)
296
- copy.tag_list.should == element.tag_list
282
+ it "should merge given attributes into defined ones" do
283
+ el = Element.new_from_scratch({:name => 'article', :page_id => 1})
284
+ el.page_id.should == 1
297
285
  end
298
286
 
287
+ it "should not have forbidden attributes from definition" do
288
+ el = Element.new_from_scratch({:name => 'article'})
289
+ el.contents.should == []
290
+ end
299
291
  end
300
292
 
301
- describe "Finding previous or next element." do
293
+ describe '#preview_text' do
294
+ let(:element) { FactoryGirl.build_stubbed(:element) }
295
+ let(:content) { mock_model(Content, preview_text: 'Lorem', preview_content?: false) }
296
+ let(:content_2) { mock_model(Content, preview_text: 'Lorem', preview_content?: false) }
297
+ let(:preview_content) { mock_model(Content, preview_text: 'Lorem', preview_content?: true) }
298
+
299
+ context "without a content marked as preview" do
300
+ let(:contents) { [content, content_2] }
301
+ before { element.stub!(:contents).and_return(contents) }
302
+
303
+ it "returns the preview text of first content found" do
304
+ content.should_receive(:preview_text).with(30)
305
+ element.preview_text
306
+ end
307
+ end
308
+
309
+ context "with a content marked as preview" do
310
+ let(:contents) { [content, preview_content] }
311
+ before { element.stub!(:contents).and_return(contents) }
312
+
313
+ it "should return the preview_text of this content" do
314
+ preview_content.should_receive(:preview_text).with(30)
315
+ element.preview_text
316
+ end
317
+ end
318
+
319
+ context "without any contents present" do
320
+ before { element.stub!(:contents).and_return([]) }
302
321
 
322
+ it "should return nil" do
323
+ element.preview_text.should be_nil
324
+ end
325
+ end
326
+ end
327
+
328
+ context 'previous and next elements.' do
303
329
  let(:page) { FactoryGirl.create(:language_root_page) }
304
330
 
305
331
  before(:each) do
@@ -309,7 +335,6 @@ module Alchemy
309
335
  end
310
336
 
311
337
  describe '#prev' do
312
-
313
338
  it "should return previous element on same page" do
314
339
  @element3.prev.should == @element2
315
340
  end
@@ -319,11 +344,9 @@ module Alchemy
319
344
  @element3.prev('headline').should == @element1
320
345
  end
321
346
  end
322
-
323
347
  end
324
348
 
325
349
  describe '#next' do
326
-
327
350
  it "should return next element on same page" do
328
351
  @element2.next.should == @element3
329
352
  end
@@ -333,99 +356,98 @@ module Alchemy
333
356
  @element1.next('text').should == @element3
334
357
  end
335
358
  end
336
-
337
359
  end
338
-
339
360
  end
340
361
 
341
- describe '#belonging_cellnames' do
362
+ context 'retrieving contents, essences and ingredients' do
363
+ let(:element) { FactoryGirl.create(:element, :name => 'news', :create_contents_after_create => true) }
342
364
 
343
- before do
344
- @page = FactoryGirl.create(:public_page)
345
- @element = FactoryGirl.create(:element, :page => @page)
365
+ it "should return an ingredient by name" do
366
+ element.ingredient('news_headline').should == EssenceText.first.ingredient
346
367
  end
347
368
 
348
- context "with page having cells defining the correct elements" do
349
-
350
- before do
351
- Cell.stub!(:definitions).and_return([
352
- {'name' => 'header', 'elements' => ['article', 'headline']},
353
- {'name' => 'footer', 'elements' => ['article', 'text']},
354
- {'name' => 'sidebar', 'elements' => ['teaser']}
355
- ])
356
- end
357
-
358
- it "should return a list of all cells from given page this element could be placed in" do
359
- @header_cell = FactoryGirl.create(:cell, :name => 'header', :page => @page)
360
- @footer_cell = FactoryGirl.create(:cell, :name => 'footer', :page => @page)
361
- @sidebar_cell = FactoryGirl.create(:cell, :name => 'sidebar', :page => @page)
362
- @element.belonging_cellnames(@page).should include('header')
363
- @element.belonging_cellnames(@page).should include('footer')
364
- end
365
-
366
- context "but without any cells" do
367
-
368
- it "should return the 'nil cell'" do
369
- @element.belonging_cellnames(@page).should == ['for_other_elements']
370
- end
371
-
372
- end
369
+ it "should return the content for rss title" do
370
+ element.content_for_rss_title.should == element.contents.find_by_name('news_headline')
371
+ end
373
372
 
373
+ it "should return the content for rss descdefinitionription" do
374
+ element.content_for_rss_description.should == element.contents.find_by_name('body')
374
375
  end
376
+ end
375
377
 
376
- context "with page having cells defining the wrong elements" do
378
+ describe '#save' do
379
+ let(:time) { Time.now }
380
+ let(:locker) { FactoryGirl.build_stubbed(:user) }
381
+ let(:page) { FactoryGirl.create(:page, updated_at: time) }
382
+ let(:element) { FactoryGirl.create(:element, page: page) }
377
383
 
378
- before do
379
- Cell.stub!(:definitions).and_return([
380
- {'name' => 'header', 'elements' => ['download', 'headline']},
381
- {'name' => 'footer', 'elements' => ['contactform', 'text']},
382
- {'name' => 'sidebar', 'elements' => ['teaser']}
383
- ])
384
- end
384
+ before { User.stub(:stamper).and_return(locker.id) }
385
385
 
386
- it "should return the 'nil cell'" do
387
- @header_cell = FactoryGirl.create(:cell, :name => 'header', :page => @page)
388
- @footer_cell = FactoryGirl.create(:cell, :name => 'footer', :page => @page)
389
- @sidebar_cell = FactoryGirl.create(:cell, :name => 'sidebar', :page => @page)
390
- @element.belonging_cellnames(@page).should == ['for_other_elements']
391
- end
386
+ it "updates page timestamps" do
387
+ element.save
388
+ page.reload
389
+ page.updated_at.should_not eq(time)
390
+ end
392
391
 
392
+ it "updates page userstamps" do
393
+ element.save
394
+ page.reload
395
+ page.updater_id.should eq(locker.id)
393
396
  end
397
+ end
394
398
 
399
+ describe '#save_contents' do
400
+ it "should return true if attributes hash is nil" do
401
+ element = FactoryGirl.create(:element, create_contents_after_create: true)
402
+ element.save_contents(nil).should be_true
403
+ end
395
404
  end
396
405
 
397
406
  describe '#taggable?' do
407
+ let(:element) { FactoryGirl.build(:element) }
398
408
 
399
- context "description has 'taggable' key with true value" do
409
+ context "definition has 'taggable' key with true value" do
400
410
  it "should return true" do
401
- element = FactoryGirl.build(:element)
402
- element.stub(:description).and_return({'name' => 'article', 'taggable' => true})
411
+ element.stub(:definition).and_return({'name' => 'article', 'taggable' => true})
403
412
  element.taggable?.should be_true
404
413
  end
405
414
  end
406
415
 
407
- context "description has 'taggable' key with foo value" do
416
+ context "definition has 'taggable' key with foo value" do
408
417
  it "should return false" do
409
- element = FactoryGirl.build(:element)
410
- element.stub(:description).and_return({'name' => 'article', 'taggable' => 'foo'})
418
+ element.stub(:definition).and_return({'name' => 'article', 'taggable' => 'foo'})
411
419
  element.taggable?.should be_false
412
420
  end
413
421
  end
414
422
 
415
- context "description has no 'taggable' key" do
423
+ context "definition has no 'taggable' key" do
416
424
  it "should return false" do
417
- element = FactoryGirl.build(:element)
418
- element.stub(:description).and_return({'name' => 'article'})
425
+ element.stub(:definition).and_return({'name' => 'article'})
419
426
  element.taggable?.should be_false
420
427
  end
421
428
  end
429
+ end
430
+
431
+ describe '#trash' do
432
+ let(:element) { FactoryGirl.create(:element, :page_id => 1, :cell_id => 1) }
433
+ let(:trashed_element) { element.trash ; element }
434
+ subject { trashed_element }
435
+
436
+ it { should_not be_public }
437
+ its(:position) { should be_nil }
438
+ specify { expect { element.trash }.to_not change(element, :page_id) }
439
+ specify { expect { element.trash }.to_not change(element, :cell_id) }
422
440
 
441
+ it "it should be possible to trash more than one element from the same page" do
442
+ trashed_element_2 = FactoryGirl.create(:element, :page_id => 1)
443
+ trashed_element_2.trash
444
+ Element.trashed.should include(trashed_element, trashed_element_2)
445
+ end
423
446
  end
424
447
 
425
- describe '#save_contents' do
426
- it "should return true if attributes hash is nil" do
427
- element = FactoryGirl.create(:element, create_contents_after_create: true)
428
- element.save_contents(nil).should be_true
448
+ describe "#to_partial_path" do
449
+ it "should return a String in the format of 'alchemy/elements/#{name}_view'" do
450
+ expect(Element.new(name: 'mock').to_partial_path).to eq('alchemy/elements/mock_view')
429
451
  end
430
452
  end
431
453