blabs 0.4.4

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 (77) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +15 -0
  3. data/app/assets/images/uploadify-cancel.png +0 -0
  4. data/app/assets/images/uploadify.swf +0 -0
  5. data/app/assets/javascripts/blabs.js +79 -0
  6. data/app/assets/javascripts/jquery.uploadify.min.js +16 -0
  7. data/app/assets/stylesheets/blabs/blog.sass +6 -0
  8. data/app/assets/stylesheets/uploadify.css.scss +92 -0
  9. data/app/controllers/blabs/blog_posts_controller.rb +131 -0
  10. data/app/controllers/blabs/images_controller.rb +35 -0
  11. data/app/controllers/blabs/published_blog_posts_controller.rb +30 -0
  12. data/app/controllers/blabs_controller.rb +31 -0
  13. data/app/helpers/blabs/blog_posts_helper.rb +114 -0
  14. data/app/models/blabs/blog_post.rb +70 -0
  15. data/app/models/blabs/image.rb +4 -0
  16. data/app/models/blabs/tag.rb +9 -0
  17. data/app/models/blabs/tagging.rb +9 -0
  18. data/app/uploaders/image_uploader.rb +65 -0
  19. data/app/views/blabs/blog_posts/_form.html.haml +54 -0
  20. data/app/views/blabs/blog_posts/_image.html.haml +5 -0
  21. data/app/views/blabs/blog_posts/_image_panel.html.haml +32 -0
  22. data/app/views/blabs/blog_posts/edit.html.haml +7 -0
  23. data/app/views/blabs/blog_posts/feed.atom.builder +15 -0
  24. data/app/views/blabs/blog_posts/index.html.haml +27 -0
  25. data/app/views/blabs/blog_posts/new.html.haml +7 -0
  26. data/app/views/blabs/blog_posts/show.html.haml +16 -0
  27. data/app/views/layouts/blabs.html.erb +21 -0
  28. data/config/routes.rb +16 -0
  29. data/db/migrate/20130802113537_create_blabs_tables.rb +23 -0
  30. data/db/migrate/20130909092406_add_extract_to_blog_posts.rb +5 -0
  31. data/db/migrate/20131209144117_create_tags.rb +16 -0
  32. data/lib/blabs.rb +53 -0
  33. data/lib/blabs/engine.rb +13 -0
  34. data/lib/blabs/version.rb +3 -0
  35. data/lib/generators/blabs_generator.rb +8 -0
  36. data/lib/tasks/blabs_tasks.rake +4 -0
  37. data/spec/controllers/blabs/blog_posts_controller_spec.rb +50 -0
  38. data/spec/controllers/blabs/published_blog_posts_controller_spec.rb +32 -0
  39. data/spec/dummy/README.rdoc +28 -0
  40. data/spec/dummy/Rakefile +6 -0
  41. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  42. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  43. data/spec/dummy/app/controllers/application_controller.rb +15 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/models/account.rb +2 -0
  46. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  47. data/spec/dummy/bin/bundle +3 -0
  48. data/spec/dummy/bin/rails +4 -0
  49. data/spec/dummy/bin/rake +4 -0
  50. data/spec/dummy/config.ru +4 -0
  51. data/spec/dummy/config/application.rb +23 -0
  52. data/spec/dummy/config/boot.rb +5 -0
  53. data/spec/dummy/config/database.yml +25 -0
  54. data/spec/dummy/config/environment.rb +5 -0
  55. data/spec/dummy/config/environments/development.rb +29 -0
  56. data/spec/dummy/config/environments/production.rb +80 -0
  57. data/spec/dummy/config/environments/test.rb +36 -0
  58. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  59. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  60. data/spec/dummy/config/initializers/inflections.rb +16 -0
  61. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  62. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  63. data/spec/dummy/config/initializers/session_store.rb +3 -0
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  65. data/spec/dummy/config/locales/en.yml +23 -0
  66. data/spec/dummy/config/routes.rb +56 -0
  67. data/spec/dummy/db/migrate/20130808145755_create_accounts.rb +9 -0
  68. data/spec/dummy/db/schema.rb +60 -0
  69. data/spec/dummy/public/404.html +58 -0
  70. data/spec/dummy/public/422.html +58 -0
  71. data/spec/dummy/public/500.html +57 -0
  72. data/spec/dummy/public/favicon.ico +0 -0
  73. data/spec/helpers/blabs/blog_posts_helper_spec.rb +50 -0
  74. data/spec/lib/blabs_spec.rb +8 -0
  75. data/spec/models/blabs/blog_post_spec.rb +76 -0
  76. data/spec/spec_helper.rb +19 -0
  77. metadata +356 -0
@@ -0,0 +1,114 @@
1
+ # encoding: UTF-8
2
+
3
+ module Blabs::BlogPostsHelper
4
+ EXTRACT_LENGTH = 600
5
+
6
+ def title(title)
7
+ content_for(:title, title)
8
+ content_tag(:h1, title)
9
+ end
10
+
11
+ def field_error(form, object, field)
12
+ if object.errors[field] && object.errors[field].size > 0
13
+ content_tag 'div', object.errors[field].first,
14
+ class: 'i i-info form-error'
15
+ end
16
+ end
17
+
18
+ def admin_controls(blog_post)
19
+ edit_link = link_to("Edit", edit_blabs_blog_post_path(blog_post))
20
+ publish_link = publish_or_unpublish_link(blog_post)
21
+ delete_link = link_to("Delete", blog_post, method: :delete, data: { confirm: "Are you sure?" })
22
+ raw [edit_link, publish_link, delete_link].join(" • ")
23
+ end
24
+
25
+ def formatted_body(text)
26
+ options = { autolink: true, lax_spacing: true, space_after_headers: true}
27
+ renderer = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
28
+ auto_link_username(renderer.render(text)).html_safe
29
+ end
30
+
31
+ def validate_layout
32
+ if !content_for?(:title) && !@blog_post.meta_title.blank?
33
+ raise "You need to put a title yield in your layouts title tag.\n
34
+ e.g. <title>My Blog - <%= yield(:title) %> </title>\n"
35
+ end
36
+
37
+ if !content_for?(:meta_tags)
38
+ raise "You need to put a meta_tags yield in your layouts head tag.\n
39
+ e.g. <%= yield(:meta_tags) %> </head>\n"
40
+ end
41
+
42
+ end
43
+
44
+ def pretty_date(date)
45
+ date.strftime("#{date.day.ordinalize} %B %Y") unless date.blank?
46
+ end
47
+
48
+ def extract(blog_post)
49
+ if blog_post.extract.present?
50
+ extract = blog_post.extract + " " + link_to("Read more", pretty_blog_post_path(blog_post), :class=>"more")
51
+ formatted_body(extract)
52
+ else
53
+ truncate_html(formatted_body(blog_post.body), length: EXTRACT_LENGTH,
54
+ omission: "... #{link_to("Read more", pretty_blog_post_path(blog_post), :class=>"more")}",
55
+ escape: false)
56
+ end
57
+ end
58
+
59
+ def show_meta_tags
60
+ content_for(:meta_tags) do
61
+ concat twitter_meta_tag("card", "summary")
62
+ concat twitter_meta_tag("site", Blabs.twitter_site_username)
63
+ concat twitter_meta_tag("creator", @blog_post.try(:author_twitter_username))
64
+ concat twitter_meta_tag("title", @blog_post.title)
65
+
66
+ sample = truncate(strip_tags(formatted_body(@blog_post.body)), length: EXTRACT_LENGTH)
67
+
68
+ concat twitter_meta_tag("description", sample.strip)
69
+ concat twitter_meta_tag("image", @blog_post.post_image_url)
70
+
71
+ concat open_graph_meta_tag("title", @blog_post.title)
72
+ concat open_graph_meta_tag("description", sample.strip)
73
+ concat open_graph_meta_tag("image", @blog_post.post_image_url)
74
+ concat open_graph_meta_tag("url", pretty_blog_post_url(@blog_post))
75
+ end
76
+ end
77
+
78
+ def index_meta_tags
79
+ content_for(:meta_tags) do
80
+ concat auto_discovery_link_tag :atom, "/blog/feed"
81
+ concat auto_discovery_link_tag :rss, "/blog/feed.rss"
82
+ end
83
+ end
84
+
85
+ def auto_link_username(body)
86
+ body.gsub(/@(\w+)/, %Q{<a href="https://twitter.com/\\1">@\\1</a>})
87
+ end
88
+
89
+ def display_author(blog_post)
90
+ if blog_post.author_twitter_username.blank?
91
+ blog_post.author
92
+ else
93
+ link_to(blog_post.author, "https://twitter.com/#{blog_post.author_twitter_username.delete("@")}")
94
+ end
95
+ end
96
+
97
+ private
98
+ def publish_or_unpublish_link(blog_post)
99
+ if blog_post.published?
100
+ link_to("Unpublish", blabs_published_blog_posts_path(id: blog_post), method: :delete)
101
+ else
102
+ link_to("Publish", blabs_published_blog_posts_path(id: blog_post), method: :post)
103
+ end
104
+ end
105
+
106
+ def twitter_meta_tag(label, value)
107
+ tag(:meta, name: "twitter:#{label}", content: value)
108
+ end
109
+
110
+ def open_graph_meta_tag(label, value)
111
+ tag(:meta, property: "og:#{label}", content: value)
112
+ end
113
+
114
+ end
@@ -0,0 +1,70 @@
1
+ module Blabs
2
+ class BlogPost < ActiveRecord::Base
3
+ self.table_name_prefix = 'blabs_'
4
+
5
+ validates :title, presence: true
6
+ validates :body, presence: true
7
+ validates :author, presence: true
8
+ validates :path, presence: true, uniqueness: true
9
+
10
+ serialize :extra_attributes, Hash
11
+
12
+ before_validation :set_path
13
+ before_validation :set_meta_title
14
+
15
+ has_many :taggings, class_name: "Blabs::Tagging", foreign_key: "blabs_blog_post_id"
16
+ has_many :tags, class_name: "Blabs::Tag", through: :taggings, foreign_key: "blabs_tag_id"
17
+
18
+ def self.tagged_with(name)
19
+ Tag.find_by_name!(name).blog_posts
20
+ end
21
+
22
+ def to_param
23
+ path
24
+ end
25
+
26
+ def publish!
27
+ update_attributes(published: true, published_at: Time.now) unless published?
28
+ end
29
+
30
+ def unpublish!
31
+ update_attributes(published: false, published_at: nil) if published?
32
+ end
33
+
34
+ def toggle_publish!(value)
35
+ value ? publish! : unpublish!
36
+ end
37
+
38
+ def post_image_url
39
+ (Blabs.post_image_url_attribute && extra_attributes[Blabs.post_image_url_attribute]) || begin
40
+ find_link_regex = /\[([^\[]+)\]\(([^\)]+)\)/
41
+ body.match(find_link_regex) && $2
42
+ end
43
+ end
44
+
45
+ def author_twitter_username
46
+ username = self[:author_twitter_username]
47
+ username.blank? ? "" : "@#{username}".squeeze("@")
48
+ end
49
+
50
+ def tag_list=(names)
51
+ self.tags = names.split(",").map do |n|
52
+ Blabs::Tag.where(name: n.strip).first_or_create!
53
+ end
54
+ end
55
+
56
+ def tag_list
57
+ self.tags.collect {|t| t.name }.join(", ")
58
+ end
59
+
60
+ private
61
+
62
+ def set_path
63
+ self.path = self.title.parameterize if self.path.blank? && self.title.present?
64
+ end
65
+
66
+ def set_meta_title
67
+ self.meta_title = self.title if self.meta_title.blank?
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,4 @@
1
+ class Blabs::Image < ActiveRecord::Base
2
+ mount_uploader :image, ImageUploader
3
+ self.table_name_prefix = 'blabs_'
4
+ end
@@ -0,0 +1,9 @@
1
+ module Blabs
2
+ class Tag < ActiveRecord::Base
3
+ self.table_name_prefix = 'blabs_'
4
+
5
+ has_many :taggings, class_name: "Blabs::Tagging", foreign_key: "blabs_tag_id"
6
+ has_many :blog_posts, through: :taggings, class_name: "Blabs::BlogPost"
7
+
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Blabs
2
+ class Tagging < ActiveRecord::Base
3
+ self.table_name_prefix = 'blabs_'
4
+
5
+ belongs_to :tag, class_name: "Blabs::Tag", foreign_key: "blabs_tag_id"
6
+ belongs_to :blog_post, class_name: "Blabs::BlogPost", foreign_key: "blabs_blog_post_id"
7
+
8
+ end
9
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ require 'carrierwave/processing/mime_types'
3
+
4
+ class ImageUploader < CarrierWave::Uploader::Base
5
+
6
+ # Include RMagick or MiniMagick support:
7
+ # include CarrierWave::RMagick
8
+ include CarrierWave::MiniMagick
9
+ include CarrierWave::MimeTypes
10
+
11
+ # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
12
+ # include Sprockets::Helpers::RailsHelper
13
+ # include Sprockets::Helpers::IsolatedHelper
14
+
15
+ # Choose what kind of storage to use for this uploader:
16
+ if Rails.env.production?
17
+ storage :fog
18
+ fog_credentials Blabs.fog_credentials
19
+ fog_directory Blabs.fog_directory
20
+ else
21
+ storage :file
22
+ end
23
+
24
+
25
+ # Override the directory where uploaded files will be stored.
26
+ # This is a sensible default for uploaders that are meant to be mounted:
27
+ def store_dir
28
+ "uploads/#{model.class.to_s.underscore}/#{model.id}"
29
+ end
30
+
31
+ # Provide a default URL as a default if there hasn't been a file uploaded:
32
+ # def default_url
33
+ # # For Rails 3.1+ asset pipeline compatibility:
34
+ # # asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
35
+ #
36
+ # "/images/fallback/" + [version_name, "default.png"].compact.join('_')
37
+ # end
38
+
39
+ # Process files as they are uploaded:
40
+ # process :scale => [200, 300]
41
+ #
42
+ # def scale(width, height)
43
+ # # do something
44
+ # end
45
+
46
+ process :set_content_type
47
+
48
+ # Create different versions of your uploaded files:
49
+ version :thumb do
50
+ process :resize_to_fit => [60, 60]
51
+ end
52
+
53
+ # Add a white list of extensions which are allowed to be uploaded.
54
+ # For images you might use something like this:
55
+ # def extension_white_list
56
+ # %w(jpg jpeg gif png)
57
+ # end
58
+
59
+ # Override the filename of the uploaded files:
60
+ # Avoid using model.id or version_name here, see uploader/store.rb for details.
61
+ # def filename
62
+ # "something.jpg" if original_filename
63
+ # end
64
+
65
+ end
@@ -0,0 +1,54 @@
1
+ =form_for(@blog_post) do |f|
2
+ .col-lg-8
3
+ .form-group
4
+ = f.label :title
5
+ = f.text_field :title, class: "form-control", placeholder: "Give your blog post an interesting title"
6
+ = field_error(f, @blog_post, :title)
7
+ .row
8
+ .col-lg-6
9
+ .form-group
10
+ = f.label :author
11
+ = f.text_field :author, class: "form-control", placeholder: "You need to put your name in"
12
+ = field_error(f, @blog_post, :author)
13
+ .col-lg-6
14
+ .form-group
15
+ = f.label :author_twitter_username
16
+ = f.text_field :author_twitter_username, class: "form-control", placeholder: "Your twitter name (for the twitter summary card)"
17
+ = field_error(f, @blog_post, :author_twitter_username)
18
+ .form-group
19
+ = f.label :body
20
+ %p.pull-right= link_to("Markdown syntax (new window)", "http://daringfireball.net/projects/markdown/syntax", target: :window, tabIndex: -1)
21
+ = f.text_area :body, class: "form-control", placeholder: "Use markdown to write your blog post", rows: 25
22
+ = field_error(f, @blog_post, :body)
23
+ .form-group
24
+ = f.label :extract
25
+ %p.pull-right Leave blank to use the default extract
26
+ = f.text_area :extract, class: "form-control", placeholder: "Use markdown to write your extract", rows: 2
27
+ = field_error(f, @blog_post, :extract)
28
+ .form-group
29
+ = f.submit "Save (unpublished)", class: "btn btn-default"
30
+ = f.submit "Save and publish", class: "btn btn-default btn-success", name: "publish"
31
+ = f.submit "Preview", class: "btn btn-default btn-info", name: "preview"
32
+ or
33
+ = link_to "cancel", "/blog", data: { confirm: "Are you sure?" }
34
+ .col-lg-4.panel.panel-info
35
+ .panel-heading For SEO nerds
36
+ .well.well-sm If you don't put anything in here, a sensible default will be applied
37
+ .form-group
38
+ = f.label :meta_title
39
+ = f.text_field :meta_title, class: "form-control", placeholder: "Meta title - for you SEO nerds (will default to the title)"
40
+ = field_error(f, @blog_post, :meta_title)
41
+ .form-group
42
+ = f.label :path
43
+ = f.text_field :path, class: "form-control", placeholder: "The url - (will be defaulted to something sensible)"
44
+ = field_error(f, @blog_post, :path)
45
+ .form-group
46
+ = f.label :tag_list
47
+ = f.text_field :tag_list, class: "form-control", placeholder: "Comma delimited list of tags"
48
+ - if Blabs.extra_attributes.any?
49
+ .col-lg-4.panel.panel-info
50
+ - Blabs.extra_attributes.each do |attribute|
51
+ .form-group
52
+ = label_tag "blabs_blog_post[extra_attributes][#{attribute}]", attribute.to_s.humanize
53
+ = text_field_tag "blabs_blog_post[extra_attributes][#{attribute}]", @blog_post.extra_attributes[attribute], class: "form-control"
54
+ =render "image_panel"
@@ -0,0 +1,5 @@
1
+ %li.list-group-item.image[image]
2
+ = link_to(image_tag(image.image_url(:thumb)), image.image_url) if image.image?
3
+ = link_to("Add", image.image_url, class: "add-to-blog")
4
+ |
5
+ = link_to("Delete", blabs_image_path(image), :method => :delete, data: { confirm: "Are you sure?" }, :remote => :true)
@@ -0,0 +1,32 @@
1
+ .col-lg-4.panel.panel-success
2
+ .panel-heading Images
3
+ .well.well-sm Upload an image, and then add it to the post with the add button
4
+ = fields_for Blabs::Image.new do |f|
5
+ = f.file_field :image, rel: blabs_images_path
6
+ %ul#images.list-group
7
+ = render partial: 'image', collection: @images
8
+
9
+ - key = Rails.application.config.session_options[:key]
10
+ = content_for :scripts do
11
+ :javascript
12
+ var upload_params = {};
13
+ var csrf_param = $('meta[name=csrf-param]').attr('content');
14
+ var csrf_token = $('meta[name=csrf-token]').attr('content');
15
+ upload_params[csrf_param] = encodeURI(encodeURIComponent(csrf_token));
16
+ upload_params['#{ key }'] = '#{ cookies[key] }';
17
+
18
+ var url = $('input#blabs_image_image').attr('rel');
19
+ $('input#blabs_image_image').uploadify({
20
+ 'swf' : '#{ image_path("uploadify.swf") }',
21
+ 'buttonText': "Upload image",
22
+ 'uploader' : url,
23
+ 'fileObjName' : 'blabs_image[image]',
24
+ 'fileTypeExts' : '*.png;*.jpg;*.gif',
25
+ 'multi' : true,
26
+ 'formData' : upload_params,
27
+ 'auto' : true,
28
+ 'onUploadSuccess' : function(file, data, response) {
29
+ var newImage = $('#images').prepend(data);
30
+ $(document).trigger('new_image', $(newImage).attr("id"));
31
+ }
32
+ });
@@ -0,0 +1,7 @@
1
+ %ul.breadcrumb
2
+ %li= link_to("All posts", "/blog")
3
+ %li.active= "Edit #{@blog_post.title}"
4
+
5
+ .content
6
+ =title("Edit #{@blog_post.title}")
7
+ =render partial: 'form'
@@ -0,0 +1,15 @@
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 formatted_body(post.body), type: 'html'
9
+ entry.updated(post.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ"))
10
+ entry.author do |author|
11
+ author.name post.author
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ - index_meta_tags
2
+
3
+ .blabs-blog
4
+ %nav.blabs-navigation
5
+ - if blabs_admin_required && @blog_posts.any?
6
+ =link_to("New Blog Post", new_blabs_blog_post_path)
7
+ -if @blog_posts.none?
8
+ %article
9
+ %header
10
+ %h2.title You don't have any blog posts.
11
+ %p.authorship #{link_to("Why don't you create the first!", new_blabs_blog_post_path)}
12
+
13
+ -else
14
+ -@blog_posts.each do |blog_post|
15
+ %article
16
+ %header
17
+ %h2.title=link_to(blog_post.title, pretty_blog_post_path(blog_post))
18
+ %p.authorship #{display_author(blog_post)} • #{pretty_date(blog_post.published_at) || pretty_date(blog_post.created_at)}
19
+ %hr
20
+ .content.post-body
21
+ - if @blog_posts.size == 1
22
+ = formatted_body(blog_post.body)
23
+ - else
24
+ = extract(blog_post)
25
+ - if blabs_admin_required
26
+ .blabs-admin-controls
27
+ = admin_controls(blog_post)
@@ -0,0 +1,7 @@
1
+ %ul.breadcrumb
2
+ %li= link_to("All posts", "/blog")
3
+ %li.active= "New blog post"
4
+
5
+ .content
6
+ =title("New blog post")
7
+ =render partial: 'form'
@@ -0,0 +1,16 @@
1
+ - content_for(:title, @blog_post.meta_title)
2
+ - show_meta_tags
3
+ - validate_layout
4
+ .blabs-blog
5
+ %nav.blabs-navigation
6
+ =link_to("< Back", "/blog")
7
+ %article
8
+ %header
9
+ %h1.title= @blog_post.title
10
+ %p.authorship #{display_author(@blog_post)} • #{pretty_date(@blog_post.published_at) || pretty_date(@blog_post.created_at)}
11
+
12
+ .content.post-body
13
+ = formatted_body(@blog_post.body)
14
+ - if blabs_admin_required
15
+ .blabs-admin-controls
16
+ = admin_controls(@blog_post)