mist 0.6.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 (147) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +192 -0
  7. data/README.md +108 -0
  8. data/Rakefile +9 -0
  9. data/app/assets/images/mist/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  10. data/app/assets/images/mist/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  11. data/app/assets/images/mist/ui-bg_flat_10_000000_40x100.png +0 -0
  12. data/app/assets/images/mist/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  13. data/app/assets/images/mist/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  14. data/app/assets/images/mist/ui-bg_glass_65_ffffff_1x400.png +0 -0
  15. data/app/assets/images/mist/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  16. data/app/assets/images/mist/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  17. data/app/assets/images/mist/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  18. data/app/assets/images/mist/ui-icons_222222_256x240.png +0 -0
  19. data/app/assets/images/mist/ui-icons_228ef1_256x240.png +0 -0
  20. data/app/assets/images/mist/ui-icons_ef8c08_256x240.png +0 -0
  21. data/app/assets/images/mist/ui-icons_ffd27a_256x240.png +0 -0
  22. data/app/assets/images/mist/ui-icons_ffffff_256x240.png +0 -0
  23. data/app/assets/javascripts/mist/jquery-ui-1.8.17.custom.min.js +356 -0
  24. data/app/assets/javascripts/mist/posts.js.coffee +3 -0
  25. data/app/assets/javascripts/mist_core.js +3 -0
  26. data/app/assets/stylesheets/mist/posts.css.sass +97 -0
  27. data/app/assets/stylesheets/mist/scaffolds.css.scss +56 -0
  28. data/app/assets/stylesheets/mist/ui-lightness/jquery-ui-1.8.17.custom.css +565 -0
  29. data/app/assets/stylesheets/mist_core.css +4 -0
  30. data/app/controllers/application_controller.rb +3 -0
  31. data/app/controllers/mist/posts_controller.rb +130 -0
  32. data/app/helpers/mist/posts_helper.rb +61 -0
  33. data/app/mailers/.gitkeep +0 -0
  34. data/app/models/.gitkeep +0 -0
  35. data/app/models/mist/post.rb +240 -0
  36. data/app/models/mist/post_sweeper.rb +63 -0
  37. data/app/views/layouts/mist/posts.html.erb +66 -0
  38. data/app/views/mist/posts/_form.html.erb +55 -0
  39. data/app/views/mist/posts/_post.html.erb +19 -0
  40. data/app/views/mist/posts/_sidebar_title.html.erb +5 -0
  41. data/app/views/mist/posts/edit.html.erb +16 -0
  42. data/app/views/mist/posts/feed.atom.builder +17 -0
  43. data/app/views/mist/posts/index.html.erb +31 -0
  44. data/app/views/mist/posts/new.html.erb +15 -0
  45. data/app/views/mist/posts/show.html.erb +3 -0
  46. data/config/cucumber.yml +8 -0
  47. data/config/environment.rb +1 -0
  48. data/config/routes.rb +7 -0
  49. data/features/atom_feed.feature +20 -0
  50. data/features/authorize_create_post.feature +14 -0
  51. data/features/authorize_destroy_post.feature +27 -0
  52. data/features/authorize_update_post.feature +28 -0
  53. data/features/authorize_view_unpublished.feature +22 -0
  54. data/features/create_post.feature +13 -0
  55. data/features/destroy_post.feature +13 -0
  56. data/features/format_with_markdown.feature +13 -0
  57. data/features/popular_posts.feature +35 -0
  58. data/features/recent_posts.feature +28 -0
  59. data/features/similar_posts.feature +43 -0
  60. data/features/step_definitions/authorization_steps.rb +17 -0
  61. data/features/step_definitions/filesystem_steps.rb +13 -0
  62. data/features/step_definitions/page_steps.rb +43 -0
  63. data/features/step_definitions/post_steps.rb +58 -0
  64. data/features/step_definitions/sidebar_steps.rb +21 -0
  65. data/features/support/env.rb +58 -0
  66. data/features/support/setup.rb +15 -0
  67. data/features/update_post.feature +19 -0
  68. data/gemfiles/common +20 -0
  69. data/gemfiles/rails-3.1.3 +4 -0
  70. data/gemfiles/rails-3.1.3.lock +195 -0
  71. data/gemfiles/rails-3.2.0 +4 -0
  72. data/lib/generators/mist/USAGE +13 -0
  73. data/lib/generators/mist/setup_generator.rb +21 -0
  74. data/lib/generators/mist/templates/initializer.rb +17 -0
  75. data/lib/generators/mist/templates/mist.css.scss +2 -0
  76. data/lib/generators/mist/templates/mist.js.coffee +1 -0
  77. data/lib/generators/mist/views_generator.rb +11 -0
  78. data/lib/mist.rb +26 -0
  79. data/lib/mist/code_example_parser.rb +86 -0
  80. data/lib/mist/configuration.rb +68 -0
  81. data/lib/mist/configuration/author.rb +8 -0
  82. data/lib/mist/engine.rb +12 -0
  83. data/lib/mist/git_file_system_history.rb +66 -0
  84. data/lib/mist/git_model.rb +154 -0
  85. data/lib/mist/git_model/attributes.rb +21 -0
  86. data/lib/mist/git_model/class_methods.rb +141 -0
  87. data/lib/mist/permalink.rb +5 -0
  88. data/lib/mist/repository.rb +15 -0
  89. data/lib/mist/version.rb +8 -0
  90. data/mist.gemspec +33 -0
  91. data/spec/controllers/posts_controller_spec.rb +263 -0
  92. data/spec/dummy_rails_app/app/assets/javascripts/mist.js.coffee +1 -0
  93. data/spec/dummy_rails_app/app/assets/stylesheets/mist.css.scss +2 -0
  94. data/spec/dummy_rails_app/app/views/layouts/mist/posts.html.erb +60 -0
  95. data/spec/dummy_rails_app/config.ru +4 -0
  96. data/spec/dummy_rails_app/config/application.rb +48 -0
  97. data/spec/dummy_rails_app/config/boot.rb +7 -0
  98. data/spec/dummy_rails_app/config/database.yml +28 -0
  99. data/spec/dummy_rails_app/config/environment.rb +5 -0
  100. data/spec/dummy_rails_app/config/environments/development.rb +32 -0
  101. data/spec/dummy_rails_app/config/environments/production.rb +60 -0
  102. data/spec/dummy_rails_app/config/environments/test.rb +42 -0
  103. data/spec/dummy_rails_app/config/initializers/active_gist_credentials.rb +8 -0
  104. data/spec/dummy_rails_app/config/initializers/backtrace_silencers.rb +7 -0
  105. data/spec/dummy_rails_app/config/initializers/inflections.rb +10 -0
  106. data/spec/dummy_rails_app/config/initializers/mime_types.rb +5 -0
  107. data/spec/dummy_rails_app/config/initializers/mist.rb +17 -0
  108. data/spec/dummy_rails_app/config/initializers/secret_token.rb +7 -0
  109. data/spec/dummy_rails_app/config/initializers/session_store.rb +8 -0
  110. data/spec/dummy_rails_app/config/initializers/wrap_parameters.rb +14 -0
  111. data/spec/dummy_rails_app/config/locales/en.yml +5 -0
  112. data/spec/dummy_rails_app/config/routes.rb +3 -0
  113. data/spec/dummy_rails_app/db/schema.rb +22 -0
  114. data/spec/dummy_rails_app/db/seeds.rb +7 -0
  115. data/spec/dummy_rails_app/doc/README_FOR_APP +2 -0
  116. data/spec/dummy_rails_app/lib/assets/.gitkeep +0 -0
  117. data/spec/dummy_rails_app/lib/tasks/.gitkeep +0 -0
  118. data/spec/dummy_rails_app/lib/tasks/cucumber.rake +65 -0
  119. data/spec/dummy_rails_app/public/404.html +26 -0
  120. data/spec/dummy_rails_app/public/422.html +26 -0
  121. data/spec/dummy_rails_app/public/500.html +26 -0
  122. data/spec/dummy_rails_app/public/favicon.ico +0 -0
  123. data/spec/dummy_rails_app/public/robots.txt +5 -0
  124. data/spec/dummy_rails_app/script/cucumber +10 -0
  125. data/spec/dummy_rails_app/script/rails +6 -0
  126. data/spec/dummy_rails_app/vendor/assets/stylesheets/.gitkeep +0 -0
  127. data/spec/dummy_rails_app/vendor/plugins/.gitkeep +0 -0
  128. data/spec/factories/posts.rb +8 -0
  129. data/spec/fixtures/gist_404 +6 -0
  130. data/spec/fixtures/gist_with_1_code_example +66 -0
  131. data/spec/helpers/posts_helper_spec.rb +64 -0
  132. data/spec/lib/mist/code_example_parser_spec.rb +135 -0
  133. data/spec/lib/mist/configuration_spec.rb +88 -0
  134. data/spec/lib/mist/permalink_spec.rb +17 -0
  135. data/spec/lib/mist/repository_spec.rb +20 -0
  136. data/spec/models/mist/git_model_spec.rb +260 -0
  137. data/spec/models/mist/post_spec.rb +575 -0
  138. data/spec/requests/posts_caching_spec.rb +152 -0
  139. data/spec/spec_helper.rb +20 -0
  140. data/spec/support/cache_helpers.rb +71 -0
  141. data/spec/support/config.rb +42 -0
  142. data/spec/support/fakeweb.rb +3 -0
  143. data/spec/views/mist/posts/edit.html.erb_spec.rb +11 -0
  144. data/spec/views/mist/posts/index.html.erb_spec.rb +27 -0
  145. data/spec/views/mist/posts/new.html.erb_spec.rb +15 -0
  146. data/spec/views/mist/posts/show.html.erb_spec.rb +11 -0
  147. metadata +371 -0
@@ -0,0 +1,63 @@
1
+ class Mist::PostSweeper < ActionController::Caching::Sweeper
2
+ observe Mist::Post
3
+
4
+ def after_save(post)
5
+ bust! post
6
+ end
7
+
8
+ def after_destroy(post)
9
+ bust! post
10
+ end
11
+
12
+ def before_save(post)
13
+ @tags_were = post.tags_was
14
+ end
15
+
16
+ def bust!(post)
17
+ expire_page :action => 'feed', :format => 'atom'
18
+
19
+ touched = touched_posts post
20
+ each_combo(Mist.authorized_actions) do |options|
21
+ options.keys.each { |key| options.delete(key) unless options[key] }
22
+
23
+ expire_page options.merge(:action => 'index')
24
+ bust_post! post, options
25
+ touched.each { |p| bust_post! p, options }
26
+ end
27
+ end
28
+
29
+ def bust_post!(post, options)
30
+ expire_action options.merge(:action => 'show', :id => post.id)
31
+ end
32
+
33
+ def touched_posts(post)
34
+ (post.similar_posts | Mist::Post.matching_tags(@tags_were)).tap { |a| a.delete post }
35
+ end
36
+
37
+ private
38
+ def each_combo(keys)
39
+ len = keys.length
40
+ flags = 0 # start with all off / false
41
+ combos = []
42
+
43
+ # yields a single combination
44
+ process = proc do
45
+ options = ActiveSupport::OrderedHash.new
46
+ for i in 0...len
47
+ flag = (flags >> i) & 1
48
+ options[keys[i]] = (flag == 1)
49
+ end
50
+ combos << options
51
+ yield options if block_given?
52
+ end
53
+
54
+ # iterate through all combos and yield them one at a time
55
+ while ((flags >> len) & 1) == 0
56
+ process.call
57
+ flags += 1
58
+ end
59
+
60
+ # return combos
61
+ combos
62
+ end
63
+ end
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>
5
+ <%= Mist.title %>
6
+ <% if @post %>
7
+ - <%= @post.title %>
8
+ <% end %>
9
+ </title>
10
+
11
+ <%= auto_discovery_link_tag :atom, feed_posts_path(:format => :atom) %>
12
+ <%= stylesheet_link_tag "mist_core" %>
13
+ <%= stylesheet_link_tag "mist" %>
14
+ <%= javascript_include_tag "mist_core" %>
15
+ <%= javascript_include_tag "mist" %>
16
+ <%= csrf_meta_tags %>
17
+ </head>
18
+ <body>
19
+ <%= yield :header %>
20
+
21
+ <div id="sidebar">
22
+ <%= yield :sidebar %>
23
+
24
+ <% unless similar_posts.empty? %>
25
+ <div id="sidebar-similar-posts" class="sidebar bordered">
26
+ <h3>Similar Posts</h3>
27
+ <ul>
28
+ <% similar_posts.each do |post| %>
29
+ <li> <%= link_to post.title, post_path(post) %> </li>
30
+ <% end %>
31
+ </ul>
32
+ </div>
33
+ <% end %>
34
+
35
+ <% unless popular_posts.empty? %>
36
+ <div id="sidebar-popular-posts" class="sidebar bordered">
37
+ <h3>Popular Posts</h3>
38
+ <ul>
39
+ <% popular_posts.each do |post| %>
40
+ <li> <%= link_to post.title, post_path(post) %> </li>
41
+ <% end %>
42
+ </ul>
43
+ </div>
44
+ <% end %>
45
+
46
+ <% unless recent_posts.empty? %>
47
+ <div id="sidebar-recent-posts" class="sidebar bordered">
48
+ <h3>Recent Posts</h3>
49
+ <ul>
50
+ <% recent_posts.each do |post| %>
51
+ <li> <%= link_to post.title, post_path(post) %> </li>
52
+ <% end %>
53
+ </ul>
54
+ </div>
55
+ <% end %>
56
+ </div>
57
+
58
+ <div class="content">
59
+ <%= yield %>
60
+ </div>
61
+
62
+ <div id="footer">
63
+ <p>Powered by <%= link_to "Mist", 'http://github.com/sinisterchipmunk/mist' %> <%= Mist::VERSION %></p>
64
+ </div>
65
+ </body>
66
+ </html>
@@ -0,0 +1,55 @@
1
+ <%= form_for(@post) do |f| %>
2
+ <% if @post.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
5
+
6
+ <ul>
7
+ <% @post.errors.full_messages.each do |msg| %>
8
+ <li><%= msg %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+
14
+ <div class="fields">
15
+ <p class="field">
16
+ <div class="field-label">
17
+ <%= f.label :title %>
18
+ </div>
19
+ <div class="field-input">
20
+ <%= f.text_field :title %>
21
+ </div>
22
+ </p>
23
+
24
+ <p class="field">
25
+ <div class="field-label">
26
+ <%= f.label :published_at %>
27
+ </div>
28
+ <div class="field-input">
29
+ <%= f.text_field :published_at %>
30
+ </div>
31
+ </p>
32
+
33
+ <p class="field">
34
+ <div class="field-label">
35
+ <%= f.label :content %>
36
+ </div>
37
+ <div class="field-input">
38
+ <%= f.text_area :content %>
39
+ </div>
40
+ </p>
41
+
42
+ <p class="field">
43
+ <div class="field-label">
44
+ <%= f.label :tags %>
45
+ </div>
46
+ <div class="field-input">
47
+ <%= f.text_field :tags, :value => @post.tags.join(', ') %>
48
+ </div>
49
+ </p>
50
+ </div>
51
+
52
+ <p class="actions">
53
+ <%= f.submit "Save" %>
54
+ </p>
55
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <div class="post bordered">
2
+ <div class="title">
3
+ <h1>
4
+ <%= link_to post.title, post_path(post.id) %>
5
+ </h1>
6
+
7
+ <%= authorized_links admin_link_separator,
8
+ [:edit_post, 'Edit', edit_post_path(post.id)],
9
+ [:destroy_post, 'Destroy', post.id, {:confirm => 'Are you sure?', :method => :delete}] %>
10
+ </div>
11
+
12
+ <%= yield :subcaption %>
13
+
14
+ <% if preview %>
15
+ <%= post.content_as_html_preview %>
16
+ <% else %>
17
+ <%= post.content_as_html %>
18
+ <% end %>
19
+ </div>
@@ -0,0 +1,5 @@
1
+ <% content_for :sidebar do %>
2
+ <div class="banner sidebar bordered">
3
+ <%= link_to Mist.title, posts_path %>
4
+ </div>
5
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <%= render :partial => 'sidebar_title' %>
2
+
3
+ <div class="post bordered">
4
+ <div class="title">
5
+ <h1>
6
+ Editing post "<%= @post.title %>"
7
+ </h1>
8
+ </div>
9
+
10
+ <p>
11
+ <%= link_to 'Show', @post %> |
12
+ <%= link_to 'Back', posts_path %>
13
+ </p>
14
+
15
+ <%= render 'form' %>
16
+ </div>
@@ -0,0 +1,17 @@
1
+ atom_feed do |feed|
2
+ feed.title @title
3
+ feed.updated @updated
4
+
5
+ @posts.each do |post|
6
+ feed.entry(post) do |entry|
7
+ entry.title(post.title)
8
+ entry.content(post.content_as_html, :type => 'html')
9
+ entry.summary(post.content_as_html_preview, :type => 'html')
10
+
11
+ entry.author do |author|
12
+ author.name Mist.author.name
13
+ author.email Mist.author.email
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ <% content_for :header do %>
2
+ <div id="header" class="bordered">
3
+ <div class="banner">
4
+ <%= link_to Mist.title, posts_path %>
5
+
6
+ <div class="author">
7
+ with <%= mail_to Mist.author.email, Mist.author.name %>
8
+ </div>
9
+ </div>
10
+
11
+ <div class="admin">
12
+ <%= authorized_link :create_post, 'New Post', new_post_path %>
13
+ </div>
14
+ </div>
15
+ <% end %>
16
+
17
+ <% if @posts.empty? %>
18
+ <div class="post bordered">
19
+ <div class="title">
20
+ <h1>
21
+ o hai!
22
+ </h1>
23
+ </div>
24
+
25
+ <p>
26
+ Welcome to my new blog! I'll have some decent content up in no time &mdash; promise!
27
+ </p>
28
+ </div>
29
+ <% else %>
30
+ <%= render_posts %>
31
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <%= render :partial => 'sidebar_title' %>
2
+
3
+ <div class="post bordered">
4
+ <div class="title">
5
+ <h1>
6
+ New post
7
+ </h1>
8
+ </div>
9
+
10
+ <p>
11
+ <%= link_to 'Back', posts_path %>
12
+ </p>
13
+
14
+ <%= render 'form' %>
15
+ </div>
@@ -0,0 +1,3 @@
1
+ <%= render :partial => 'sidebar_title' %>
2
+
3
+ <%= render :partial => 'post', :locals => { :post => @post, :preview => false } %>
@@ -0,0 +1,8 @@
1
+ <%
2
+ rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
3
+ rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
4
+ std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"
5
+ %>
6
+ default: <%= std_opts %> features
7
+ wip: --tags @wip:3 --wip features
8
+ rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
@@ -0,0 +1 @@
1
+ require File.expand_path('../spec/dummy_rails_app/config/environment', File.dirname(__FILE__))
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ Mist::Engine.routes.draw do
2
+ resources :posts, :path => '' do
3
+ collection do
4
+ get :feed
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ Feature: Atom feed
2
+ As a blog owner
3
+ In order to increase return visits
4
+ I want to publish an Atom feed
5
+
6
+ Scenario: With no blog posts
7
+ When I am on the feed posts page in "atom" format
8
+ # I don't know how to test this. But, at least we've made sure no errors
9
+ # were generated...
10
+
11
+ Scenario: With an unpublished blog post
12
+ Given I have created a post called "Post title"
13
+ When I am on the feed posts page in "atom" format
14
+ Then I should not see "Post title"
15
+
16
+ Scenario: With a published blog post
17
+ Given I have published a post called "Post title"
18
+ When I am on the feed posts page in "atom" format
19
+ Then I should see "Post title"
20
+
@@ -0,0 +1,14 @@
1
+ Feature: Authorization of Create new post
2
+ As a blog owner
3
+ In order to protect by blog from malicious users
4
+ I want to authorize who creates a post
5
+
6
+ Scenario: Create post without authorization
7
+ Given I am not authorized
8
+ When I am on the posts page
9
+ Then I should not see "New Post"
10
+
11
+ Scenario: Create post with authorization
12
+ Given I am authorized
13
+ When I am on the posts page
14
+ Then I should see "New Post"
@@ -0,0 +1,27 @@
1
+ Feature: Update existing post
2
+ As a blog owner
3
+ In order to protect by site from malicious users
4
+ I want to authorize who can destroy an existing post
5
+
6
+ Background:
7
+ Given I have published a post called "Post title"
8
+
9
+ Scenario: Destroy post when not authorized
10
+ Given I am not authorized
11
+ When I look at the "Post title" post
12
+ Then I should not see "Destroy"
13
+
14
+ Scenario: Destroy post when authorized
15
+ Given I am authorized
16
+ When I look at the "Post title" post
17
+ Then I should see "Destroy"
18
+
19
+ Scenario: Destroy from main page when authorized
20
+ Given I am authorized
21
+ When I am on the posts page
22
+ Then I should see "Destroy"
23
+
24
+ Scenario: Destroy from main page when not authorized
25
+ Given I am not authorized
26
+ When I am on the posts page
27
+ Then I should not see "Destroy"
@@ -0,0 +1,28 @@
1
+ Feature: Update existing post
2
+ As a blog owner
3
+ In order to protect by site from malicious users
4
+ I want to authorize who can update an existing post
5
+
6
+ Background:
7
+ Given I have published a post called "Post title"
8
+
9
+ Scenario: Change post when not authorized
10
+ Given I am not authorized
11
+ When I look at the "Post title" post
12
+ Then I should not see "Edit"
13
+
14
+ Scenario: Change post when authorized
15
+ Given I am authorized
16
+ When I look at the "Post title" post
17
+ Then I should see "Edit"
18
+
19
+ Scenario: Update from main page when authorized
20
+ Given I am authorized
21
+ When I am on the posts page
22
+ Then I should see "Edit"
23
+
24
+ Scenario: Update from main page when not authorized
25
+ Given I am not authorized
26
+ When I am on the posts page
27
+ Then I should not see "Edit"
28
+
@@ -0,0 +1,22 @@
1
+ Feature: Authorize viewing unpublished posts
2
+ As a blog author
3
+ In order to keep my content clean and consistent
4
+ I want to prevent others from seeing a post before it is published
5
+
6
+ Scenario: Shown to authorized when not published
7
+ Given I am authorized
8
+ When I create a post called "Post title"
9
+ And I am on the posts page
10
+ Then I should see "Post title"
11
+
12
+ Scenario: Not shown to unauthorized when not published
13
+ Given I have created a post called "Post title"
14
+ And I am not authorized
15
+ When I am on the posts page
16
+ Then I should not see "Post title"
17
+
18
+ Scenario: Become available when published
19
+ Given I have published a post called "Post title"
20
+ And I am not authorized
21
+ When I am on the posts page
22
+ Then I should see "Post title"