landable 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +15 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +56 -0
  7. data/Rakefile +22 -0
  8. data/app/controllers/concerns/landable/variables_concern.rb +38 -0
  9. data/app/controllers/landable/api/access_tokens_controller.rb +48 -0
  10. data/app/controllers/landable/api/assets_controller.rb +55 -0
  11. data/app/controllers/landable/api/categories_controller.rb +13 -0
  12. data/app/controllers/landable/api/directories_controller.rb +19 -0
  13. data/app/controllers/landable/api/page_revisions_controller.rb +38 -0
  14. data/app/controllers/landable/api/pages_controller.rb +96 -0
  15. data/app/controllers/landable/api/templates_controller.rb +33 -0
  16. data/app/controllers/landable/api/themes_controller.rb +57 -0
  17. data/app/controllers/landable/api_controller.rb +75 -0
  18. data/app/controllers/landable/application_controller.rb +4 -0
  19. data/app/controllers/landable/public/pages_controller.rb +25 -0
  20. data/app/controllers/landable/public/preview/page_revisions_controller.rb +16 -0
  21. data/app/controllers/landable/public/preview/pages_controller.rb +16 -0
  22. data/app/controllers/landable/public/sitemap_controller.rb +18 -0
  23. data/app/decorators/landable/null_page_decorator.rb +26 -0
  24. data/app/decorators/landable/page_decorator.rb +40 -0
  25. data/app/helpers/landable/application_helper.rb +13 -0
  26. data/app/helpers/landable/pages_helper.rb +20 -0
  27. data/app/models/concerns/landable/has_assets.rb +74 -0
  28. data/app/models/concerns/landable/table_name.rb +12 -0
  29. data/app/models/concerns/landable/traffic/table_name.rb +14 -0
  30. data/app/models/landable/access_token.rb +20 -0
  31. data/app/models/landable/asset.rb +63 -0
  32. data/app/models/landable/asset_search_engine.rb +20 -0
  33. data/app/models/landable/author.rb +12 -0
  34. data/app/models/landable/category.rb +27 -0
  35. data/app/models/landable/directory.rb +23 -0
  36. data/app/models/landable/page.rb +259 -0
  37. data/app/models/landable/page_revision.rb +74 -0
  38. data/app/models/landable/page_search_engine.rb +20 -0
  39. data/app/models/landable/search_engine.rb +39 -0
  40. data/app/models/landable/template.rb +24 -0
  41. data/app/models/landable/theme.rb +20 -0
  42. data/app/models/landable/traffic/access.rb +11 -0
  43. data/app/models/landable/traffic/ad_group.rb +11 -0
  44. data/app/models/landable/traffic/ad_type.rb +11 -0
  45. data/app/models/landable/traffic/attribution.rb +38 -0
  46. data/app/models/landable/traffic/bid_match_type.rb +11 -0
  47. data/app/models/landable/traffic/browser.rb +11 -0
  48. data/app/models/landable/traffic/campaign.rb +11 -0
  49. data/app/models/landable/traffic/city.rb +11 -0
  50. data/app/models/landable/traffic/content.rb +11 -0
  51. data/app/models/landable/traffic/cookie.rb +16 -0
  52. data/app/models/landable/traffic/country.rb +11 -0
  53. data/app/models/landable/traffic/creative.rb +11 -0
  54. data/app/models/landable/traffic/device.rb +11 -0
  55. data/app/models/landable/traffic/device_type.rb +11 -0
  56. data/app/models/landable/traffic/domain.rb +12 -0
  57. data/app/models/landable/traffic/event.rb +12 -0
  58. data/app/models/landable/traffic/event_type.rb +11 -0
  59. data/app/models/landable/traffic/experiment.rb +11 -0
  60. data/app/models/landable/traffic/http_method.rb +11 -0
  61. data/app/models/landable/traffic/ip_address.rb +11 -0
  62. data/app/models/landable/traffic/ip_lookup.rb +12 -0
  63. data/app/models/landable/traffic/keyword.rb +11 -0
  64. data/app/models/landable/traffic/location.rb +11 -0
  65. data/app/models/landable/traffic/match_type.rb +11 -0
  66. data/app/models/landable/traffic/medium.rb +11 -0
  67. data/app/models/landable/traffic/mime_type.rb +11 -0
  68. data/app/models/landable/traffic/network.rb +11 -0
  69. data/app/models/landable/traffic/owner.rb +10 -0
  70. data/app/models/landable/traffic/ownership.rb +10 -0
  71. data/app/models/landable/traffic/page_view.rb +24 -0
  72. data/app/models/landable/traffic/path.rb +13 -0
  73. data/app/models/landable/traffic/placement.rb +11 -0
  74. data/app/models/landable/traffic/platform.rb +11 -0
  75. data/app/models/landable/traffic/position.rb +11 -0
  76. data/app/models/landable/traffic/query_string.rb +12 -0
  77. data/app/models/landable/traffic/referer.rb +11 -0
  78. data/app/models/landable/traffic/region.rb +11 -0
  79. data/app/models/landable/traffic/search_term.rb +11 -0
  80. data/app/models/landable/traffic/source.rb +11 -0
  81. data/app/models/landable/traffic/target.rb +11 -0
  82. data/app/models/landable/traffic/user_agent.rb +28 -0
  83. data/app/models/landable/traffic/user_agent_type.rb +11 -0
  84. data/app/models/landable/traffic/visit.rb +15 -0
  85. data/app/models/landable/traffic/visitor.rb +13 -0
  86. data/app/responders/landable/api_responder.rb +76 -0
  87. data/app/responders/landable/page_render_responder.rb +15 -0
  88. data/app/serializers/landable/access_token_serializer.rb +6 -0
  89. data/app/serializers/landable/asset_serializer.rb +14 -0
  90. data/app/serializers/landable/author_serializer.rb +5 -0
  91. data/app/serializers/landable/category_serializer.rb +5 -0
  92. data/app/serializers/landable/directory_serializer.rb +8 -0
  93. data/app/serializers/landable/page_revision_serializer.rb +15 -0
  94. data/app/serializers/landable/page_serializer.rb +31 -0
  95. data/app/serializers/landable/template_serializer.rb +5 -0
  96. data/app/serializers/landable/theme_serializer.rb +7 -0
  97. data/app/services/landable/authentication_service.rb +44 -0
  98. data/app/services/landable/registration_service.rb +13 -0
  99. data/app/services/landable/render_service.rb +86 -0
  100. data/app/services/landable/tidy_service.rb +155 -0
  101. data/app/uploaders/landable/asset_uploader.rb +9 -0
  102. data/app/validators/path_validator.rb +12 -0
  103. data/app/validators/url_validator.rb +13 -0
  104. data/app/views/templates/preview.liquid +122 -0
  105. data/bin/rails +8 -0
  106. data/bin/redb +7 -0
  107. data/config.ru +7 -0
  108. data/config/cucumber.yml +5 -0
  109. data/config/routes.rb +62 -0
  110. data/db/migrate/20130510221424_create_landable_schema.rb +338 -0
  111. data/db/migrate/20130909182713_landable_pages__add_updated_by.rb +11 -0
  112. data/db/migrate/20130909182715_landable_page_revisions__break_out_snapshot.rb +72 -0
  113. data/db/migrate/20130909191153_landable_pages__add_lock_version.rb +5 -0
  114. data/db/migrate/20131002220041_file_based_themes.rb +12 -0
  115. data/db/migrate/20131008164204_create_head_tag_on_page.rb +19 -0
  116. data/db/migrate/20131008193544_drop_status_codes_model.rb +44 -0
  117. data/db/migrate/20131028145652_add_traffic_schema.rb +276 -0
  118. data/db/migrate/20131101213623_add_dnt_column_to_visits.rb +7 -0
  119. data/db/migrate/20131104224120_add_meta_on_events.rb +7 -0
  120. data/db/migrate/20131106185946_add_index_on_page_revisions_path.rb +6 -0
  121. data/db/migrate/20131106193021_page_revisisons__path_status_code_index.rb +9 -0
  122. data/db/migrate/20131108212501_traffic_owner_ids_are_serials.rb +20 -0
  123. data/db/migrate/20131121150902_add_attribution_id_to_unique_index.rb +10 -0
  124. data/db/migrate/20131216214027_drop_browser_screenshot_tables.rb +6 -0
  125. data/db/migrate/20140128170659_file_backed_templates.rb +8 -0
  126. data/db/migrate/20140205193757_fix_status_codes.rb +24 -0
  127. data/db/migrate/20140206211322_add_response_time_to_traffic_page_views.rb +7 -0
  128. data/db/migrate/20140220170324_add_slug_to_categories.rb +14 -0
  129. data/db/migrate/20140220174630_add_abstract_and_hero_asset_to_pages_and_page_revisions.rb +8 -0
  130. data/db/migrate/20140224205516_rename_traffic_schema.rb +40 -0
  131. data/db/pgtap/pgtap.sql +9034 -0
  132. data/db/test/landable.access_tokens.sql +13 -0
  133. data/db/test/landable.assets.sql +16 -0
  134. data/db/test/landable.authors.sql +22 -0
  135. data/db/test/landable.categories.sql +9 -0
  136. data/db/test/landable.page_revisions.sql +41 -0
  137. data/db/test/landable.pages.sql +19 -0
  138. data/db/test/landable.templates.sql +15 -0
  139. data/db/test/landable.themes.sql +25 -0
  140. data/doc/schema/access_token.json +22 -0
  141. data/doc/schema/asset.json +65 -0
  142. data/doc/schema/author.json +30 -0
  143. data/doc/schema/directory.json +24 -0
  144. data/doc/schema/page.json +95 -0
  145. data/doc/schema/page_revision.json +70 -0
  146. data/doc/schema/theme.json +37 -0
  147. data/doc/schema/uuid.json +6 -0
  148. data/features/api/access_tokens.feature +84 -0
  149. data/features/api/assets.feature +46 -0
  150. data/features/api/cors.feature +25 -0
  151. data/features/api/pages.feature +42 -0
  152. data/features/api/preview.feature +16 -0
  153. data/features/api/templates.feature +33 -0
  154. data/features/api/themes.feature +33 -0
  155. data/features/liquid/body.feature +35 -0
  156. data/features/liquid/drops/categories.feature +54 -0
  157. data/features/liquid/tags.feature +168 -0
  158. data/features/public/content_types.feature +17 -0
  159. data/features/public/publishing.feature +45 -0
  160. data/features/public/status_codes.feature +25 -0
  161. data/features/public/views.feature +17 -0
  162. data/features/step_definitions/asset_steps.rb +60 -0
  163. data/features/step_definitions/core_api_steps.rb +139 -0
  164. data/features/step_definitions/debug_steps.rb +3 -0
  165. data/features/step_definitions/factory_steps.rb +124 -0
  166. data/features/step_definitions/html_steps.rb +9 -0
  167. data/features/step_definitions/liquid_steps.rb +79 -0
  168. data/features/step_definitions/revision_steps.rb +5 -0
  169. data/features/step_definitions/theme_steps.rb +43 -0
  170. data/features/support/env.rb +66 -0
  171. data/features/support/usefulness.rb +13 -0
  172. data/landable.gemspec +54 -0
  173. data/lib/generators/landable/collection/collection_generator.rb +0 -0
  174. data/lib/generators/landable/collection/templates/stylesheets/landable/%file_name%.less +0 -0
  175. data/lib/generators/landable/collection/templates/stylesheets/landable/%file_name%/mixins.less +0 -0
  176. data/lib/generators/landable/collection/templates/stylesheets/landable/%file_name%/variables.less +0 -0
  177. data/lib/generators/landable/component/component_generator.rb +0 -0
  178. data/lib/generators/landable/component/templates/javascripts/landable/%file_name%.less +0 -0
  179. data/lib/generators/landable/component/templates/stylesheets/landable/%file_name%.less +0 -0
  180. data/lib/generators/landable/install_generator.rb +19 -0
  181. data/lib/generators/landable/landable_generator.rb +22 -0
  182. data/lib/generators/templates/landable.rb +34 -0
  183. data/lib/landable.rb +30 -0
  184. data/lib/landable/configuration.rb +115 -0
  185. data/lib/landable/core_ext/ipaddr.rb +18 -0
  186. data/lib/landable/engine.rb +69 -0
  187. data/lib/landable/error.rb +16 -0
  188. data/lib/landable/inflections.rb +4 -0
  189. data/lib/landable/layout.rb +60 -0
  190. data/lib/landable/liquid.rb +27 -0
  191. data/lib/landable/liquid/asset_tags.rb +76 -0
  192. data/lib/landable/liquid/drops.rb +46 -0
  193. data/lib/landable/liquid/filters.rb +11 -0
  194. data/lib/landable/liquid/tags.rb +91 -0
  195. data/lib/landable/migration.rb +40 -0
  196. data/lib/landable/mime_types.rb +15 -0
  197. data/lib/landable/partial.rb +46 -0
  198. data/lib/landable/seeds.rb +36 -0
  199. data/lib/landable/traffic.rb +34 -0
  200. data/lib/landable/traffic/crawl_tracker.rb +9 -0
  201. data/lib/landable/traffic/noop_tracker.rb +8 -0
  202. data/lib/landable/traffic/ping_tracker.rb +9 -0
  203. data/lib/landable/traffic/scan_tracker.rb +9 -0
  204. data/lib/landable/traffic/scrape_tracker.rb +9 -0
  205. data/lib/landable/traffic/tracker.rb +283 -0
  206. data/lib/landable/traffic/user_tracker.rb +65 -0
  207. data/lib/landable/version.rb +10 -0
  208. data/lib/tasks/landable/cucumber.rake +67 -0
  209. data/lib/tasks/landable/data.rake +166 -0
  210. data/lib/tasks/landable/pgtap.rake +26 -0
  211. data/lib/tasks/landable/rdoc.rake +11 -0
  212. data/lib/tasks/landable/seed.rake +16 -0
  213. data/lib/tasks/landable/spec.rake +15 -0
  214. data/script/cucumber +10 -0
  215. data/spec/concerns/landable/has_assets_spec.rb +75 -0
  216. data/spec/concerns/landable/table_name_spec.rb +15 -0
  217. data/spec/concerns/landable/traffic/table_name_spec.rb +16 -0
  218. data/spec/controllers/concerns/landable/variables_concern_spec.rb +66 -0
  219. data/spec/controllers/landable/api/assets_controller_spec.rb +24 -0
  220. data/spec/controllers/landable/api/categories_controller_spec.rb +45 -0
  221. data/spec/controllers/landable/api/directories_controller_spec.rb +56 -0
  222. data/spec/controllers/landable/api/page_revisions_controller_spec.rb +29 -0
  223. data/spec/controllers/landable/api/pages_controller_spec.rb +271 -0
  224. data/spec/controllers/landable/api_controller_spec.rb +189 -0
  225. data/spec/controllers/public/preview/page_revisions_controller_spec.rb +41 -0
  226. data/spec/controllers/public/preview/pages_controller_spec.rb +36 -0
  227. data/spec/controllers/public/sitemap_controller_spec.rb +25 -0
  228. data/spec/decorators/page_decorator_spec.rb +90 -0
  229. data/spec/dummy/README.rdoc +28 -0
  230. data/spec/dummy/Rakefile +6 -0
  231. data/spec/dummy/app/assets/images/foo.jpg +0 -0
  232. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  233. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  234. data/spec/dummy/app/controllers/application_controller.rb +10 -0
  235. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  236. data/spec/dummy/app/controllers/priority_controller.rb +7 -0
  237. data/spec/dummy/app/helpers/application_helper.rb +5 -0
  238. data/spec/dummy/app/mailers/.keep +0 -0
  239. data/spec/dummy/app/models/.keep +0 -0
  240. data/spec/dummy/app/models/concerns/.keep +0 -0
  241. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  242. data/spec/dummy/app/views/layouts/priority.html.erb +18 -0
  243. data/spec/dummy/app/views/partials/_foobazz.html +1 -0
  244. data/spec/dummy/app/views/priority/show.html.erb +11 -0
  245. data/spec/dummy/bin/bundle +3 -0
  246. data/spec/dummy/bin/rails +4 -0
  247. data/spec/dummy/bin/rake +4 -0
  248. data/spec/dummy/config.ru +4 -0
  249. data/spec/dummy/config/application.rb +21 -0
  250. data/spec/dummy/config/boot.rb +5 -0
  251. data/spec/dummy/config/database.yml +60 -0
  252. data/spec/dummy/config/environment.rb +5 -0
  253. data/spec/dummy/config/environments/development.rb +29 -0
  254. data/spec/dummy/config/environments/production.rb +80 -0
  255. data/spec/dummy/config/environments/test.rb +42 -0
  256. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  257. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  258. data/spec/dummy/config/initializers/inflections.rb +16 -0
  259. data/spec/dummy/config/initializers/landable.rb +22 -0
  260. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  261. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  262. data/spec/dummy/config/initializers/session_store.rb +3 -0
  263. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  264. data/spec/dummy/config/locales/en.yml +23 -0
  265. data/spec/dummy/config/routes.rb +4 -0
  266. data/spec/dummy/db/structure.sql +3736 -0
  267. data/spec/dummy/lib/assets/.keep +0 -0
  268. data/spec/dummy/log/.keep +0 -0
  269. data/spec/dummy/public/404.html +58 -0
  270. data/spec/dummy/public/422.html +58 -0
  271. data/spec/dummy/public/500.html +57 -0
  272. data/spec/dummy/public/favicon.ico +0 -0
  273. data/spec/factories/asset.rb +29 -0
  274. data/spec/factories/authors.rb +12 -0
  275. data/spec/factories/category.rb +6 -0
  276. data/spec/factories/page_revision.rb +6 -0
  277. data/spec/factories/pages.rb +33 -0
  278. data/spec/factories/template.rb +13 -0
  279. data/spec/factories/theme.rb +14 -0
  280. data/spec/fixtures/assets/cthulhu.jpg +0 -0
  281. data/spec/fixtures/assets/panda.png +0 -0
  282. data/spec/fixtures/assets/sloth.png +0 -0
  283. data/spec/fixtures/assets/small.pdf +0 -0
  284. data/spec/helpers/pages_helper_spec.rb +35 -0
  285. data/spec/lib/landable/configuration_spec.rb +20 -0
  286. data/spec/lib/landable/layout_spec.rb +25 -0
  287. data/spec/lib/landable/liquid_spec.rb +24 -0
  288. data/spec/lib/landable/migration_spec.rb +51 -0
  289. data/spec/lib/landable/partial_spec.rb +84 -0
  290. data/spec/lib/landable/tracking_spec.rb +62 -0
  291. data/spec/lib/landable/traffic_spec.rb +45 -0
  292. data/spec/models/landable/access_token_spec.rb +13 -0
  293. data/spec/models/landable/asset_spec.rb +48 -0
  294. data/spec/models/landable/directory_spec.rb +36 -0
  295. data/spec/models/landable/page/errors_spec.rb +30 -0
  296. data/spec/models/landable/page_revision_spec.rb +75 -0
  297. data/spec/models/landable/page_spec.rb +377 -0
  298. data/spec/models/landable/template_spec.rb +47 -0
  299. data/spec/models/landable/theme_spec.rb +8 -0
  300. data/spec/responders/page_render_responder_spec.rb +43 -0
  301. data/spec/routing/public_page_route_spec.rb +36 -0
  302. data/spec/services/landable/authentication_service_spec.rb +61 -0
  303. data/spec/services/landable/render_service_spec.rb +103 -0
  304. data/spec/services/landable/tidy_service_spec.rb +157 -0
  305. data/spec/spec_helper.rb +38 -0
  306. data/spec/support/behaviors.rb +107 -0
  307. data/spec/support/carrier_wave.rb +17 -0
  308. data/spec/support/categories.yml +2 -0
  309. data/spec/support/helpers.rb +22 -0
  310. metadata +795 -0
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe Partial do
5
+ # Defined in spec/dummy/app/views/partials/...
6
+ let(:partials) { ['partials/test', 'partials/foobazz'] }
7
+
8
+ describe '#to_template' do
9
+ context 'with configured partials' do
10
+ before :each do
11
+ Landable.configuration.stub(:partials_to_templates).and_return(partials)
12
+ Partial.all.map(&:to_template)
13
+
14
+ @foobazz = Landable::Template.where(file: 'partials/foobazz').first
15
+ @test = Landable::Template.where(file: 'partials/test').first
16
+ end
17
+
18
+ it 'creates the templates' do
19
+ Landable::Template.all.should include(@foobazz)
20
+ Landable::Template.all.should include(@test)
21
+ end
22
+
23
+ context 'the templates' do
24
+ it 'populates a name by humanizing the file' do
25
+ @foobazz.name.should == 'Partials Foobazz'
26
+ @test.name.should == 'Partials Test'
27
+ end
28
+
29
+ it 'populates a description' do
30
+ @foobazz.description.should == 'Defined in Source Code at partials/foobazz'
31
+ @test.description.should == 'Defined in Source Code at partials/test'
32
+ end
33
+
34
+ it 'are not editable' do
35
+ @foobazz.editable.should == false
36
+ @test.editable.should == false
37
+ end
38
+
39
+ it 'are not layouts' do
40
+ @foobazz.is_layout.should == false
41
+ @test.is_layout.should == false
42
+ end
43
+
44
+ it 'popules a thumbnail_url' do
45
+ @foobazz.thumbnail_url.should == 'http://placehold.it/300x200'
46
+ @test.thumbnail_url.should == 'http://placehold.it/300x200'
47
+ end
48
+
49
+ it 'populates a body' do
50
+ @foobazz.body.should == ''
51
+ @test.body.should == ''
52
+ end
53
+
54
+ it 'references the flle path' do
55
+ @foobazz.file.should == 'partials/foobazz'
56
+ @test.file.should == 'partials/test'
57
+ end
58
+
59
+ it 'creates a slug by underscoring the name' do
60
+ @test.slug.should == 'partials_test'
61
+ @foobazz.slug.should == 'partials_foobazz'
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '::files' do
68
+ it 'returns an array of files' do
69
+ Landable.configuration.stub(:partials_to_templates).and_return(partials)
70
+
71
+ Partial.files.count.should == 2
72
+ Partial.files.should include('partials/test', 'partials/foobazz')
73
+ end
74
+
75
+ context 'with no configured partials' do
76
+ it 'has no files' do
77
+ Landable.configuration.stub(:partials_to_templates).and_return([])
78
+
79
+ Partial.files.should == []
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe Landable::Traffic::Tracker do
5
+ let(:referer) { "/something/ valid" }
6
+ let(:user_agent) { "type" }
7
+ let(:format) { double('format', { html?: true}) }
8
+ let(:request) { double('request', { query_parameters: {}, user_agent: user_agent, referer: referer, format: format }) }
9
+ let(:controller) { double('controller', { request: request }) }
10
+
11
+ describe "#for" do
12
+ it 'should default to UserTracker if user_agent does not exist' do
13
+ Landable::Traffic::Tracker.for(controller).should be_a(Landable::Traffic::UserTracker)
14
+ end
15
+
16
+ it 'should create the appropriate type of tracker based on user_agent' do
17
+ type = double('type', { user_agent_type: "Scan" })
18
+ fake_agent = {}
19
+ fake_agent["type"] = type
20
+ stub_const("Landable::Traffic::UserAgent", fake_agent)
21
+
22
+ Landable::Traffic::Tracker.for(controller).should be_a(Landable::Traffic::ScanTracker)
23
+ end
24
+
25
+ it 'should not bark if user_agent is nil' do
26
+ user_agent = nil
27
+ request = double('request', { query_parameters: {}, user_agent: user_agent, format: format })
28
+ controller = double('controller', { request: request })
29
+
30
+ Landable::Traffic::Tracker.for(controller).should be_a(Landable::Traffic::UserTracker)
31
+ end
32
+
33
+ it 'should set type to noop when non-html content' do
34
+ user_agent = nil
35
+ Landable.configuration.stub(:traffic_enabled).and_return(:html)
36
+ format = double('format', { html?: false })
37
+ request = double('request', { query_parameters: {}, user_agent: user_agent, format: format })
38
+ controller = double('controller', { request: request })
39
+
40
+ Landable::Traffic::Tracker.for(controller).should be_a(Landable::Traffic::NoopTracker)
41
+ end
42
+
43
+ it 'should allow non-html content if config says so' do
44
+ format = double('format', { html?: false})
45
+ request = double('request', { query_parameters: {}, user_agent: user_agent, format: format })
46
+ controller = double('controller', { request: request })
47
+
48
+ Landable::Traffic::Tracker.for(controller).should be_a(Landable::Traffic::UserTracker)
49
+ end
50
+ end
51
+
52
+ describe '#referer_uri' do
53
+ it 'should encode special characters' do
54
+ tracker = Landable::Traffic::UserTracker.new controller
55
+
56
+ tracker.send(:referer_uri).path.should == "/something/%20valid"
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+
5
+ class TrackError < StandardError
6
+ end
7
+
8
+ class SaveError < StandardError
9
+ end
10
+
11
+ describe Traffic, type: :controller do
12
+
13
+ controller(ApplicationController) do
14
+ include Landable::Traffic
15
+ prepend_around_action :track_with_landable!
16
+
17
+ def my_method
18
+ render nothing: true
19
+ end
20
+ end
21
+
22
+ before do
23
+ routes.draw do
24
+ get 'my_method' => 'anonymous#my_method'
25
+ end
26
+ end
27
+
28
+ describe 'track_with_landable!' do
29
+ it 'should log errors' do
30
+ tracker = double('tracker')
31
+
32
+ Landable::Traffic::Tracker.stub(:for).and_return(tracker)
33
+ tracker.stub(:track).and_raise(TrackError)
34
+ tracker.stub(:save).and_raise(SaveError)
35
+
36
+ controller.should_receive(:newrelic_notice_error) { |error| error.should be_an_instance_of TrackError }
37
+ controller.should_receive(:newrelic_notice_error) { |error| error.should be_an_instance_of SaveError }
38
+
39
+ get :my_method
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe AccessToken do
5
+ it { should_not have_valid(:author_id).when(nil) }
6
+
7
+ it "generates an expiration timestamp before creation" do
8
+ author = create :author
9
+ token = AccessToken.create!(author: author)
10
+ expect(token.expires_at).not_to be_nil
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Landable::Asset do
4
+ def asset_fixture(name)
5
+ File.expand_path("../../../fixtures/assets/#{name}", __FILE__)
6
+ end
7
+
8
+ let(:png) { File.open(asset_fixture('panda.png')) }
9
+ let(:pdf) { File.open(asset_fixture('small.pdf')) }
10
+
11
+ after do
12
+ png.close
13
+ pdf.close
14
+ end
15
+
16
+ it { should_not have_valid(:name).when(nil, '', 'No Spaces') }
17
+ it { should_not have_valid(:author).when(nil) }
18
+ it { should_not have_valid(:md5sum).when(nil, '') }
19
+ it { should_not have_valid(:mime_type).when(nil, '') }
20
+ it { should_not have_valid(:file_size).when(nil, 1.5) }
21
+
22
+ it "stores an md5sum of its contents" do
23
+ asset = build(:asset, data: png)
24
+ expect {
25
+ asset.valid?
26
+ }.to change { asset.md5sum }.from(nil).to('0f62ef551dbebcdb7379401528b6115c')
27
+ end
28
+
29
+ it "requires the md5sum to be unique" do
30
+ orig = create(:asset, data: png)
31
+ dupe = build(:asset, data: png)
32
+
33
+ expect(dupe).not_to be_valid
34
+ expect(dupe.errors_on(:md5sum)).to eql(['has already been taken'])
35
+ end
36
+
37
+ it "provides access to the other asset with the same contents" do
38
+ orig = create(:asset, data: png)
39
+ dupe = build(:asset, data: png)
40
+ expect(dupe.duplicate_of).to eql(orig)
41
+ end
42
+
43
+ it "returns a list of pages using the asset" do
44
+ page = create(:page, body: "panda.png", path: "/testing/assets")
45
+ asset = create(:asset, data:pdf, name: "panda.png")
46
+ asset.associated_pages.should eq(["/testing/assets"])
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe Directory, '.listing' do
5
+ def stub_contents
6
+ ['/seo/foo', '/aff/bar', '/seo/baz', '/aff/deeply/nested', '/aff/deeply_nested', '/quux'].each do |path|
7
+ create :page, path: path
8
+ end
9
+ end
10
+
11
+ it 'knows its own path' do
12
+ dir = Directory.listing '/'
13
+ dir.path.should == '/'
14
+ end
15
+
16
+ it 'lists immediate children' do
17
+ stub_contents
18
+
19
+ dir = Directory.listing '/'
20
+ dir.subdirectories.map(&:path).should == ['/aff', '/seo']
21
+ dir.pages.map(&:path).should == ['/quux']
22
+
23
+ dir = Directory.listing '/aff'
24
+ dir.subdirectories.map(&:path).should == ['/aff/deeply']
25
+ dir.pages.map(&:path).should == ['/aff/bar', '/aff/deeply_nested']
26
+
27
+ dir = Directory.listing '/seo'
28
+ dir.subdirectories.should be_empty
29
+ dir.pages.map(&:path).should == ['/seo/baz', '/seo/foo']
30
+
31
+ dir = Directory.listing '/aff/deeply'
32
+ dir.subdirectories.should be_empty
33
+ dir.pages.map(&:path).should == ['/aff/deeply/nested']
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe Page do
5
+
6
+ describe '#error?' do
7
+ describe 'yep' do
8
+ specify { build(:page, status_code: 418).should be_error }
9
+ specify { build(:page, status_code: 522).should be_error }
10
+ end
11
+
12
+ describe 'nope' do
13
+ specify { build(:page, status_code: 311).should_not be_error }
14
+ specify { build(:page, status_code: 200).should_not be_error }
15
+ end
16
+ end
17
+
18
+ describe '#error' do
19
+ def error_for code
20
+ build(:page, status_code: code).error
21
+ end
22
+
23
+ specify { error_for(410).should be_a Landable::Page::GoneError }
24
+ specify { error_for(555).should be_a Landable::Error }
25
+ specify { error_for(200).should be_nil }
26
+ specify { error_for(302).should be_nil }
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe PageRevision do
5
+ let(:author) { create(:author) }
6
+ let(:asset) { create(:asset) }
7
+
8
+ let(:page) do
9
+ create(:page, path: '/test/path', title: 'title', status_code: 200,
10
+ body: 'body', redirect_url: 'http://www.redirect.com/here',
11
+ meta_tags: {'key'=>'value'}, head_content: 'head_content')
12
+ end
13
+
14
+ let(:revision) do
15
+ PageRevision.new page_id: page.id, author_id: author.id
16
+ end
17
+
18
+ it { should be_a HasAssets }
19
+
20
+ it 'defaults to is_published = true' do
21
+ PageRevision.new.is_published.should == true
22
+ end
23
+
24
+ describe '#page_id=' do
25
+ it 'should set page revision attributes matching the page' do
26
+ attrs = revision.attributes.except('page_revision_id','ordinal','notes','is_minor','is_published','author_id','created_at','updated_at', 'page_id')
27
+ attrs.should include(page.attributes.except(*PageRevision.ignored_page_attributes))
28
+ end
29
+ end
30
+
31
+ describe '#snapshot' do
32
+ it 'should build a page based on the cached page attributes' do
33
+ snapshot = revision.snapshot
34
+ snapshot.should be_new_record
35
+ snapshot.should be_an_instance_of Page
36
+ snapshot.title.should == page.title
37
+ snapshot.path.should == page.path
38
+ snapshot.head_content.should == page.head_content
39
+ snapshot.meta_tags.should == page.meta_tags
40
+ snapshot.body.should == page.body
41
+ snapshot.redirect_url.should == page.redirect_url
42
+ snapshot.category_id.should == page.category_id
43
+ snapshot.theme_id.should == page.theme_id
44
+ snapshot.status_code.should == page.status_code
45
+ end
46
+ end
47
+
48
+ describe '#is_published' do
49
+ it 'should set is_published to true and false as requested' do
50
+ revision = PageRevision.new
51
+ revision.page_id = page.id
52
+ revision.author_id = author.id
53
+ revision.unpublish!
54
+ revision.is_published.should == false
55
+ revision.publish!
56
+ revision.is_published.should == true
57
+ end
58
+ end
59
+
60
+ describe '#preview_path' do
61
+ it 'should return the preview path' do
62
+ revision.should_receive(:public_preview_page_revision_path) { 'foo' }
63
+ revision.preview_path.should == 'foo'
64
+ end
65
+ end
66
+
67
+ describe '#preview_url' do
68
+ it 'should return the preview url' do
69
+ revision.should_receive(:public_preview_page_revision_url) { 'foo' }
70
+ revision.preview_url.should == 'foo'
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,377 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe Page do
5
+ it { should be_a HasAssets }
6
+ it { should_not have_valid(:status_code).when(nil,'') }
7
+ it { should have_valid(:status_code).when(200, 301, 302, 410) }
8
+ it { should_not have_valid(:status_code).when(201, 303, 405, 500, 404) }
9
+
10
+ # config.reserved_paths = %w(/reserved_path_set_in_initializer /reject/.* /admin.*)
11
+ context 'PathValidator' do
12
+ it { should_not have_valid(:path).when(nil, '', '/reserved_path_set_in_initializer') }
13
+ it { should_not have_valid(:path).when('/reject/this', '/admin', '/ADMIN', '/admin_something' '/admin/path') }
14
+ it { should have_valid(:path).when('/reserved_path_set_in_initializer_not', '/do/not/reject/path', '/', '/rejectwhatever', '/reject') }
15
+ end
16
+
17
+ it 'should set is_publishable to true on before_save' do
18
+ page = FactoryGirl.build :page, is_publishable: false
19
+ page.save!
20
+ page.is_publishable.should be_true
21
+ end
22
+
23
+ specify "#redirect?" do
24
+ Page.new.should_not be_redirect
25
+ Page.new().should_not be_redirect
26
+ Page.new(status_code: 200).should_not be_redirect
27
+ Page.new(status_code: 410).should_not be_redirect
28
+
29
+ Page.new(status_code: 301).should be_redirect
30
+ Page.new(status_code: 302).should be_redirect
31
+ end
32
+
33
+ describe '#published?' do
34
+ context 'when published' do
35
+ it 'should be true' do
36
+ page = create :page
37
+ page.publish! author: create(:author), notes: 'yo'
38
+ page.should be_published
39
+ end
40
+ end
41
+
42
+ context 'when not published' do
43
+ it 'should be false' do
44
+ page = create :page
45
+ page.should_not be_published
46
+ end
47
+ end
48
+ end
49
+
50
+ specify '#path_extension' do
51
+ Page.new(path: 'foo').path_extension.should be_nil
52
+ Page.new(path: 'foo.bar').path_extension.should == 'bar'
53
+ Page.new(path: 'foo.bar.baz').path_extension.should == 'baz'
54
+ Page.new(path: 'foo.bar-baz').path_extension.should be_nil
55
+ end
56
+
57
+ describe '#content_type' do
58
+ def content_type_for path
59
+ Page.new(path: path).content_type
60
+ end
61
+
62
+ it 'should be text/html for html pages' do
63
+ content_type_for('asdf').should == 'text/html'
64
+ content_type_for('asdf.htm').should == 'text/html'
65
+ content_type_for('asdf.html').should == 'text/html'
66
+ end
67
+
68
+ it 'should be application/json for json' do
69
+ content_type_for('asdf.json').should == 'application/json'
70
+ end
71
+
72
+ it 'should be application/xml for xml' do
73
+ content_type_for('asdf.xml').should == 'application/xml'
74
+ end
75
+
76
+ it 'should be text/plain for everything else' do
77
+ content_type_for('foo.bar').should == 'text/plain'
78
+ content_type_for('foo.txt').should == 'text/plain'
79
+ end
80
+ end
81
+
82
+ describe '#html?' do
83
+ let(:page) { build :page }
84
+
85
+ it 'should be true if content_type is text/html' do
86
+ page.should_receive(:content_type) { 'text/html' }
87
+ page.should be_html
88
+ end
89
+
90
+ it 'should be false if content_type is not text/html' do
91
+ page.should_receive(:content_type) { 'text/plain' }
92
+ page.should_not be_html
93
+ end
94
+ end
95
+
96
+ describe '#redirect_url' do
97
+ it 'is required if redirect?' do
98
+ page = Page.new status_code: 301
99
+ page.should_not have_valid(:redirect_url).when(nil, '')
100
+ page.should have_valid(:redirect_url).when('http://example.com', 'http://www.somepath.com')
101
+ end
102
+
103
+ it 'not required for 200, 410' do
104
+ page = Page.new
105
+ page.should have_valid(:redirect_url).when(nil, '')
106
+ end
107
+ end
108
+
109
+ describe '#meta_tags' do
110
+ it { subject.should have_valid(:meta_tags).when(nil) }
111
+
112
+ specify "quacks like a Hash" do
113
+ # Note the change from symbol to string; thus, always favor strings.
114
+ page = create :page, meta_tags: { keywords: 'foo' }
115
+ page.meta_tags.keys.should == [:keywords]
116
+
117
+ tags = Page.first.meta_tags
118
+ tags.should be_a(Enumerable)
119
+ tags.keys.should == ['keywords']
120
+ tags.values.should == ['foo']
121
+ end
122
+ end
123
+
124
+ describe '#head_content' do
125
+ it { subject.should have_valid(:meta_tags).when(nil) }
126
+
127
+ it 'works as a basic text area' do
128
+ page = create :page, head_content: "<head en='en'/>"
129
+ page.head_content.should == "<head en='en'/>"
130
+
131
+ page.head_content = "<head en='magic'/>"
132
+ page.save
133
+
134
+ page.head_content.should == "<head en='magic'/>"
135
+ end
136
+ end
137
+
138
+ describe '#path=' do
139
+ it 'ensures a leading "/" on path' do
140
+ Page.new(path: 'foo/bar').path.should == '/foo/bar'
141
+ end
142
+
143
+ it 'leaves nil and empty paths alone' do
144
+ Page.new(path: '').path.should == ''
145
+ Page.new(path: nil).path.should == nil
146
+ end
147
+ end
148
+
149
+ describe '#publish' do
150
+ let(:page) { FactoryGirl.create :page }
151
+ let(:author) { FactoryGirl.create :author }
152
+
153
+ it 'should create a page_revision' do
154
+ expect {page.publish!(author: author)}.to change{page.revisions.count}.from(0).to(1)
155
+ end
156
+
157
+ it 'should have the provided author' do
158
+ page.publish! author: author
159
+ revision = page.revisions.last
160
+
161
+ revision.author.should == author
162
+ end
163
+
164
+ it 'should update the published_revision_id' do
165
+ page.publish! author: author
166
+ revision = page.revisions.last
167
+
168
+ page.published_revision.should == revision
169
+ end
170
+
171
+ it 'should set is_publishable to false' do
172
+ page.is_publishable = true
173
+ page.publish! author: author
174
+ page.is_publishable.should be_false
175
+ end
176
+
177
+ it 'should unset previous revision.is_published' do
178
+ page.publish! author: author
179
+ revision1 = page.published_revision
180
+ page.publish! author: author
181
+ revision1.is_published.should be_false
182
+ end
183
+ end
184
+
185
+ describe '#revert_to' do
186
+ let(:page) { FactoryGirl.create :page }
187
+ let(:author) { FactoryGirl.create :author }
188
+
189
+ it 'should NOT update published_revision for the page' do
190
+ page.title = 'Bar'
191
+ page.publish! author: author
192
+ revision = page.published_revision
193
+
194
+ page.title = 'Foo'
195
+ page.publish! author: author
196
+
197
+ page.revert_to! revision
198
+
199
+ page.published_revision.id.should_not == revision.id
200
+ end
201
+
202
+ it 'should copy revision attributes into the page model' do
203
+ page.title = 'Bar'
204
+ page.publish! author: author
205
+
206
+ revision = page.published_revision
207
+
208
+ page.title = 'Foo'
209
+ page.save!
210
+ page.publish! author: author
211
+
212
+ # ensure assignment for all copied attributes
213
+ keys = %w(title path body category_id theme_id status_code meta_tags redirect_url)
214
+ keys.each do |key|
215
+ page.should_receive("#{key}=").with(revision.send(key))
216
+ end
217
+
218
+ page.revert_to! revision
219
+ end
220
+ end
221
+
222
+ describe '#forbid_changing_path' do
223
+ context 'created_record' do
224
+ it 'does not allow a path to be changed' do
225
+ page = create :page, path: '/test'
226
+ page.path = '/different'
227
+ expect { page.save! }.to raise_error
228
+
229
+ page.reload
230
+ page.path.should == '/test'
231
+ end
232
+ end
233
+
234
+ context 'new_record' do
235
+ it 'allows the path to be changed' do
236
+ page = build :page, path: '/test'
237
+ page.save!
238
+
239
+ page.path.should == '/test'
240
+ end
241
+ end
242
+ end
243
+
244
+ describe '#preview_path' do
245
+ it 'should return the preview path' do
246
+ page = build :page
247
+ page.should_receive(:public_preview_page_path) { 'foo' }
248
+ page.preview_path.should == 'foo'
249
+ end
250
+ end
251
+
252
+ describe '#preview_url' do
253
+ it 'should return the preview url' do
254
+ page = build :page
255
+ page.should_receive(:public_preview_page_url) { 'foo' }
256
+ page.preview_url.should == 'foo'
257
+ end
258
+ end
259
+
260
+ describe '::sitemappable' do
261
+ let(:page) { create :page }
262
+ let(:page_2) { create :page, :redirect }
263
+ let(:page_3) { create :page, meta_tags: { 'robots' => 'noindex' } }
264
+
265
+ it 'only returns pages with a status code of 200 and dont have a noindex tag' do
266
+ page_2.status_code.should == 301
267
+
268
+ Landable::Page.sitemappable.should include(page)
269
+ Landable::Page.sitemappable.should_not include(page_2, page_3)
270
+ end
271
+ end
272
+
273
+ describe '#downcase_path' do
274
+ it 'should force a path to be lowercase' do
275
+ page = build :page, path: '/SEO'
276
+ page.should be_valid
277
+ page.path.should == '/seo'
278
+ end
279
+
280
+ it 'doesnt change a downcase path' do
281
+ page = build :page, path: '/seo'
282
+ page.should be_valid
283
+ page.path.should == '/seo'
284
+ end
285
+ end
286
+
287
+ describe '#redirect_url' do
288
+ context 'validater' do
289
+ before(:each) { @page = build :page, path: '/' }
290
+
291
+ it 'should correctly validate http://' do
292
+ @page.redirect_url = 'http://www.google.com'
293
+ @page.should be_valid
294
+ end
295
+
296
+ it 'should correctly validate https://' do
297
+ @page.redirect_url = 'http://www.google.com'
298
+ @page.should be_valid
299
+ end
300
+
301
+ it 'should correctly validate /' do
302
+ @page.redirect_url = '/some/uri'
303
+ @page.should be_valid
304
+ end
305
+
306
+ it 'should not validate www' do
307
+ @page.redirect_url = 'www.google.com'
308
+ @page.should_not be_valid
309
+ end
310
+
311
+ it 'should not validate bad urls' do
312
+ @page.redirect_url = 'hdasdfpou'
313
+ @page.should_not be_valid
314
+ end
315
+
316
+ end
317
+ end
318
+
319
+ describe '::generate_sitemap' do
320
+ it 'returns a sitemap' do
321
+ page = create :page
322
+ Landable::Page.generate_sitemap(host: 'example.com',
323
+ protocol: 'http',
324
+ exclude_categories: [],
325
+ sitemap_additional_paths: []).should include("<loc>http://example.com#{page.path}</loc>")
326
+ end
327
+
328
+ it 'does not include excluded categories' do
329
+ cat = create :category, name: 'Testing'
330
+ page = create :page, category: cat
331
+ Landable::Page.generate_sitemap(host: 'example.com',
332
+ protocol: 'http',
333
+ exclude_categories: ['Testing'],
334
+ sitemap_additional_paths: []).should_not include("<loc>http://example.com#{page.path}</loc>")
335
+ end
336
+
337
+ it 'can handle https protocol' do
338
+ page = create :page
339
+ Landable::Page.generate_sitemap(host: 'example.com',
340
+ protocol: 'https',
341
+ exclude_categories: [],
342
+ sitemap_additional_paths: []).should include("<loc>https://example.com#{page.path}</loc>")
343
+ end
344
+
345
+ it 'can handle additional pages' do
346
+ Landable::Page.generate_sitemap(host: 'example.com',
347
+ protocol: 'https',
348
+ exclude_categories: [],
349
+ sitemap_additional_paths: ['/terms.html']).should include("<loc>https://example.com/terms.html</loc>")
350
+ end
351
+ end
352
+
353
+ describe '::by_path' do
354
+ it 'returns first page with path name' do
355
+ page = create :page, path: '/seo'
356
+ Landable::Page.by_path('/seo').should == page
357
+ end
358
+ end
359
+
360
+ describe 'validate#body_strip_search' do
361
+ it 'raises errors if errors!' do
362
+ page = build :page, path: '/'
363
+ page.body = "{% image_tag 'bad_image' %}"
364
+ page.should_not be_valid
365
+ page.errors[:body].should_not be_empty
366
+ end
367
+
368
+ it 'does not raise error when no syntax error' do
369
+ page = build :page, path: '/'
370
+ page.body = 'body'
371
+ page.should be_valid
372
+ page.save!
373
+ page.body.should == 'body'
374
+ end
375
+ end
376
+ end
377
+ end