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,47 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe Template do
5
+ describe 'validators' do
6
+ # some valid seed data
7
+ before(:each) { create :template }
8
+
9
+ it { should validate_presence_of :name }
10
+ it { should validate_presence_of :description }
11
+ it { should validate_presence_of :slug }
12
+ it { should validate_uniqueness_of :slug }
13
+ end
14
+
15
+ describe '#name=' do
16
+ context 'without a slug' do
17
+ it 'should assign a slug' do
18
+ template = build(:template, slug: nil)
19
+ template.name = 'Six Seven'
20
+ template.name.should == 'Six Seven'
21
+ template.slug.should == 'six_seven'
22
+ end
23
+ end
24
+
25
+ context 'with a slug' do
26
+ it 'should leave the slug alone' do
27
+ template = build(:template, slug: 'six')
28
+ template.name = 'seven'
29
+ template.name.should == 'seven'
30
+ template.slug.should == 'six'
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#partial?' do
36
+ it 'returns true when template references a file' do
37
+ template = create :template, :partial
38
+ template.partial?.should be_true
39
+ end
40
+
41
+ it 'returns false when template has no file' do
42
+ template = create :template
43
+ template.partial?.should be_false
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe Theme do
5
+ it { should have_valid(:thumbnail_url).when(nil) }
6
+ it { should be_a HasAssets }
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Landable::PageRenderResponder do
4
+
5
+ let(:page) { build :page }
6
+ let(:responder) { Landable::PageRenderResponder.new double(request: double, formats: []), [page] }
7
+
8
+ describe '#to_html' do
9
+ context 'okay' do
10
+ it 'should render a 200' do
11
+ content = double
12
+ content_type = double
13
+ Landable::RenderService.should_receive(:call) { content }
14
+ page.should_receive(:content_type) { content_type }
15
+
16
+ responder.should_receive(:render).with(text: content, content_type: content_type, layout: false)
17
+
18
+ responder.to_html
19
+ end
20
+ end
21
+
22
+ context 'redirect' do
23
+ let(:page) { build :page, :redirect }
24
+
25
+ it 'should render a redirect' do
26
+ responder.should_receive(:redirect_to).with(page.redirect_url, status: page.status_code)
27
+ responder.should_not_receive(:render)
28
+
29
+ responder.to_html
30
+ end
31
+ end
32
+
33
+ context 'missing' do
34
+ let(:page) { build :page, :gone }
35
+
36
+ it 'should render a 410' do
37
+ expect { responder.to_html }.to raise_error(Landable::Page::GoneError)
38
+ responder.should_not_receive(:render)
39
+ end
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'public page routes' do
4
+
5
+ routes { Landable::Engine.routes }
6
+
7
+ context 'should match' do
8
+ let(:author) { create :author }
9
+ let(:pages) do
10
+ [ create(:page, status_code: 200),
11
+ create(:page, status_code: 301, redirect_url: 'http://google.com/'),
12
+ create(:page, status_code: 302, redirect_url: 'http://foobar.com/'),
13
+ create(:page, status_code: 410)
14
+ ].each do |page|
15
+ page.publish! author: author, status_code: page.status_code
16
+ end
17
+ end
18
+
19
+ specify 'published pages with any status_code' do
20
+ pages.each do |page|
21
+ expect(get: page.path).to route_to(controller: 'landable/public/pages', action: 'show', url: page.path[1..-1])
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'should not match' do
27
+ specify 'random stuff' do
28
+ expect(get: '/foobar').to_not be_routable
29
+ end
30
+
31
+ specify 'unpublished pages' do
32
+ page = create :page
33
+ expect(get: page.path).to_not be_routable
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Landable::AuthenticationService do
4
+ let(:simple_auth) do
5
+ proc do |username, password|
6
+ if username == 'simple' && password == 'authenticator'
7
+ { username: 'simple', email: 'simple@example.com', first_name: 'Simple', last_name: 'Ton' }
8
+ end
9
+ end
10
+ end
11
+
12
+ let(:echo_auth) do
13
+ Landable::AuthenticationService::EchoAuthenticator
14
+ end
15
+
16
+ before do
17
+ Landable.configuration.stub!(authenticators: [simple_auth, echo_auth])
18
+ end
19
+
20
+ it "returns the result of the first successful authentication strategy" do
21
+ described_class.call('simple', 'authenticator')[:username].should == 'simple'
22
+ described_class.call('echo', 'echo')[:username].should == 'echo'
23
+ end
24
+
25
+ it "raises AuthenticationFailedError if no strategy worked" do
26
+ expect {
27
+ described_class.call('will', 'fail')
28
+ }.to raise_error(Landable::AuthenticationFailedError)
29
+ end
30
+
31
+ describe 'EchoAuthenticator' do
32
+ it 'returns nil outside of development and test environments' do
33
+ Rails.env.stub(development?: false, test?: false)
34
+ echo_auth.call('would-have', 'worked').should be_nil
35
+
36
+ Rails.env.stub(development?: true, test?: false)
37
+ echo_auth.call('will-now', 'work').should_not be_nil
38
+
39
+ Rails.env.stub(development?: false, test?: true)
40
+ echo_auth.call('will-now', 'work').should_not be_nil
41
+ end
42
+
43
+ it 'returns nil for password "fail"' do
44
+ echo_auth.call('will', 'fail').should be_nil
45
+ end
46
+
47
+ it 'returns an author for the given username' do
48
+ entry = echo_auth.call('anyone', 'anything')
49
+ entry.should include(username: 'anyone', email: 'anyone@example.com')
50
+ entry.should have_key(:first_name)
51
+ entry.should have_key(:last_name)
52
+ end
53
+
54
+ it 'can be instantiated to only echo a certain username/password' do
55
+ instance = echo_auth.new('trogdor', 'some-pass')
56
+ instance.call('previously', 'worked').should be_nil
57
+ instance.call('trogdor', 'trogdor').should be_nil
58
+ instance.call('trogdor', 'some-pass')[:username].should == 'trogdor'
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe RenderService do
5
+ let(:page) { build :page, body: 'render test', theme: theme }
6
+ let(:theme) { create :theme }
7
+
8
+ def render(*args)
9
+ options = args.extract_options!
10
+ target = args.first || page
11
+
12
+ RenderService.call(target, options)
13
+ end
14
+
15
+ it 'returns a string' do
16
+ page.body = 'Hi mom'
17
+ theme.body = '{{body}}'
18
+ render.should == 'Hi mom'
19
+ end
20
+
21
+ context 'without a theme' do
22
+ it 'returns the bare page body' do
23
+ page.theme = nil
24
+ render.should == page.body
25
+ end
26
+
27
+ it 'returns the bare page body if the theme is defined, but has no liquid body template' do
28
+ page.theme.body = nil
29
+ render.should == page.body
30
+ end
31
+
32
+ it 'returns an empty string if there is also no page body' do
33
+ page.theme = nil
34
+ page.body = nil
35
+ render.should == ''
36
+ end
37
+ end
38
+
39
+ context 'without a body' do
40
+ it 'renders the bare theme' do
41
+ theme.body = 'foo {{body}}'
42
+ page.body = nil
43
+ render.should == 'foo '
44
+ end
45
+ end
46
+
47
+ context 'for a redirect' do
48
+ let(:page) { build :page, :redirect }
49
+
50
+ context 'previewing' do
51
+ let(:rendered) { render(preview: true) }
52
+
53
+ it 'conveys information about the redirect' do
54
+ rendered.should include "#{page.status_code}"
55
+ rendered.should include "<a href=\"#{page.redirect_url}\">#{page.redirect_url}</a>"
56
+ end
57
+ end
58
+
59
+ context 'not previewing' do
60
+ let(:rendered) { render }
61
+
62
+ it 'should not include those things' do
63
+ rendered.should_not include "#{page.status_code}"
64
+ rendered.should_not include "<a href=\"#{page.redirect_url}\">#{page.redirect_url}</a>"
65
+ end
66
+ end
67
+ end
68
+
69
+ context 'for non-html' do
70
+ let(:page) { build :page, body: 'render test', path: 'foo.txt', theme: theme }
71
+
72
+ it 'renders without a theme' do
73
+ render.should == 'render test'
74
+ end
75
+
76
+ context 'previewing' do
77
+ let(:rendered) { render(preview: true) }
78
+
79
+ it 'renders with <pre> around the content' do
80
+ rendered.should == '<pre>render test</pre>'
81
+ end
82
+ end
83
+ end
84
+
85
+ context 'with a Responder' do
86
+ # setup
87
+ let(:controller) { double('MockController', fetch_landable_variables: { 'hello_world' => "I'm a Loner, Dottie. A Rebel." }) }
88
+ let(:responder) { double('responder', controller: controller) }
89
+ let(:rendered) { render(responder: responder) }
90
+
91
+ # tests
92
+ it 'should include registered variables from an external controller source' do
93
+ # setup
94
+ page.body = '{{hello_world}}'
95
+ # actions
96
+ # expectations
97
+ rendered.should match "I'm a Loner, Dottie. A Rebel."
98
+ # end
99
+ end
100
+ # end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ describe TidyService do
5
+ let(:service) { TidyService }
6
+
7
+ describe '.tidyable?' do
8
+ after(:each) { service.class_variable_set :@@is_tidyable, nil }
9
+
10
+ context 'when tidyable' do
11
+ it 'should check on the availability of a `tidy` command' do
12
+ Kernel.should_receive(:system).with('which tidy > /dev/null') { true }
13
+ service.should be_tidyable
14
+ service.class_variable_get(:@@is_tidyable).should be_true
15
+ end
16
+ end
17
+
18
+ context 'not tidyable' do
19
+ it 'should return false' do
20
+ Kernel.should_receive(:system).with('which tidy > /dev/null') { false }
21
+ service.should_not be_tidyable
22
+ service.class_variable_get(:@@is_tidyable).should be_false
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '.call!' do
28
+ it 'should call #call with raise_on_error: true' do
29
+ input = double
30
+ output = double
31
+ service.should_receive(:call).with(input, raise_on_error: true) { output }
32
+ service.call!(input).should == output
33
+ end
34
+ end
35
+
36
+ describe '.call' do
37
+ context 'when not tidyable' do
38
+ it 'should raise an exception' do
39
+ service.should_receive(:tidyable?) { false }
40
+ expect { service.call 'foo' }.to raise_error(StandardError)
41
+ end
42
+ end
43
+
44
+ context 'when tidyable' do
45
+ before(:each) do
46
+ service.should_receive(:tidyable?) { true }
47
+ end
48
+
49
+ it 'should invoke tidy and return a Result' do
50
+ input = double('input')
51
+ output = double('output')
52
+ result = double('result')
53
+
54
+ service.should_receive(:wrap_liquid) { |input| input }.ordered # passthrough; will test later
55
+
56
+ mock_io = double('io')
57
+ mock_io.should_receive(:puts).with(input).ordered
58
+ mock_io.should_receive(:close_write).ordered
59
+ mock_io.should_receive(:read) { output }
60
+
61
+ service.should_receive(:unwrap_liquid) { |input| input }.ordered # passthrough; will test later
62
+
63
+ # other setup
64
+ service.should_receive(:options) { ['one', 'two', 'three'] }
65
+ IO.should_receive(:popen).with('tidy one two three', 'r+').and_yield(mock_io)
66
+
67
+ TidyService::Result.should_receive(:new).with(output) { result }
68
+
69
+ service.call(input).should == result
70
+ end
71
+
72
+ it 'should wrap known liquid tags before sending to tidy, and unwrap them after' do
73
+ original_string = '<div> {% template foobar title: "sixteen" %} <span> {% meta_tags "something" %} </span> </div>'
74
+ wrapped_string = '<div> <div data-liquid="' + Base64.encode64('{% template foobar title: "sixteen" %}').strip + '"></div> <span> <div data-liquid="' + Base64.encode64('{% meta_tags "something" %}').strip + '"></div> </span> </div>'
75
+
76
+ # mock out tidy itself, and make it a no-op
77
+ mock_io = double('io')
78
+ mock_io.should_receive(:puts).with(wrapped_string)
79
+ mock_io.should_receive(:close_write)
80
+ mock_io.should_receive(:read) { wrapped_string }
81
+ IO.should_receive(:popen).and_yield(mock_io)
82
+
83
+ # ensuring that the output == the input, modulo any new whitespace
84
+ service.call(original_string).to_s.gsub(/\s+/, ' ').should == original_string.gsub(/\s+/, ' ')
85
+ end
86
+
87
+ context 'raise_on_error is enabled' do
88
+ it 'should raise TidyError when status is 2' do
89
+ IO.should_receive(:popen)
90
+ $?.should_receive(:exitstatus) { 2 }
91
+
92
+ # meh, shouldn't have to do this. could use a refactor.
93
+ service.should_receive(:wrap_liquid) { |input| input }
94
+
95
+ expect { service.call('<div>foo</div>', raise_on_error: true) }.to raise_error TidyService::TidyError
96
+ end
97
+
98
+ it 'should be cool when status is 1' do
99
+ IO.should_receive(:popen)
100
+ $?.should_receive(:exitstatus) { 1 }
101
+
102
+ # meh, shouldn't have to do this. could use a refactor.
103
+ service.should_receive(:wrap_liquid) { |input| input }
104
+
105
+ expect { service.call('<div>foo</div>', raise_on_error: true) }.to_not raise_error TidyService::TidyError
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ module TidyService
113
+ describe Result do
114
+
115
+ let(:result) { Result.new <<-eof
116
+ <html>
117
+ <head>
118
+ <link type="text/css" rel="stylesheet">
119
+ <title>sup</title>
120
+ <style type="text/css">
121
+ body {}
122
+ </style>
123
+ </head>
124
+ <body class="foo">
125
+ <div>hello</div>
126
+ <div>friend</div>
127
+ </body>
128
+ </html>
129
+ eof
130
+ }
131
+
132
+ describe '#to_s' do
133
+ it 'should return the string given on init' do
134
+ Result.new('foobar').to_s.should == 'foobar'
135
+ end
136
+ end
137
+
138
+ describe '#body' do
139
+ it 'should return the de-indented contents of <body>' do
140
+ result.body.should == "<div>hello</div>\n<div>friend</div>"
141
+ end
142
+ end
143
+
144
+ describe '#head' do
145
+ it 'should return the de-indented contents of <head>' do
146
+ result.head.should == "<link type=\"text/css\" rel=\"stylesheet\">\n<title>sup</title>\n<style type=\"text/css\">\n body {}\n</style>"
147
+ end
148
+ end
149
+
150
+ describe '#css' do
151
+ it 'should return embedded and linked stylesheets from the head' do
152
+ result.css.should == "<link type=\"text/css\" rel=\"stylesheet\">\n\n<style type=\"text/css\">\n body {}\n</style>"
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end