ecrire 0.26.2 → 0.26.3

Sign up to get free protection for your applications and to get access to all the features.
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