ecrire 0.26.2 → 0.26.3

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/lib/ecrire/app/controllers/application_controller.rb +62 -7
  4. data/lib/ecrire/app/controllers/ecrire/theme_controller.rb +14 -15
  5. data/lib/ecrire/app/controllers/static_controller.rb +6 -1
  6. data/lib/ecrire/app/helpers/admin/posts_helper.rb +0 -1
  7. data/lib/ecrire/app/helpers/application_helper.rb +69 -19
  8. data/lib/ecrire/app/helpers/pagination_helper.rb +26 -0
  9. data/lib/ecrire/app/models/post.rb +77 -8
  10. data/lib/ecrire/app/views/admin/posts/titles/_title.html.erb +1 -1
  11. data/lib/ecrire/application.rb +26 -0
  12. data/lib/ecrire/config/environment.rb +3 -1
  13. data/lib/ecrire/onboarding/views/layouts/application.html.erb +4 -3
  14. data/lib/ecrire/theme/engine.rb +77 -17
  15. data/lib/ecrire/theme/template/assets/stylesheets/browser.css.scss +1 -0
  16. data/lib/ecrire/theme/template/assets/stylesheets/mobile.css.scss +1 -0
  17. data/lib/ecrire/theme/template/assets/stylesheets/tablet.css.scss +1 -0
  18. data/lib/ecrire/theme/template/controllers/posts_controller.rb +8 -1
  19. data/lib/ecrire/theme/template/views/layouts/application.html.erb +6 -12
  20. data/lib/ecrire/version.rb +1 -1
  21. data/lib/ecrire.rb +4 -0
  22. data/test/editor/models/post_test.rb +12 -0
  23. data/test/theme/helpers/application_helper_test.rb +31 -0
  24. metadata +5 -18
  25. data/lib/ecrire/app/controllers/admin/partials_controller.rb +0 -43
  26. data/lib/ecrire/app/controllers/admin/properties_controller.rb +0 -35
  27. data/lib/ecrire/app/controllers/images_controller.rb +0 -2
  28. data/lib/ecrire/app/controllers/partials_controller.rb +0 -8
  29. data/lib/ecrire/app/helpers/admin/labels_helper.rb +0 -26
  30. data/lib/ecrire/app/helpers/open_graph_helper.rb +0 -45
  31. data/lib/ecrire/app/helpers/posts_helper.rb +0 -13
  32. data/lib/ecrire/app/models/admin/partial.rb +0 -4
  33. data/lib/ecrire/app/models/concerns/.keep +0 -0
  34. data/lib/ecrire/app/models/partial.rb +0 -3
  35. data/lib/ecrire/app/models/property/image.rb +0 -30
  36. data/lib/ecrire/app/models/property/label.rb +0 -28
  37. data/lib/ecrire/theme/template/assets/javascripts/theme.js.coffee +0 -1
  38. data/lib/ecrire/theme/template/secrets.yml +0 -11
  39. /data/lib/ecrire/app/assets/javascripts/{application.js → ecrire.js} +0 -0
  40. /data/lib/ecrire/app/assets/stylesheets/{application.css.scss → ecrire.css.scss} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ce26477b0912ec3e5e72cdfe31cd7ebaf91367b
4
- data.tar.gz: 7cfef2c63d03495c04cc90b97adc668bc653266a
3
+ metadata.gz: 42ebfbcd7b234a8ed0eced636045769471049b42
4
+ data.tar.gz: fcf2461eb398ad52f848e08f4be8d2a8975ef354
5
5
  SHA512:
6
- metadata.gz: bee627694b0efe4e26e5b1f23516714635f9fecbade81d20fa8c628c295a8fde2ce271869106608d345c2a5edabed12d00366088bea9cb39220ee7038a4a67eb
7
- data.tar.gz: d34158b4a0a0ec3541afa55a2cf5cb222c20e12af7543725d59b5a9c819524704a54db7c18c97029e5ecb456e0bb9871754b46f9e742825a56762af363a9d5db
6
+ metadata.gz: 5fc4670039672091e34e0055e400063b5715c95f39375efc770f528d32d09435aff6cccc141be01e901d99cd9ba40aa05c4aebbc4ba9c76e04048eceace9c07d
7
+ data.tar.gz: 633ca19d3ad1b9d88a3da8f8419c737cd89b0750ea423b19a92de5593f98c0ce48668b523db7b2ccf04d3afc609e33411caa3b9eb47ff83bd3ec407e42d62d4f
data/.gitignore CHANGED
@@ -18,4 +18,4 @@ tmp
18
18
  .DS_Store
19
19
  Gemfile.lock
20
20
 
21
- /lib/ecrire/template/secrets.yml
21
+ secrets.yml
@@ -1,17 +1,73 @@
1
+ ##
2
+ # Base controller for every controller in Ecrire (Theme & Admin)
3
+ # Ecrire::ThemeController inherits from this class so there are no reason why
4
+ # you should inherit from this class.
5
+ #
6
+ # The controller handles user authentication, and CSRF protection.
7
+ #
8
+ # It also provides a +url+ method to build complex URL using a
9
+ # very light syntax.
10
+ #
1
11
  class ApplicationController < ::ActionController::Base
2
12
  protect_from_forgery with: :exception
3
13
 
4
14
  helper_method :current_user, :signed_in?
5
15
  helper_method :warden, :post_path, :url
6
16
 
17
+ ##
18
+ # Return current signed +user+ or nil, if the user is not
19
+ # signed in
7
20
  def current_user
8
21
  warden.user
9
22
  end
10
23
 
24
+ ##
25
+ # Returns +true+ if the user is signed in
11
26
  def signed_in?
12
27
  !warden.user.nil?
13
28
  end
14
29
 
30
+
31
+ ##
32
+ # Returns a URL based on the path and options provided.
33
+ # It does not try to validate the URL, it only generates it and assume
34
+ # it's a valid URL.
35
+ #
36
+ # +path+: The relative path of the URL you want to build.
37
+ #
38
+ # +options+: Hash containing options for rendering the url.
39
+ #
40
+ #
41
+ # The +path+ and +options+ are linked together via a mapping construct
42
+ # that you can use to map records to part of the URL.
43
+ #
44
+ # To map records to the +path+, you need to specify the record & the method you want to use inside the path.
45
+ #
46
+ # url('/admin/posts/:posts.id', post: @post)
47
+ # -> '/admin/posts/32'
48
+ #
49
+ # The method looks for every occurence of ":[key].[method]" and will look in +options+ for that key and call the given method on that key.
50
+ #
51
+ # This means the object can be anything as long as it handles the method.
52
+ #
53
+ # Here are a other examples:
54
+ #
55
+ # url('/users/:user.id/tags/:tag.id/', user: @user, tag: @tag)
56
+ # -> '/users/12/tags/12'
57
+ #
58
+ # url('/users')
59
+ # -> '/users'
60
+ #
61
+ # The +options+ also looks for the +absolute_path+ key. If it's set to +true+, the
62
+ # method will return an absolute path.
63
+ #
64
+ # url('/users/:user.id/tags/:tag.id/', user: @user, tag: @tag, absolute_path: true)
65
+ # -> 'http://localhost:3000/users/12/tags/12'
66
+ #
67
+ # url('/users', absolute_path: true)
68
+ # -> 'http://localhost:3000/users'
69
+ #
70
+
15
71
  def url(path, options = {})
16
72
  records = options.with_indifferent_access
17
73
  regex = /(:([a-z]+)\.([a-z]+))/i
@@ -19,19 +75,18 @@ class ApplicationController < ::ActionController::Base
19
75
  records[$2].send($3)
20
76
  end
21
77
 
22
- if options.delete(:full_path)
23
- options[:path] = path
24
- options[:host] ||= request.host
25
- options[:protocol] ||= request.protocol
26
- options[:port] ||= request.port
78
+ if options.delete(:absolute_path)
79
+ options[:path] = path
80
+ options[:host] ||= request.host
81
+ options[:protocol] ||= request.protocol
82
+ options[:port] ||= request.port
27
83
  ActionDispatch::Http::URL.full_url_for(options)
28
84
  else
29
85
  path
30
86
  end
31
87
  end
32
88
 
33
-
34
- protected
89
+ private
35
90
 
36
91
  def authenticate!
37
92
  warden.authenticate!
@@ -1,4 +1,16 @@
1
1
  module Ecrire
2
+ ##
3
+ # The class any controller in a theme needs to inherit from
4
+ #
5
+ # +ThemeController+ provides boilerplate code so the blog handles a few cases
6
+ # for you.
7
+ #
8
+ # Ecrire will try to redirect to the homepage when it meets selected exceptions
9
+ # Currently, the following 3 exceptions are handled:
10
+ # - ActiveRecord::RecordNotFound
11
+ # - ActionController::RoutingError
12
+ # - ActionView::ActionViewError
13
+ #
2
14
  class ThemeController < ::ApplicationController
3
15
 
4
16
  unless Rails.env.development?
@@ -7,23 +19,10 @@ module Ecrire
7
19
  rescue_from ActionView::ActionViewError, with: :redirect_home
8
20
  end
9
21
 
10
- before_action :pagination, only: :index
11
- protect_from_forgery except: :index
12
-
13
- def index
14
- respond_to do |format|
15
- format.html
16
- format.rss
17
- format.json do
18
- headers['Access-Control-Allow-Origin'] = '*'
19
- end
20
- end
21
- end
22
22
 
23
- def show
24
- end
23
+ before_action :pagination, only: :index
25
24
 
26
- protected
25
+ private
27
26
 
28
27
  def pagination
29
28
  params[:per] ||= 10
@@ -1,5 +1,10 @@
1
+ ##
2
+ # The Controller that renders static pages
3
+ #
4
+ # It looks for a a template matching the path provided.
5
+ #
1
6
  class StaticController < Ecrire::ThemeController
2
7
  def show
3
- render params[:view]
8
+ render "static/#{params[:view].to_s}"
4
9
  end
5
10
  end
@@ -1,6 +1,5 @@
1
1
  module Admin
2
2
  module PostsHelper
3
- include ::PostsHelper
4
3
 
5
4
  def post_tags(post)
6
5
  tags = post.tags.map do |tag|
@@ -1,13 +1,35 @@
1
+ ##
2
+ # +ApplicationHelper+ provides functions to help build a theme's layout
3
+ #
1
4
  module ApplicationHelper
5
+
6
+ ##
7
+ # Render the admin navigation bar if a user is signed in.
8
+ #
9
+ # You don't have to check if the user is signed in before using this method
10
+ # Ecrire will check this automatically
11
+ #
2
12
  def admin_navigation
3
13
  return unless signed_in?
4
14
  render 'sessions/navigation'
5
15
  end
6
16
 
17
+ ##
18
+ # Render <title> tag
19
+ #
20
+ # The content of the title tag can be one of three things.
21
+ # In priority of order:
22
+ # 1. The value of content_for(:title) if it's set,
23
+ # 2. The title of the variable @post, if it's set,
24
+ # 3. The +title+ passed
25
+ #
26
+ # If you need more information about content_for and how to use it, please read:
27
+ # http://ecrire.io/posts/configure-layout-element-with-content_for
28
+ #
7
29
  def title_tag(title = 'Ecrire')
8
30
  content_tag :title do
9
- if block_given?
10
- yield
31
+ if content_for?(:title)
32
+ content_for(:title)
11
33
  elsif !@post.nil?
12
34
  @post.title
13
35
  else
@@ -16,28 +38,56 @@ module ApplicationHelper
16
38
  end
17
39
  end
18
40
 
19
- def meta_informations_tags
20
- [
21
- content_tag(:link, nil, rel: 'alternate', type: 'application/rss+xml', title: 'RSS', href: '/feed'),
22
- content_tag(:link, nil, rel: %w(shortcut icon), href: asset_url('favicon.ico')),
23
- csrf_meta_tags
24
- ].join.html_safe
41
+ ##
42
+ # Render a RSS auto-discovery tag
43
+ #
44
+ # You can pass another relative path if you want.
45
+ # Ecrire will render an absolute path using the +relative_path+
46
+ #
47
+ def rss_tag(relative_path = '/feed')
48
+ content_tag :link, nil, rel: 'alternate', type: 'application/rss+xml', title: 'RSS', href: url(relative_path, absolute_path: true)
25
49
  end
26
50
 
27
- def description_meta_tag
28
- if Rails.application.secrets.fetch(:meta, {}).has_key?(:description)
29
- content_tag :meta, nil, name: 'description', content: Rails.application.secrets[:meta][:description]
30
- end
31
- end
32
51
 
33
- def open_graph_type
34
- if @post.nil?
35
- 'website'
36
- else
37
- 'article'
38
- end
52
+ ##
53
+ # Render the favicon tag
54
+ #
55
+ # Will generate the asset url given with the +name+ of your favicon.
56
+ #
57
+ def favicon_tag(name = 'favicon.ico')
58
+ content_tag :link, nil, rel: %w(shortcut icon), href: asset_url(name)
39
59
  end
40
60
 
61
+ ##
62
+ # Render the <main> tag
63
+ #
64
+ # The +html_options+ is a hash that will map to key/value for the tag.
65
+ # Unless you know what you are doing, you should not specify an id in +html_options+.
66
+ # Ecrire will generate one using the controller and action for the given request.
67
+ #
68
+ # You can also provide any key/value that you wish to see rendered in the main tag.
69
+ #
70
+ # Example with posts#index (/posts):
71
+ #
72
+ # <%= main_tag contentEditable: true do %>
73
+ # Hello world!
74
+ # <% end %>
75
+ #
76
+ # <main contentEditable=true id='PostsIndex'>
77
+ # Hello world!
78
+ # </main>
79
+ #
80
+ # Another example with posts#index (/posts):
81
+ #
82
+ # <%= main_tag class: 'content' do %>
83
+ # Hello World!
84
+ # <% end %>
85
+ #
86
+ # <main class='content' id='PostsIndex'>
87
+ # Hello World!
88
+ # </main>
89
+ #
90
+ #
41
91
  def main_tag(html_options = {}, &block)
42
92
  html_options[:id] ||= [controller_name, action_name].map(&:capitalize).join
43
93
  html_options[:class] = [html_options[:class]].compact.flatten
@@ -0,0 +1,26 @@
1
+ ##
2
+ # This module overloads the +paginate+ method defined
3
+ # by Kaminari to force kaminari to use the routes provided by the theme.
4
+ #
5
+ # This might not be needed when Kaminari reaches 1.0 per https://github.com/amatsuda/kaminari/pull/636
6
+ #
7
+ module PaginationHelper
8
+
9
+ ##
10
+ # Returns pagination for the given scope(record).
11
+ #
12
+ # The argument list is the same as the one declared by kaminari
13
+ # to keep the expected behavior when calling ```super```
14
+ #
15
+ # Example:
16
+ #
17
+ # ``` erb
18
+ # <%= paginate(@posts) %>
19
+ # ```
20
+ #
21
+ def paginate(scope, options = {}, &block)
22
+ _with_routes Ecrire::Theme::Engine.routes do
23
+ super
24
+ end
25
+ end
26
+ end
@@ -1,11 +1,28 @@
1
1
  require 'nokogiri'
2
2
 
3
+ ##
4
+ # Posts written by users stored in database
5
+ #
6
+ # == Available scopes
7
+ #
8
+ # Scopes are builtin methods to facilitate retrieving posts
9
+ # from the database. Scopes can also be chained to narrow your query
10
+ # even more.
11
+ #
12
+ # +published+: Return posts that are published
13
+ #
14
+ # +drafted+: Return posts that are NOT published
15
+ #
16
+ # +search_by_title+: Return all post that have a title that match
17
+ # the key you provide
18
+ #
19
+ #
3
20
  class Post < ActiveRecord::Base
4
21
  has_one :header, class_name: Image
5
22
  has_many :titles, -> { order "titles.created_at DESC" }
6
23
 
7
- store_accessor :properties, :labels
8
-
24
+ scope :published, lambda { status("published") }
25
+ scope :drafted, lambda { status("drafted") }
9
26
  scope :status, lambda {|status|
10
27
  if status.eql?("published")
11
28
  where "posts.published_at IS NOT NULL"
@@ -14,8 +31,11 @@ class Post < ActiveRecord::Base
14
31
  end
15
32
  }
16
33
 
17
- scope :published, lambda { status("published") }
18
- scope :drafted, lambda { status("drafted") }
34
+ scope :search_by_title, lambda {|title|
35
+ ids = Title.search_by_name(title).map(&:post_id).compact
36
+ Post.includes(:titles).where('posts.id in (?)', ids)
37
+ }
38
+
19
39
  scope :without, lambda { |post| where("posts.id != ?", post.id) }
20
40
 
21
41
  validates :titles, length: {minimum: 1}
@@ -24,6 +44,7 @@ class Post < ActiveRecord::Base
24
44
 
25
45
  attr_writer :tags
26
46
 
47
+ #:nodoc:
27
48
  def title=(new_title)
28
49
  if self.published?
29
50
  self.titles.new(name: new_title)
@@ -34,54 +55,102 @@ class Post < ActiveRecord::Base
34
55
  end
35
56
  end
36
57
 
58
+ ##
59
+ # Return the current title for this post
60
+ #
37
61
  def title
38
62
  (self.titles.first || self.titles.new).name
39
63
  end
40
64
 
65
+ ##
66
+ # Return the slug link to the current title
67
+ #
68
+ def slug
69
+ self.titles.first.slug
70
+ end
71
+
72
+ ##
73
+ # Returns the year this post was published
74
+ #
41
75
  def year
42
76
  published_at.year
43
77
  end
44
78
 
79
+ ##
80
+ # Returns the month this post was published
81
+ #
45
82
  def month
46
83
  published_at.month
47
84
  end
48
85
 
49
- def slug
50
- self.titles.first.slug
51
- end
52
-
86
+ #:nodoc:
53
87
  def status=(new_status)
54
88
  if new_status.eql? "publish"
55
89
  publish!
56
90
  end
57
91
  end
58
92
 
93
+ ##
94
+ # Publish this post if it is not yet published
95
+ #
96
+ # Update the database with the publish date
97
+ #
59
98
  def publish!
60
99
  return unless published_at.nil?
61
100
  self.published_at = DateTime.now
62
101
  save!
63
102
  end
64
103
 
104
+ ##
105
+ # Returns whether this post is published or not
106
+ #
65
107
  def published?
66
108
  !draft?
67
109
  end
68
110
 
111
+ ##
112
+ # The opposite of +published?+
113
+ #
69
114
  def draft?
70
115
  published_at.nil?
71
116
  end
72
117
 
118
+ ##
119
+ # Returns the content of this post. Depending on the current status
120
+ # of this post, it can be one of three things:
121
+ #
122
+ # - Compiled content;
123
+ # - Markdown content;
124
+ # - Empty string.
125
+ #
126
+ # This method should be used when trying to render the body of a post
127
+ # to a page in HTML.
128
+ #
73
129
  def content
74
130
  (self.compiled_content || super || '').html_safe
75
131
  end
76
132
 
133
+ ##
134
+ # Returns the compiled excerpt for this post.
135
+ # The excerpt is based on content but only returns text.
136
+ #
137
+ # The excerpt is parsed from the content and it also
138
+ # filters out images and header.
139
+ #
77
140
  def excerpt
78
141
  (self.compiled_excerpt || "").html_safe
79
142
  end
80
143
 
144
+ ##
145
+ # Returns true if an image header was set for this post
146
+ #
81
147
  def header?
82
148
  !self.header.nil? && !self.header.url.blank?
83
149
  end
84
150
 
151
+ ##
152
+ # Returns the list of Tags link set for this post
153
+ #
85
154
  def tags
86
155
  @tags ||= Tag.where("tags.id in (?)", super || [])
87
156
  end
@@ -1,5 +1,5 @@
1
1
  <% post = Post.new(titles: [title], published_at: title.post.published_at) %>
2
2
  <li>
3
3
  <%= content_tag :p, title.name %>
4
- <%= link_to url(Ecrire::Theme::Engine.post_path, post: post, full_path: true), url(Ecrire::Theme::Engine.post_path, post: post, full_path: true) %>
4
+ <%= link_to url(Ecrire::Theme::Engine.post_path, post: post, absolute_path: true), url(Ecrire::Theme::Engine.post_path, post: post, absolute_path: true) %>
5
5
  </li>
@@ -6,6 +6,21 @@ require 'observejs'
6
6
  require 'pg_search'
7
7
 
8
8
  module Ecrire
9
+ ##
10
+ # Ecrire::Application is the entry point when running
11
+ # a blog.
12
+ #
13
+ # The big difference between this application and a normal Rails
14
+ # application is that Ecrire will look for +secrets.yml+ in the
15
+ # current working directory.
16
+ #
17
+ # If it doesn't find one, it will load the Onboarding process
18
+ # so the user can configure the database and the first user.
19
+ #
20
+ # If the application finds +secrets.yml+, it will load the Theme which is located
21
+ # in the current working directory.
22
+ #
23
+ #
9
24
  class Application < Rails::Application
10
25
  require 'ecrire/config/environment'
11
26
 
@@ -23,6 +38,13 @@ module Ecrire
23
38
  require 'ecrire/onboarding/engine'
24
39
  end
25
40
 
41
+ ##
42
+ # Return paths based off Rails default plus some customization.
43
+ #
44
+ # These paths are Ecrire's, not the users's theme.
45
+ #
46
+ # For the user's paths, look at Ecrire::Theme::Engine.paths
47
+ #
26
48
  def paths
27
49
  @paths ||= begin
28
50
  paths = super
@@ -32,6 +54,10 @@ module Ecrire
32
54
  end
33
55
  end
34
56
 
57
+ ##
58
+ # Returns true if Ecrire::Onboarding::Engine is loaded
59
+ # in the application runtime
60
+ #
35
61
  def self.onboarding?
36
62
  defined?(Ecrire::Onboarding::Engine)
37
63
  end
@@ -22,7 +22,9 @@ Ecrire::Application.configure do
22
22
  lambda do |filename, path|
23
23
  path =~ /assets/ && !%w(.js .css).include?(File.extname(filename))
24
24
  end,
25
- /^(admin|application)\.(css|js)$/
25
+ /^(admin|ecrire)\.(css|js)$/,
26
+ /^(application).(js|coffee)$/,
27
+ /^(browser|tablet|mobile).(css|scss)$/
26
28
  ]
27
29
 
28
30
  Warden::Manager.serialize_into_session do |user|
@@ -3,12 +3,13 @@
3
3
 
4
4
  <head>
5
5
  <%= title_tag 'Before you start blogging...' %>
6
- <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
7
- <%= javascript_include_tag "application", "base", "data-turbolinks-track" => true %>
6
+ <%= stylesheet_link_tag "ecrire", media: "all", "data-turbolinks-track" => true %>
7
+ <%= javascript_include_tag "ecrire", "base", "data-turbolinks-track" => true %>
8
8
 
9
9
  <%= stylesheet_link_tag "theme", media: "all", "data-turbolinks-track" => true %>
10
10
  <%= javascript_include_tag "theme", "base", "data-turbolinks-track" => true %>
11
- <%= meta_informations_tags %>
11
+ <%= csrf_meta_tags %>
12
+ <%= favicon_tag %>
12
13
  </head>
13
14
 
14
15
  <body>
@@ -1,25 +1,47 @@
1
1
  module Ecrire
2
+ ##
3
+ # Theme module links the user's theme with
4
+ # Ecrire::Application. It uses Rails::Engine to hook
5
+ # itself up and split the codebase between the Gem and the user's
6
+ # Theme.
7
+ #
2
8
  module Theme
3
- class Engine < Rails::Engine
4
- attr_accessor :post_path
5
-
6
- initializer 'ecrire.logs', before: :initialize_logger do |app|
7
- unless Rails.env.test?
8
- app.paths.add "log", with: "log/#{Rails.env}.log"
9
- end
10
- end
11
9
 
12
- initializer 'ecrire.load_paths', before: :bootstrap_hook do |app|
13
- ActiveSupport::Dependencies.autoload_paths.unshift(*self.paths.autoload_paths)
14
- ActiveSupport::Dependencies.autoload_once_paths.unshift(*self.paths.autoload_once)
15
- end
10
+ ##
11
+ # Engine is the foundation of Rails. It can be used
12
+ # to encapsulate part of the application and this is
13
+ # exactly what it does here.
14
+ #
15
+ # Ecrire::Theme::Engine is the block that hooks into Ecrire::Application
16
+ # to provide Theme support.
17
+ #
18
+ # This class is the only element that the Gem includes at runtime.
19
+ #
20
+ # Everything else is defined in the user's theme folder.
21
+ #
22
+ # This engine is the reason why it's possible to have
23
+ # Ecrire as a gem and theme as user's folder.
24
+ #
25
+ class Engine < Rails::Engine
16
26
 
17
- initializer 'ecrire.append_paths', before: :set_autoload_paths do |app|
18
- app.config.eager_load_paths.unshift *paths.eager_load
19
- app.config.autoload_once_paths.unshift *paths.autoload_once
20
- app.config.autoload_paths.unshift *paths.autoload_paths
21
- end
27
+ ##
28
+ # +post_path+ needs to be defined so Ecrire can link from the admin
29
+ # to a post. This is also needed when listing all the titles a post has
30
+ # and the URL they represent.
31
+ #
32
+ attr_accessor :post_path
22
33
 
34
+ ##
35
+ # Return paths for a theme. The paths
36
+ # follow the structure of the default theme.
37
+ #
38
+ # It creates a new Rails::Paths because
39
+ # it's highly customized and it was less readable to
40
+ # disable and changes every paths.
41
+ #
42
+ # This could be modified in the user's theme.
43
+ #
44
+ #
23
45
  def paths
24
46
  @paths ||= begin
25
47
  paths = Rails::Paths::Root.new(root_path)
@@ -42,10 +64,30 @@ module Ecrire
42
64
  end
43
65
  end
44
66
 
67
+ ##
68
+ # Disables migration for now.
69
+ # Any Rails::Engine instance can support migrations
70
+ # which means that Theme could have their own
71
+ # models.
72
+ #
73
+ # It's likely that at some point this behavior is resumed but
74
+ # I want to make sure that I understand the implication before
75
+ # turning this back on.
76
+ #
77
+ # For example, I would like to make sure that the Admin is shelled from
78
+ # those future migrations.
45
79
  def has_migrations?
46
80
  false
47
81
  end
48
82
 
83
+ ##
84
+ # Return the root_path for the current theme
85
+ #
86
+ # The method starts at the current working directory and moves from parent
87
+ # to parent until it either finds +config.ru+ or it reaches the root.
88
+ #
89
+ # Raise an error if it reaches the root and can't find +config.ru+.
90
+ #
49
91
  def root_path(file = 'config.ru')
50
92
  begin
51
93
  pathname = Pathname.pwd
@@ -62,6 +104,24 @@ module Ecrire
62
104
  end
63
105
  end
64
106
 
107
+ initializer 'ecrire.logs', before: :initialize_logger do |app|
108
+ unless Rails.env.test?
109
+ app.paths.add "log", with: "log/#{Rails.env}.log"
110
+ end
111
+ end
112
+
113
+ initializer 'ecrire.load_paths', before: :bootstrap_hook do |app|
114
+ ActiveSupport::Dependencies.autoload_paths.unshift(*self.paths.autoload_paths)
115
+ ActiveSupport::Dependencies.autoload_once_paths.unshift(*self.paths.autoload_once)
116
+ end
117
+
118
+ initializer 'ecrire.append_paths', before: :set_autoload_paths do |app|
119
+ app.config.eager_load_paths.unshift *paths.eager_load
120
+ app.config.autoload_once_paths.unshift *paths.autoload_once
121
+ app.config.autoload_paths.unshift *paths.autoload_paths
122
+ end
123
+
124
+
65
125
  end
66
126
  end
67
127
  end
@@ -1,3 +1,4 @@
1
+ @import 'ecrire';
1
2
  @import 'bourbon';
2
3
  @import 'variables';
3
4
  @import 'shared/**/*';
@@ -1,3 +1,4 @@
1
+ @import 'ecrire';
1
2
  @import 'bourbon';
2
3
  @import 'variables';
3
4
  @import 'shared/**/*';
@@ -1,3 +1,4 @@
1
+ @import 'ecrire';
1
2
  @import 'bourbon';
2
3
  @import 'variables';
3
4
  @import 'shared/**/*';
@@ -8,7 +8,14 @@ class PostsController < Ecrire::ThemeController
8
8
  @posts = @posts.where.not(id: @latest.id)
9
9
  end
10
10
  @tags = Tag.all
11
- super
11
+
12
+ respond_to do |format|
13
+ format.html
14
+ format.rss
15
+ format.json do
16
+ headers['Access-Control-Allow-Origin'] = '*'
17
+ end
18
+ end
12
19
  end
13
20
 
14
21
  def show
@@ -3,14 +3,17 @@
3
3
 
4
4
  <head>
5
5
  <%= title_tag %>
6
- <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
7
- <%= javascript_include_tag "application", "base", "data-turbolinks-track" => true %>
6
+
7
+ <%= javascript_include_tag "ecrire", "application", "data-turbolinks-track" => true %>
8
8
 
9
9
  <%= stylesheet_link_tag "browser", media: "(min-width: 1025px)", "data-turbolinks-track" => true %>
10
10
  <%= stylesheet_link_tag "tablet", media: "(min-width: 641px) and (max-width: 1024px)", "data-turbolinks-track" => true %>
11
11
  <%= stylesheet_link_tag "mobile", media: "(max-width: 640px)", "data-turbolinks-track" => true %>
12
12
 
13
- <%= meta_informations_tags %>
13
+ <%= csrf_meta_tags %>
14
+
15
+ <%= favicon_tag %>
16
+ <%= rss_tag %>
14
17
  </head>
15
18
 
16
19
  <body>
@@ -31,21 +34,12 @@
31
34
  </ol>
32
35
  </nav>
33
36
 
34
-
35
- <% if content_for?(:header) %>
36
- <div class='content'>
37
- <%= content_for :header %>
38
- </div>
39
- <% end %>
40
-
41
37
  </header>
42
38
 
43
39
  <article>
44
40
  <%= yield %>
45
41
  </article>
46
42
 
47
- <footer>
48
- </footer>
49
43
  <% end %>
50
44
  </body>
51
45
  </html>
@@ -1,3 +1,3 @@
1
1
  module Ecrire
2
- VERSION = '0.26.2'
2
+ VERSION = '0.26.3'
3
3
  end
data/lib/ecrire.rb CHANGED
@@ -3,6 +3,10 @@ module Ecrire
3
3
  autoload :Application, 'ecrire/application'
4
4
  autoload :Markdown, 'ecrire/markdown'
5
5
 
6
+ ##
7
+ # Returns true if Ecrire could find
8
+ # a Gemfile in the current working directory
9
+ #
6
10
  def self.bundle?
7
11
  ENV['BUNDLE_GEMFILE'] ||= Dir.pwd + '/Gemfile'
8
12
  File.exists?(ENV['BUNDLE_GEMFILE'])
@@ -22,6 +22,18 @@ class PostTest < ActiveSupport::TestCase
22
22
  assert post.read_attribute(:tags).include?(tag.id)
23
23
  end
24
24
 
25
+ test 'find post by title' do
26
+ keyword = 'title'
27
+
28
+ posts = Title.search_by_name(keyword).map(&:post).uniq
29
+
30
+ assert posts.count == Post.search_by_title(keyword).count
31
+
32
+ Post.search_by_title(keyword).each do |post|
33
+ assert posts.include?(post)
34
+ end
35
+ end
36
+
25
37
  test "fetch published post" do
26
38
  @posts = Post.status("published")
27
39
  assert_equal @posts.count, Post.published.count
@@ -25,6 +25,37 @@ class ApplicationHelperTest < ActionView::TestCase
25
25
  assert_select node(html), "#TestIndex"
26
26
  end
27
27
 
28
+ test 'title_tag should always render the tag' do
29
+ html = title_tag
30
+ assert_select node(html), 'title', 1
31
+ end
32
+
33
+ test 'title_tag content should always prioritize content_for' do
34
+ @post = Post.first
35
+ title = "A new title"
36
+ content_for :title, title
37
+ html = title_tag
38
+
39
+ assert_select node(html), 'title', title
40
+ end
41
+
42
+ test 'title_tag should render the post if content_for(:title) is not set' do
43
+ @post = Post.first
44
+ html = title_tag('yada')
45
+
46
+ assert_select node(html), 'title', @post.title
47
+
48
+ end
49
+
50
+ test 'title_tag should render the argument passed if nothing else is set' do
51
+ html = title_tag
52
+ assert_select node(html), 'title', 'Ecrire'
53
+
54
+ html = title_tag('Oops')
55
+ assert_select node(html), 'title', 'Oops'
56
+ end
57
+
58
+
28
59
  def node(html)
29
60
  Nokogiri::HTML::Document.parse(html)
30
61
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ecrire
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.26.2
4
+ version: 0.26.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pier-Olivier Thibault
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-11 00:00:00.000000000 Z
11
+ date: 2015-05-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Blog engine on Rails
14
14
  email: pothibo@gmail.com
@@ -48,7 +48,7 @@ files:
48
48
  - lib/ecrire/app/assets/javascripts/admin/posts/tag.js.coffee
49
49
  - lib/ecrire/app/assets/javascripts/admin/posts/title.coffee
50
50
  - lib/ecrire/app/assets/javascripts/admin/posts/toggle.js.coffee
51
- - lib/ecrire/app/assets/javascripts/application.js
51
+ - lib/ecrire/app/assets/javascripts/ecrire.js
52
52
  - lib/ecrire/app/assets/javascripts/shared/overlay.js.coffee
53
53
  - lib/ecrire/app/assets/javascripts/shared/popup.js.coffee
54
54
  - lib/ecrire/app/assets/javascripts/vendor/moment.js
@@ -65,7 +65,7 @@ files:
65
65
  - lib/ecrire/app/assets/stylesheets/admin/save.css.scss
66
66
  - lib/ecrire/app/assets/stylesheets/admin/tags.css.scss
67
67
  - lib/ecrire/app/assets/stylesheets/admin/title.css.scss
68
- - lib/ecrire/app/assets/stylesheets/application.css.scss
68
+ - lib/ecrire/app/assets/stylesheets/ecrire.css.scss
69
69
  - lib/ecrire/app/assets/stylesheets/editor/code.css.scss
70
70
  - lib/ecrire/app/assets/stylesheets/editor/content.css.scss
71
71
  - lib/ecrire/app/assets/stylesheets/editor/figure.css.scss
@@ -77,40 +77,29 @@ files:
77
77
  - lib/ecrire/app/controllers/admin/application_controller.rb
78
78
  - lib/ecrire/app/controllers/admin/configurations/images_controller.rb
79
79
  - lib/ecrire/app/controllers/admin/images_controller.rb
80
- - lib/ecrire/app/controllers/admin/partials_controller.rb
81
80
  - lib/ecrire/app/controllers/admin/posts/tags_controller.rb
82
81
  - lib/ecrire/app/controllers/admin/posts/titles_controller.rb
83
82
  - lib/ecrire/app/controllers/admin/posts_controller.rb
84
- - lib/ecrire/app/controllers/admin/properties_controller.rb
85
83
  - lib/ecrire/app/controllers/admin/tags_controller.rb
86
84
  - lib/ecrire/app/controllers/admin/titles_controller.rb
87
85
  - lib/ecrire/app/controllers/application_controller.rb
88
86
  - lib/ecrire/app/controllers/ecrire/theme_controller.rb
89
- - lib/ecrire/app/controllers/images_controller.rb
90
- - lib/ecrire/app/controllers/partials_controller.rb
91
87
  - lib/ecrire/app/controllers/sessions_controller.rb
92
88
  - lib/ecrire/app/controllers/static_controller.rb
93
89
  - lib/ecrire/app/forms/admin/image_builder.rb
94
90
  - lib/ecrire/app/forms/admin/partial_builder.rb
95
91
  - lib/ecrire/app/forms/admin/post_builder.rb
96
92
  - lib/ecrire/app/helpers/admin/images_helper.rb
97
- - lib/ecrire/app/helpers/admin/labels_helper.rb
98
93
  - lib/ecrire/app/helpers/admin/posts_helper.rb
99
94
  - lib/ecrire/app/helpers/application_helper.rb
100
95
  - lib/ecrire/app/helpers/content_tag_helper.rb
101
- - lib/ecrire/app/helpers/open_graph_helper.rb
102
- - lib/ecrire/app/helpers/posts_helper.rb
96
+ - lib/ecrire/app/helpers/pagination_helper.rb
103
97
  - lib/ecrire/app/models/admin/image.rb
104
- - lib/ecrire/app/models/admin/partial.rb
105
98
  - lib/ecrire/app/models/admin/post.rb
106
99
  - lib/ecrire/app/models/admin/tag.rb
107
100
  - lib/ecrire/app/models/admin/title.rb
108
- - lib/ecrire/app/models/concerns/.keep
109
101
  - lib/ecrire/app/models/image.rb
110
- - lib/ecrire/app/models/partial.rb
111
102
  - lib/ecrire/app/models/post.rb
112
- - lib/ecrire/app/models/property/image.rb
113
- - lib/ecrire/app/models/property/label.rb
114
103
  - lib/ecrire/app/models/tag.rb
115
104
  - lib/ecrire/app/models/title.rb
116
105
  - lib/ecrire/app/models/user.rb
@@ -239,7 +228,6 @@ files:
239
228
  - lib/ecrire/theme/template/Procfile
240
229
  - lib/ecrire/theme/template/Rakefile
241
230
  - lib/ecrire/theme/template/assets/images/.keep
242
- - lib/ecrire/theme/template/assets/javascripts/theme.js.coffee
243
231
  - lib/ecrire/theme/template/assets/stylesheets/browser.css.scss
244
232
  - lib/ecrire/theme/template/assets/stylesheets/browser/base.css.scss
245
233
  - lib/ecrire/theme/template/assets/stylesheets/mobile.css.scss
@@ -256,7 +244,6 @@ files:
256
244
  - lib/ecrire/theme/template/controllers/tags_controller.rb
257
245
  - lib/ecrire/theme/template/helpers/blog_helper.rb
258
246
  - lib/ecrire/theme/template/routes.rb
259
- - lib/ecrire/theme/template/secrets.yml
260
247
  - lib/ecrire/theme/template/views/layouts/application.html.erb
261
248
  - lib/ecrire/theme/template/views/posts/_latest.html.erb
262
249
  - lib/ecrire/theme/template/views/posts/_post.html.erb
@@ -1,43 +0,0 @@
1
- module Admin
2
- class PartialsController < Admin::ApplicationController
3
-
4
- def index
5
- @partials = Admin::Partial.page(params[:page]).per(params[:per_page])
6
-
7
- respond_to do |format|
8
- format.js
9
- format.html
10
- end
11
- end
12
-
13
- def new
14
- @partial = Admin::Partial.new
15
- end
16
-
17
- def edit
18
- @partial = Admin::Partial.find(params[:id].to_i)
19
- end
20
-
21
- def create
22
- @partial = Admin::Partial.create(partial_params)
23
- respond_to do |format|
24
- format.html do
25
- redirect_to edit_admin_partial_path(@partial)
26
- end
27
- end
28
- end
29
-
30
- def update
31
- @partial = Admin::Partial.find(params[:id].to_i)
32
- @partial.update(partial_params)
33
- redirect_to edit_admin_partial_path(@partial) and return
34
- end
35
-
36
- protected
37
-
38
- def partial_params
39
- params.require(:admin_partial).permit(:title, :content, :javascript, :stylesheet)
40
- end
41
-
42
- end
43
- end
@@ -1,35 +0,0 @@
1
- module Admin
2
- class PropertiesController < Admin::ApplicationController
3
- INSTANCES = {
4
- label: Property::Label,
5
- image: Property::Image
6
- }.with_indifferent_access
7
-
8
- helper_method :post
9
-
10
- def create
11
- instance = instance_for_property(params[:property])
12
- @property = instance.create(params)
13
- render "admin/properties/#{instance.name}/create"
14
- end
15
-
16
- def destroy
17
- instance = instance_for_property(params[:property])
18
- @property = instance.destroy(params)
19
- render "admin/properties/#{instance.name}/destroy"
20
- end
21
-
22
- protected
23
-
24
- def post
25
- @post ||= Admin::Post.find(params[:post_id])
26
- end
27
-
28
- def instance_for_property(name)
29
- instance = INSTANCES[name].new
30
- instance.post = post
31
- instance
32
- end
33
-
34
- end
35
- end
@@ -1,2 +0,0 @@
1
- class ImagesController < ApplicationController
2
- end
@@ -1,8 +0,0 @@
1
- class PartialsController < ApplicationController
2
- def show
3
- @partial = Partial.find(params[:id].to_i)
4
- respond_to do |format|
5
- format.html { render layout: false }
6
- end
7
- end
8
- end
@@ -1,26 +0,0 @@
1
- module Admin::LabelsHelper
2
- def create_label_button(label, post)
3
- button_to label.name.capitalize,
4
- admin_post_properties_path(post.id),
5
- remote: true,
6
- form: {id: "label-#{label.id}"},
7
- form_class: %w(create label),
8
- params: {
9
- property: :label,
10
- value: label.name
11
- }
12
- end
13
-
14
- def destroy_label_button(label, post)
15
- button_to label.name.capitalize,
16
- admin_post_properties_path(post.id),
17
- form: {id: "label-#{label.id}"},
18
- form_class: %w(destroy label),
19
- method: :delete,
20
- remote: true,
21
- params: {
22
- property: :label,
23
- value: label.name
24
- }
25
- end
26
- end
@@ -1,45 +0,0 @@
1
- module OpenGraphHelper
2
- # OpenGraph is in meta tags. First I thought it would bring problem if meta tags wouldn't change between page load.
3
- # Thinking it through, it doesn't matter if open graph tags aren't changed between pages as it's needed by crawler that does full page load
4
- # So the tags will always match the content.
5
- # I doubt this will change in the future as turbolinks checks for crawlers and disable itself when it meets one.
6
- #
7
-
8
- def open_graph_tags
9
- if @post.nil?
10
- og_website
11
- else
12
- og_article(@post)
13
- end
14
- end
15
-
16
- protected
17
-
18
- def og_website
19
- [
20
- og_title,
21
- og_type('website')
22
- ].join.html_safe
23
- end
24
-
25
- def og_article(post)
26
- raise OGNoArticleError if post.nil?
27
- [
28
- og_title,
29
- og_type('article'),
30
- content_tag(:meta, nil, property: 'og:article:published_time', content: post.published_at.iso8601)
31
- ].join.html_safe
32
-
33
- end
34
-
35
- def og_type(value)
36
- content_tag :meta, nil, property: 'og:type', content: value
37
- end
38
-
39
- def og_title
40
- content_tag :meta, nil, property: 'og:title', content: title
41
- end
42
-
43
- class OGNoArticleError < StandardError; end
44
-
45
- end
@@ -1,13 +0,0 @@
1
- module PostsHelper
2
- def edit_post_link(options = {})
3
- return unless signed_in?
4
-
5
- link_to t('posts.edit'), edit_admin_post_path(post.id), options
6
- end
7
-
8
- def paginate(scope, options = {}, &block)
9
- _with_routes Ecrire::Theme::Engine.routes do
10
- super
11
- end
12
- end
13
- end
@@ -1,4 +0,0 @@
1
- module Admin
2
- class Partial < ::Partial
3
- end
4
- end
File without changes
@@ -1,3 +0,0 @@
1
- class Partial < ActiveRecord::Base
2
- validates_presence_of :title
3
- end
@@ -1,30 +0,0 @@
1
- module Property
2
- class Image
3
- attr_accessor :post
4
-
5
- def name
6
- "image"
7
- end
8
-
9
- def create(params)
10
- unless post.header.nil?
11
- post.header.destroy
12
- end
13
- img = post.images.build
14
- img.file = params[:admin_image][:file]
15
- img.save
16
- post.header = img
17
- post.save
18
- end
19
-
20
- # TODO:
21
- # 3. Remove id in post properties
22
- def destroy(value)
23
- return if post.header.nil?
24
- img = post.header
25
- img.destroy
26
- end
27
-
28
- end
29
- end
30
-
@@ -1,28 +0,0 @@
1
- module Property
2
- class Label
3
- attr_accessor :post
4
-
5
- def name
6
- "label"
7
- end
8
-
9
- def create(params)
10
- new_label = ::Label.find_or_create_by!(name: params[:value])
11
- labels = post.labels
12
- labels << new_label
13
- post.labels = labels
14
- post.save!
15
- new_label
16
- end
17
-
18
- def destroy(params)
19
- label = ::Label.find_by!(name: params[:value])
20
- labels = post.labels
21
- labels.delete(label)
22
- post.labels = labels
23
- post.save!
24
- label
25
- end
26
-
27
- end
28
- end
@@ -1 +0,0 @@
1
- #= require_tree .
@@ -1,11 +0,0 @@
1
- ---
2
- development: &1
3
- adapter: postgresql
4
- database: ecrire
5
- user: pothibo
6
- password: 3dbdbc10bdf7bd7d8ad7b6dbcf4bb26c
7
- encoding: utf8
8
- secret_key: 43fce5ef5993544ba39821911080adfa
9
- secret_key_base: 43fce5ef5993544ba39821911080adfa
10
- test: *1
11
- production: *1