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,17 @@
1
+ Feature: Content types by extension
2
+ As a content author
3
+ I want Landable to deliver the right content type for my content
4
+
5
+ Scenario Outline:
6
+ Given a published page "/foo.<ext>"
7
+ When I GET "/foo.<ext>"
8
+ Then the response header "Content-Type" should be "<content_type>"
9
+
10
+ Examples:
11
+ | ext | content_type |
12
+ | xml | application/xml |
13
+ | json | application/json |
14
+ | txt | text/plain |
15
+ | html | text/html |
16
+ | htm | text/html |
17
+ | foo | text/plain |
@@ -0,0 +1,45 @@
1
+ Feature: Only published revisions are publicly available
2
+
3
+ @allow-rescue
4
+ Scenario: Never-published page
5
+ Given a page "/unpub"
6
+ When I GET "/unpub"
7
+ Then the response status should be 404
8
+ When I publish the page "/unpub"
9
+ And I GET "/unpub"
10
+ Then the response status should be 200
11
+
12
+ Scenario: Currently published revision
13
+ Given a published page "/pubbed"
14
+ When I GET "/pubbed"
15
+ Then the response status should be 200
16
+ And the response body should include the body of page "/pubbed"
17
+
18
+ Scenario: Unpublished change from one theme to another
19
+ Given a published page "/pubbed" with a theme containing "foo"
20
+ When I choose another theme containing "bar"
21
+ And I GET "/pubbed"
22
+ Then I should see "foo"
23
+ When I publish the page with another theme
24
+ And I GET "/pubbed"
25
+ Then I should see "bar"
26
+
27
+ Scenario: Changes to the theme itself do not need to be published (for now?)
28
+ Given a published page "/pubbed" with a theme containing "foo"
29
+ When I change the theme to contain "bar"
30
+ And I GET "/pubbed"
31
+ Then I should see "bar"
32
+
33
+ @allow-rescue
34
+ Scenario: Unpublished status change
35
+ Given a published page "/pubbed"
36
+ When I change the page to a 410
37
+ And I GET "/pubbed"
38
+ Then the response status should be 200
39
+ When I publish the page
40
+ And I GET "/pubbed"
41
+ Then the response status should be 410
42
+ When I revert to the previous revision
43
+ And I publish the page
44
+ And I GET "/pubbed"
45
+ Then the response status should be 200
@@ -0,0 +1,25 @@
1
+ Feature: Page status codes
2
+
3
+ @allow-rescue
4
+ Scenario Outline: HTTP response status
5
+ Given a published page "/foo" with status <code>
6
+ When I GET "/foo"
7
+ Then the response status should be <code>
8
+
9
+ Examples:
10
+ | code |
11
+ | 200 |
12
+ | 301 |
13
+ | 302 |
14
+ | 410 |
15
+
16
+ Scenario Outline: Redirects
17
+ Given page "/foo" redirects to "http://google.com" with status <code>
18
+ When I GET "/foo"
19
+ Then the response status should be <code>
20
+ And I should have been redirected to "http://google.com"
21
+
22
+ Examples:
23
+ | code |
24
+ | 301 |
25
+ | 302 |
@@ -0,0 +1,17 @@
1
+ Feature: Helpers are provided for Rails views that Landable didn't serve
2
+ The dummy app defines a few routes before mounting
3
+ Landable; we want to ensure those routes still function
4
+ from both the application and Landable's perspective.
5
+
6
+ Scenario: Application view references landable helpers
7
+ Given a page "/priority" with title "Landable4Life"
8
+ And the robots meta tag of "/priority" is "noindex,nofollow"
9
+ And the body of page "/priority" is:
10
+ """
11
+ <h1>Inline HTML!</h1>
12
+ """
13
+ When I GET "/priority"
14
+ Then the response status should be 200
15
+ And the element "title" should have inner text "Home: Landable4Life"
16
+ And the element "#path" should have inner text "/priority"
17
+ And the element "meta[name='robots'][content='noindex,nofollow']" should exist
@@ -0,0 +1,60 @@
1
+ # a bit specific eh ...
2
+ Given '2 assets named "panda" and "disclaimer"' do
3
+ @assets = [create(:asset, name: 'panda', fixture: 'panda.png'),
4
+ create(:asset, name: 'disclaimer', fixture: 'small.pdf')]
5
+ end
6
+
7
+ When 'I POST an asset to "$path"' do |path|
8
+ post path, asset: attributes_for(:asset, fixture: 'panda.png')
9
+ last_request.should be_form_data
10
+ end
11
+
12
+ When 'I POST that asset to "$path" again' do |path|
13
+ post path, asset: attributes_for(:asset, fixture: 'panda.png')
14
+ last_request.should be_form_data
15
+ end
16
+
17
+ When 'I POST an asset to "$path" with both $assoc IDs' do |path, assoc|
18
+ ary = instance_variable_get("@#{assoc}s")
19
+ key = :"#{assoc}_ids"
20
+
21
+ post path, asset: attributes_for(:asset).merge({
22
+ key => ary.map(&:id)
23
+ })
24
+ end
25
+
26
+ Then 'the response should contain an "asset"' do
27
+ last_json.should have_key('asset')
28
+ last_json['asset']['mime_type'].should == 'image/png'
29
+ end
30
+
31
+ Then 'the response should contain the $version "asset"' do |version|
32
+ # version is intentionally ignored; it just reads better in the steps.
33
+
34
+ @asset ||= Landable::Asset.order('created_at DESC').first
35
+ at_json('asset/id').should == @asset.id
36
+ end
37
+
38
+ Then /^the asset ID should( not)? be in the array at "([^"]+)"$/ do |negative, json_path|
39
+ array = at_json json_path
40
+ if negative
41
+ array.should_not include(@asset.id)
42
+ else
43
+ array.should include(@asset.id)
44
+ end
45
+ end
46
+
47
+ Then 'both $assoc IDs should be in the array at "$json_path"' do |assoc, json_path|
48
+ ids = instance_variable_get("@#{assoc}s").map &:id
49
+ at_json(json_path).should include(*ids)
50
+ end
51
+
52
+ Then 'the rendered body should be the correct assets' do
53
+ <<-eos
54
+ <link href="/assets/application-/#{/./}.css" media="screen" rel="stylesheet" />
55
+ <script src="/assets/application-#{/./}.js"></script>
56
+ <img alt="Foo" src="/assets/foo-#{/./}.jpg" />
57
+ <img alt="Foo" src="/assets/foo-#{/./}.jpg" />
58
+ <img alt="Baz!" src="https://landable.dev/_assets//uploads/panda.png" />
59
+ eos
60
+ end
@@ -0,0 +1,139 @@
1
+ # Lame file name, but I don't know what else to call it atm.
2
+
3
+ expand_mustache = lambda do |context, str|
4
+ if str.respond_to?(:gsub)
5
+ str.gsub(/\{\{([^ ].+?)\}\}/) { context.eval($1) }
6
+ else
7
+ str
8
+ end
9
+ end
10
+
11
+ module Landable::FeatureHelper
12
+ def basic_authorize!(author = current_author, token = current_access_token)
13
+ basic_authorize author.username, token.id
14
+ end
15
+
16
+ def current_author
17
+ @current_author ||= create :author
18
+ end
19
+
20
+ def current_access_token
21
+ @current_access_token ||= create :access_token, author: @current_author
22
+ end
23
+
24
+ def last_json(body = last_response.body)
25
+ JSON.parse body
26
+ end
27
+
28
+ def latest(model)
29
+ model = model.to_s.classify
30
+ klass = "Landable::#{model}".constantize
31
+ klass.order('created_at DESC').first!
32
+ end
33
+ end
34
+
35
+ World(Landable::FeatureHelper)
36
+
37
+ Before '@api' do
38
+ header 'Accept', 'application/json'
39
+ header 'Content-Type', 'application/json'
40
+ end
41
+
42
+ Before '@api', '~@no-api-auth' do
43
+ basic_authorize!
44
+ end
45
+
46
+ Given 'I accept HTML' do
47
+ header 'Accept', 'text/html'
48
+ end
49
+
50
+ Given 'I accept JSON' do
51
+ header 'Accept', 'application/json'
52
+ end
53
+
54
+ Given 'my API requests include a valid access token' do
55
+ basic_authorize!
56
+ end
57
+
58
+ Given 'I repeat the request with a valid access token' do
59
+ basic_authorize!
60
+ request last_request.url, last_request.env
61
+ end
62
+
63
+ Given 'my access token will expire in 2 minutes' do
64
+ current_access_token.update_attributes!(expires_at: 2.minutes.from_now)
65
+ end
66
+
67
+ Given 'my access token expired 2 minutes ago' do
68
+ current_access_token.update_attributes!(expires_at: 2.minutes.ago)
69
+ end
70
+
71
+ When /^I (HEAD|GET|POST|PUT|PATCH|DELETE|OPTIONS)(?: to)? "(.+?)"$/ do |http_method, path|
72
+ request expand_mustache[binding, path], method: http_method
73
+ end
74
+
75
+ When /^I (POST|PUT|PATCH|DELETE|OPTIONS)(?: to)? "(.+?)"(?: with)?:$/ do |http_method, path, body|
76
+ request expand_mustache[binding, path], method: http_method, params: body
77
+ end
78
+
79
+ When 'I request CORS from "$path" with:' do |path, table|
80
+ options = table.hashes.first
81
+
82
+ header 'Origin', options['origin']
83
+ header 'Access-Control-Request-Method', options['method']
84
+ request path, method: 'OPTIONS'
85
+ end
86
+
87
+ When 'I follow the "Location" header' do
88
+ get last_response.headers['Location']
89
+ end
90
+
91
+ Then 'my access token should not expire for at least 2 hours' do
92
+ token = current_access_token.reload
93
+ token.expires_at.should be >= 2.hours.from_now
94
+ end
95
+
96
+ Then /^the response(?: status)? should(?: (not))? be (\d{3})(?: "[A-Za-z ]+")?$/ do |negate, code|
97
+ code = Integer(code)
98
+ if negate == 'not'
99
+ last_response.status.should_not eq(code)
100
+ else
101
+ last_response.status.should eq(code)
102
+ end
103
+ end
104
+
105
+ Then /^the response should contain (?:a|an) "([^"]+)"$/ do |model|
106
+ last_json.should have_key(model)
107
+ last_json[model].should have_key('id')
108
+ end
109
+
110
+ Then /^the response should contain (\d+) "([^"]+)"$/ do |count, kind|
111
+ last_json[kind].length.should == Integer(count)
112
+ end
113
+
114
+ Then 'the response body should be empty' do
115
+ last_response.body.should be_blank
116
+ end
117
+
118
+ Then 'the response body should be "$body"' do |body|
119
+ last_response.body.should == body
120
+ end
121
+
122
+ Then 'I should have been redirected to "$url"' do |url|
123
+ last_response.headers['Location'].should == url
124
+ end
125
+
126
+ Then 'the response headers should include:' do |table|
127
+ expected = table.hashes.reduce({}) do |acc, row|
128
+ acc.merge row['header'] => row['value']
129
+ end
130
+ last_response.headers.should include(expected)
131
+ end
132
+
133
+ Then 'the JSON at "$path" should be "$value"' do |json_path, value|
134
+ at_json(json_path).should == value
135
+ end
136
+
137
+ Then 'the response header "$header" should be "$content_type"' do |header, content_type|
138
+ last_response.headers[header].should include(content_type)
139
+ end
@@ -0,0 +1,3 @@
1
+ When 'I pry' do
2
+ binding.pry
3
+ end
@@ -0,0 +1,124 @@
1
+ Given /^(\d+) ([\w\s]+)$/ do |count, kind|
2
+ klass = kind.gsub(/\s+/, '_').singularize.classify.gsub(/^/, 'Landable::').constantize
3
+ klass.destroy_all
4
+
5
+ result = Integer(count).times.map do
6
+ create kind.gsub(/\s+/, '_').singularize.to_sym
7
+ end
8
+
9
+ instance_variable_set :"@#{kind}", result
10
+ end
11
+
12
+ Given 'there are no authors in the database' do
13
+ Landable::Author.delete_all
14
+ end
15
+
16
+ Given /^an author "([^"]+)"$/ do |username|
17
+ create :author, username: username
18
+ end
19
+
20
+ Given '"$username" has an unexpired access token' do |username|
21
+ create :access_token, author: Landable::Author.where(username: username).first!
22
+ end
23
+
24
+ Given 'I also have an older, expired access token' do
25
+ @expired_access_token = create :access_token, author: @current_author, expires_at: 2.minutes.ago
26
+ end
27
+
28
+ Given "there is another author's access token in the database" do
29
+ @foreign_access_token = create :access_token, author: create(:author)
30
+ end
31
+
32
+ Given /^an? (page|theme|asset|template)$/ do |model|
33
+ instance_variable_set :"@#{model}", create(model.to_sym)
34
+ end
35
+
36
+ Given /^a (page|theme) with an asset attached$/ do |model|
37
+ record = create model.to_sym
38
+ @asset = create :asset
39
+ record.assets.push @asset
40
+ instance_variable_set :"@#{model}", record
41
+ end
42
+
43
+ Given /^a page "([^"]+)"$/ do |path|
44
+ create :page, path: path, body: "<HTML>BODY</HTML>"
45
+ end
46
+
47
+ Given /^a page "([^"]+)" with title "(.+)"$/ do |path, title|
48
+ create :page, path: path, title: title, body: "<HTML>BODY</HTML>"
49
+ end
50
+
51
+ Given /^a "(\w+)" page with title "(.+)" and category "(.+)"$/ do |published, title, category|
52
+ category_obj = Landable::Category.where('lower(name) = ?', category.downcase).first
53
+ @page = create :page, title: title, category: category_obj
54
+ @page.publish! author: create(:author) if published == 'published'
55
+ end
56
+
57
+ Given 'page "$path" redirects to "$url" with status $code' do |path, url, code|
58
+ page = create :page, :redirect, path: path, redirect_url: url, status_code: code, body: "BODY"
59
+ page.publish! author: create(:author)
60
+ end
61
+
62
+ Given 'a published page "$path"' do |path|
63
+ @page = create :page, path: path
64
+ @page.publish! author: create(:author)
65
+ end
66
+
67
+ Given 'a published page "$path" with status $code' do |path, code|
68
+ code = Integer(code)
69
+ page = case code
70
+ when 301, 302 then create :page, :redirect, path: path, status_code: code, body: "BODY"
71
+ when 410 then create :page, :gone, path: path, body: "BODY"
72
+ else create :page, path: path, body: "BODY"
73
+ end
74
+ page.publish! author: create(:author)
75
+ end
76
+
77
+ Given 'a published page "$path" titled "$title" with theme "$name"' do |path, title, theme|
78
+ page = create :page, path: path, title: title, theme: create(:theme, name: theme)
79
+ page.publish! author: create(:author)
80
+ end
81
+
82
+ Given 'the body of theme "$name" is "$body"' do |name, body|
83
+ theme = Landable::Theme.where(name: name).first!
84
+ theme.update_attributes! body: body
85
+ end
86
+
87
+ Given 'the $tag meta tag of "$path" is "$value"' do |tag, path, value|
88
+ page = Landable::Page.by_path!(path)
89
+ page.meta_tags ||= {}
90
+ page.meta_tags.merge! tag => value
91
+ page.save!
92
+ end
93
+
94
+ Given 'the body of page "$path" is:' do |path, body|
95
+ Landable::Page.by_path!(path).update_attributes(body: body)
96
+ end
97
+
98
+ When 'I publish the page "$path"' do |path|
99
+ Landable::Page.by_path!(path).publish! author: create(:author)
100
+ end
101
+
102
+ When(/^I change the page to a (\d+)$/) do |code|
103
+ @page.status_code = code
104
+ @page.save!
105
+ end
106
+
107
+ Then /^there should be (\d+) ([\w\s]+) in the database$/ do |count, kind|
108
+ name = kind.gsub(/\s+/, '_').classify
109
+ klass = "Landable::#{name}".constantize
110
+ klass.count.should eql(Integer(count))
111
+ end
112
+
113
+ Then 'an author "$username" should exist' do |username|
114
+ Landable::Author.where(username: username).first!
115
+ end
116
+
117
+ Then /^the author "(.+?)" should have (\d+) access tokens?$/ do |username, n|
118
+ author = Landable::Author.where(username: username).first!
119
+ author.access_tokens.count.should == Integer(n)
120
+ end
121
+
122
+ Then 'the response body should include the body of page "$path"' do |path|
123
+ last_response.body.should include(Landable::Page.by_path!(path).body)
124
+ end
@@ -0,0 +1,9 @@
1
+ Then 'the element "$css" should exist' do |css|
2
+ doc = Nokogiri::HTML(last_response.body)
3
+ doc.at(css).should be_present
4
+ end
5
+
6
+ Then 'the element "$css" should have inner text "$text"' do |css, text|
7
+ doc = Nokogiri::HTML(last_response.body)
8
+ doc.at(css).text.should == text
9
+ end