locomotivecms_wagon 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Gemfile +1 -1
  2. data/generators/bootstrap/app/views/pages/index.liquid +10 -1
  3. data/generators/bootstrap/app/views/pages/index.liquid.haml +9 -1
  4. data/generators/bootstrap/public/stylesheets/fonts/awesome.css.scss +3 -0
  5. data/generators/bootstrap/public/{fonts → stylesheets/fonts}/font-awesome-ie7.min.css +0 -0
  6. data/generators/bootstrap/public/{fonts → stylesheets/fonts}/font-awesome.css +0 -9
  7. data/generators/foundation/app/views/pages/index.liquid +1 -0
  8. data/generators/foundation/app/views/pages/index.liquid.haml +3 -1
  9. data/lib/locomotive/wagon/liquid.rb +2 -0
  10. data/lib/locomotive/wagon/liquid/drops/content_types.rb +6 -3
  11. data/lib/locomotive/wagon/liquid/drops/page.rb +2 -2
  12. data/lib/locomotive/wagon/liquid/drops/site.rb +0 -2
  13. data/lib/locomotive/wagon/liquid/filters/misc.rb +4 -0
  14. data/lib/locomotive/wagon/liquid/filters/text.rb +0 -1
  15. data/lib/locomotive/wagon/liquid/scopeable.rb +32 -0
  16. data/lib/locomotive/wagon/liquid/tags/editable.rb +1 -0
  17. data/lib/locomotive/wagon/liquid/tags/editable/base.rb +5 -1
  18. data/lib/locomotive/wagon/liquid/tags/editable/short_text.rb +5 -0
  19. data/lib/locomotive/wagon/liquid/tags/editable/text.rb +15 -0
  20. data/lib/locomotive/wagon/liquid/tags/hybrid.rb +27 -0
  21. data/lib/locomotive/wagon/liquid/tags/link_to.rb +112 -0
  22. data/lib/locomotive/wagon/liquid/tags/nav.rb +192 -73
  23. data/lib/locomotive/wagon/misc/core_ext.rb +18 -0
  24. data/lib/locomotive/wagon/server.rb +3 -1
  25. data/lib/locomotive/wagon/server/dynamic_assets.rb +19 -12
  26. data/lib/locomotive/wagon/server/logging.rb +1 -1
  27. data/lib/locomotive/wagon/server/page.rb +1 -1
  28. data/lib/locomotive/wagon/version.rb +1 -1
  29. data/locomotivecms_wagon.gemspec +3 -1
  30. data/spec/fixtures/default/app/views/pages/about_us.liquid.haml +1 -0
  31. data/spec/fixtures/default/app/views/pages/about_us/john_doe.liquid.haml +2 -0
  32. data/spec/fixtures/default/app/views/pages/archives/news.liquid.haml +3 -0
  33. data/spec/fixtures/default/app/views/pages/events.liquid.haml +13 -0
  34. data/spec/fixtures/default/app/views/pages/music.liquid.haml +13 -1
  35. data/spec/fixtures/default/app/views/pages/songs/template.liquid.haml +1 -0
  36. data/spec/fixtures/default/app/views/pages/tags/nav_in_deep.liquid.haml +6 -0
  37. data/spec/fixtures/default/app/views/snippets/A_Complicated-one.liquid.haml +1 -0
  38. data/spec/integration/server/basic_spec.rb +16 -9
  39. data/spec/integration/server/liquid_spec.rb +48 -4
  40. metadata +96 -11
  41. checksums.yaml +0 -15
  42. data/generators/bootstrap/public/fonts/font-awesome.min.css +0 -34
  43. data/lib/locomotive/wagon/scopeable.rb +0 -28
@@ -2,6 +2,7 @@ module Locomotive
2
2
  module Wagon
3
3
  module Liquid
4
4
  module Tags
5
+
5
6
  # Display the children pages of the site, current page or the parent page. If not precised, nav is applied on the current page.
6
7
  # The html output is based on the ul/li tags.
7
8
  #
@@ -20,16 +21,8 @@ module Locomotive
20
21
  def initialize(tag_name, markup, tokens, context)
21
22
  if markup =~ Syntax
22
23
  @source = ($1 || 'page').gsub(/"|'/, '')
23
- @options = { id: 'nav', class: '', active_class: 'on', bootstrap: false }
24
- markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') }
25
-
26
- @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude]
27
24
 
28
- if @options[:snippet]
29
- if template = self.parse_snippet_template(context, @options[:snippet])
30
- @options[:liquid_render] = template
31
- end
32
- end
25
+ self.set_options(markup, context)
33
26
  else
34
27
  raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <site|parent|page|<path to a page>> <options>")
35
28
  end
@@ -40,45 +33,44 @@ module Locomotive
40
33
  def render(context)
41
34
  self.set_accessors_from_context(context)
42
35
 
43
- children_output = []
44
-
45
36
  entries = self.fetch_entries
37
+ output = self.build_entries_output(entries)
46
38
 
47
- entries.each_with_index do |p, index|
48
- css = []
49
- css << 'first' if index == 0
50
- css << 'last' if index == entries.size - 1
51
-
52
- children_output << render_entry_link(p, css.join(' '), 1)
53
- end
54
-
55
- output = children_output.join("\n")
56
-
57
- if @options[:no_wrapper] != 'true'
58
- list_class = !@options[:class].blank? ? %( class="#{@options[:class]}") : ''
59
- output = %{<nav id="#{@options[:id]}"#{list_class}><ul>\n#{output}</ul></nav>}
39
+ if self.no_wrapper?
40
+ output
41
+ else
42
+ self.render_tag(:nav, id: @options[:id], css: @options[:class]) do
43
+ self.render_tag(:ul) { output }
44
+ end
60
45
  end
61
-
62
- output
63
46
  end
64
47
 
65
48
  protected
66
49
 
67
- def set_accessors_from_context(context)
68
- self.current_page = context.registers[:page]
69
- self.mounting_point = context.registers[:mounting_point]
70
- end
50
+ # Build recursively the links of all the pages.
51
+ #
52
+ # @param [ Array ] entries List of pages
53
+ #
54
+ # @return [ String ] The final HTML output
55
+ #
56
+ def build_entries_output(entries, depth = 1)
57
+ output = []
71
58
 
72
- def parse_snippet_template(context, template_name)
73
- source = if template_name.include?('{')
74
- template_name
75
- else
76
- context[:mounting_point].snippets[template_name].try(:source)
59
+ entries.each_with_index do |page, index|
60
+ css = []
61
+ css << 'first' if index == 0
62
+ css << 'last' if index == entries.size - 1
63
+
64
+ output << self.render_entry_link(page, css.join(' '), depth)
77
65
  end
78
66
 
79
- source ? ::Liquid::Template.parse(source) : nil
67
+ output.join("\n")
80
68
  end
81
69
 
70
+ # Get all the children of a source: site (index page), parent or page.
71
+ #
72
+ # @return [ Array ] List of pages
73
+ #
82
74
  def fetch_entries
83
75
  children = (case @source
84
76
  when 'site' then self.mounting_point.pages['index']
@@ -91,7 +83,12 @@ module Locomotive
91
83
  children.delete_if { |p| !include_page?(p) }
92
84
  end
93
85
 
94
- # Determines whether or not a page should be a part of the menu
86
+ # Determine whether or not a page should be a part of the menu.
87
+ #
88
+ # @param [ Object ] page The page
89
+ #
90
+ # @return [ Boolean ] True if the page can be included or not
91
+ #
95
92
  def include_page?(page)
96
93
  if !page.listed? || page.templatized? || !page.published?
97
94
  false
@@ -102,62 +99,184 @@ module Locomotive
102
99
  end
103
100
  end
104
101
 
105
- # Returns a list element, a link to the page and its children
106
- def render_entry_link(page, css, depth)
107
- selected = self.current_page.fullpath =~ /^#{page.fullpath}/ ? " #{@options[:active_class]}" : ''
102
+ # Determine wether or not a page is currently the displayed one.
103
+ #
104
+ # @param [ Object ] page The page
105
+ #
106
+ # @return [ Boolean ]
107
+ #
108
+ def page_selected?(page)
109
+ self.current_page.fullpath =~ /^#{page.fullpath}/
110
+ end
108
111
 
109
- icon = @options[:icon] ? '<span></span>' : ''
112
+ # Determine if the children of a page have to be rendered or not.
113
+ # It depends on the depth passed in the option.
114
+ #
115
+ # @param [ Object ] page The page
116
+ # @param [ Integer ] depth The current depth
117
+ #
118
+ # @return [ Boolean ] True if the children have to be rendered.
119
+ #
120
+ def render_children_for_page?(page, depth)
121
+ depth.succ <= @options[:depth].to_i &&
122
+ (page.children || []).select { |child| self.include_page?(child) }.any?
123
+ end
110
124
 
125
+ # Return the label of an entry. It may use or not the template
126
+ # given by the snippet option.
127
+ #
128
+ # @param [ Object ] page The page
129
+ #
130
+ # @return [ String ] The label in HTML
131
+ #
132
+ def entry_label(page)
133
+ icon = @options[:icon] ? '<span></span>' : ''
111
134
  title = @options[:liquid_render] ? @options[:liquid_render].render('page' => page) : page.title
112
135
 
113
- label = %{#{icon if @options[:icon] != 'after' }#{title}#{icon if @options[:icon] == 'after' }}
114
-
115
- dropdow = ""
116
- link_options = ""
117
- href = ::I18n.locale.to_s == self.mounting_point.default_locale.to_s ? "/#{page.fullpath}" : "/#{::I18n.locale}/#{page.fullpath}"
118
- caret = ""
119
-
120
- if render_children_for_page?(page, depth) && bootstrap?
121
- dropdow = "dropdown"
122
- link_options = %{class="dropdown-toggle" data-toggle="dropdown"}
123
- href = "#"
124
- caret = %{<b class="caret"></b>}
136
+ if icon.blank?
137
+ title
138
+ elsif @options[:icon] == 'after'
139
+ "#{title} #{icon}"
140
+ else
141
+ "#{icon} #{title}"
125
142
  end
143
+ end
126
144
 
127
- output = %{<li id="#{page.slug.to_s.dasherize}-link" class="link#{selected} #{css} #{dropdow}">}
128
- output << %{<a href="#{href}" #{link_options}>#{label} #{caret}</a>}
129
- output << render_entry_children(page, depth.succ) if (depth.succ <= @options[:depth].to_i)
130
- output << %{</li>}
145
+ # Return the localized url of an entry (page).
146
+ #
147
+ # @param [ Object ] page The page
148
+ #
149
+ # @return [ String ] The localized url
150
+ #
151
+ def entry_url(page)
152
+ if ::I18n.locale.to_s == self.mounting_point.default_locale.to_s
153
+ "/#{page.fullpath}"
154
+ else
155
+ "/#{::I18n.locale}/#{page.fullpath}"
156
+ end
157
+ end
131
158
 
132
- output.strip
159
+ # Return the css of an entry (page).
160
+ #
161
+ # @param [ Object ] page The page
162
+ # @param [ String ] css The extra css
163
+ #
164
+ # @return [ String ] The css
165
+ #
166
+ def entry_css(page, css = '')
167
+ _css = 'link'
168
+ _css = "#{page}#{@options[:active_class]}" if self.page_selected?(page)
169
+
170
+ (_css + " #{css}").strip
133
171
  end
134
172
 
135
- def render_children_for_page?(page, depth)
136
- depth.succ <= @options[:depth].to_i && page.children.reject { |c| !include_page?(c) }.any?
173
+ # Return the HTML output of a page and its children if requested.
174
+ #
175
+ # @param [ Object ] page The page
176
+ # @param [ String ] css The current css to apply to the entry
177
+ # @param [ Integer] depth Used to know if the children has to be added or not.
178
+ #
179
+ # @return [ String ] The HTML output
180
+ #
181
+ def render_entry_link(page, css, depth)
182
+ url = self.entry_url(page)
183
+ label = self.entry_label(page)
184
+ css = self.entry_css(page, css)
185
+ options = ''
186
+
187
+ if self.render_children_for_page?(page, depth) && self.bootstrap?
188
+ url = '#'
189
+ label += %{ <b class="caret"></b>}
190
+ css += ' dropdown'
191
+ options = %{ class="dropdown-toggle" data-toggle="dropdown"}
192
+ end
193
+
194
+ self.render_tag(:li, id: "#{page.slug.to_s.dasherize}-link", css: css) do
195
+ children_output = depth.succ <= @options[:depth].to_i ? self.render_entry_children(page, depth.succ) : ''
196
+ %{<a href="#{url}"#{options}>#{label}</a>} + children_output
197
+ end
137
198
  end
138
199
 
139
- # Recursively creates a nested unordered list for the depth specified
200
+ # Recursively create a nested unordered list for the depth specified.
201
+ #
202
+ # @param [ Array ] entries The children of the page
203
+ # @param [ Integer ] depth The current depth
204
+ #
205
+ # @return [ String ] The HTML code
206
+ #
140
207
  def render_entry_children(page, depth)
141
- output = %{}
208
+ entries = (page.children || []).select { |child| self.include_page?(child) }
209
+ css = self.bootstrap? ? 'dropdown-menu' : ''
210
+
211
+ unless entries.empty?
212
+ self.render_tag(:ul, id: "#{@options[:id]}-#{page.slug.to_s.dasherize}", css: css) do
213
+ self.build_entries_output(entries, depth)
214
+ end
215
+ else
216
+ ''
217
+ end
218
+ end
142
219
 
143
- children = page.children.reject { |c| !include_page?(c) }
144
- if children.present?
145
- output = %{<ul id="#{@options[:id]}-#{page.slug.to_s.dasherize}" class="#{bootstrap? ? "dropdown-menu" : ""}">}
146
- children.each do |c, page|
147
- css = []
148
- css << 'first' if children.first == c
149
- css << 'last' if children.last == c
220
+ # Set the value (default or assigned by the tag) of the options.
221
+ #
222
+ def set_options(markup, context)
223
+ @options = { id: 'nav', class: '', active_class: 'on', bootstrap: false, no_wrapper: false }
150
224
 
151
- output << render_entry_link(c, css.join(' '),depth)
225
+ markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') }
226
+
227
+ @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude]
228
+
229
+ if @options[:snippet]
230
+ if template = self.parse_snippet_template(context, @options[:snippet])
231
+ @options[:liquid_render] = template
152
232
  end
153
- output << %{</ul>}
154
233
  end
234
+ end
235
+
236
+ # Avoid to call context.registers to get the current page
237
+ # and the mounting point.
238
+ #
239
+ def set_accessors_from_context(context)
240
+ self.current_page = context.registers[:page]
241
+ self.mounting_point = context.registers[:mounting_point]
242
+ end
243
+
244
+ # Parse the template of the snippet give in option of the tag.
245
+ # If the template_name contains a liquid tag or drop, it will
246
+ # be used an inline template.
247
+ #
248
+ def parse_snippet_template(context, template_name)
249
+ source = if template_name.include?('{')
250
+ template_name
251
+ else
252
+ context[:mounting_point].snippets[template_name].try(:source)
253
+ end
254
+
255
+ source ? ::Liquid::Template.parse(source) : nil
256
+ end
155
257
 
156
- output
258
+ # Render any kind HTML tags. The content of the tag comes from
259
+ # the block.
260
+ #
261
+ # @param [ String ] tag_name Name of the HTML tag (li, ul, div, ...etc).
262
+ # @param [ String ] html_options Id, class, ..etc
263
+ #
264
+ # @return [ String ] The HTML
265
+ #
266
+ def render_tag(tag_name, html_options = {}, &block)
267
+ options = ['']
268
+ options << %{id="#{html_options[:id]}"} if html_options[:id].present?
269
+ options << %{class="#{html_options[:css]}"} if html_options[:css].present?
270
+
271
+ %{<#{tag_name}#{options.join(' ')}>#{yield}</#{tag_name}>}
157
272
  end
158
273
 
159
274
  def bootstrap?
160
- @options[:bootstrap] == 'true' || @options[:bootstrap] == true
275
+ @options[:bootstrap].to_bool
276
+ end
277
+
278
+ def no_wrapper?
279
+ @options[:no_wrapper].to_bool
161
280
  end
162
281
 
163
282
  ::Liquid::Template.register_tag('nav', Nav)
@@ -26,4 +26,22 @@ unless Hash.instance_methods.include?(:underscore_keys)
26
26
  end
27
27
 
28
28
  end
29
+ end
30
+
31
+ unless String.instance_methods.include?(:to_bool)
32
+ class String
33
+ def to_bool
34
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
35
+ return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
36
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
37
+ end
38
+ end
39
+
40
+ class TrueClass
41
+ def to_bool; self; end
42
+ end
43
+
44
+ class FalseClass
45
+ def to_bool; self; end
46
+ end
29
47
  end
@@ -1,5 +1,7 @@
1
1
  require 'better_errors'
2
2
  require 'coffee_script'
3
+ require 'sprockets'
4
+ require "sprockets-sass"
3
5
 
4
6
  require 'locomotive/wagon/listen'
5
7
  require 'locomotive/wagon/server/middleware'
@@ -58,7 +60,7 @@ module Locomotive::Wagon
58
60
  }
59
61
 
60
62
  use Favicon
61
- use DynamicAssets
63
+ use DynamicAssets, reader.mounting_point.path
62
64
 
63
65
  use Logging
64
66
 
@@ -3,22 +3,29 @@ module Locomotive::Wagon
3
3
 
4
4
  class DynamicAssets < Middleware
5
5
 
6
- def call(env)
7
- self.set_accessors(env)
6
+ attr_reader :app, :sprockets, :regexp
8
7
 
9
- path = env['PATH_INFO']
8
+ def initialize(app, root)
9
+ super(app)
10
10
 
11
- if path =~ /^\/(stylesheets|javascripts)\//
11
+ @regexp = /^\/(javascripts|stylesheets)\/(.*)$/
12
12
 
13
- mime_type = MIME::Types.type_for(path).first.try(:to_s) || 'text/plain'
14
- asset = self.mounting_point.theme_assets.detect do |_asset|
15
- _asset.path == path
16
- end
13
+ # make sure Compass is correctly configured
14
+ Locomotive::Mounter::Extensions::Compass.configure(root)
15
+
16
+ @sprockets = Sprockets::Environment.new
17
+ @sprockets.append_path File.join(root, 'public/stylesheets')
18
+ @sprockets.append_path File.join(root, 'public/javascripts')
19
+ end
20
+
21
+ def call(env)
22
+ if env['PATH_INFO'] =~ self.regexp
23
+ env['PATH_INFO'] = $2
17
24
 
18
- if asset
19
- [200, { 'Content-Type' => mime_type }, [asset.content!]]
20
- else
21
- [404, { 'Content-Type' => mime_type }, ['Asset not found']]
25
+ begin
26
+ self.sprockets.call(env)
27
+ rescue Exception => e
28
+ raise Locomotive::Wagon::DefaultException.new "Unable to serve a dynamic asset. Please check the logs.", e
22
29
  end
23
30
  else
24
31
  app.call(env)
@@ -11,7 +11,7 @@ module Locomotive::Wagon
11
11
  log "Started #{env['REQUEST_METHOD'].upcase} \"#{env['PATH_INFO']}\" at #{now}"
12
12
 
13
13
  app.call(env).tap do |response|
14
- done_in_ms = (Time.now - now) * 1000
14
+ done_in_ms = ((Time.now - now) * 10000).truncate / 10.0
15
15
  log "Completed #{code_to_human(response.first)} in #{done_in_ms}ms\n\n"
16
16
  end
17
17
  end
@@ -20,7 +20,7 @@ module Locomotive::Wagon
20
20
  page = self.fetch_page
21
21
 
22
22
  if page
23
- self.log "Found page \"#{page.title}\" [/#{page.inspect}]"
23
+ self.log "Found page \"#{page.title}\" [#{page.safe_fullpath}]"
24
24
  end
25
25
 
26
26
  env['wagon.page'] = page
@@ -1,5 +1,5 @@
1
1
  module Locomotive
2
2
  module Wagon
3
- VERSION = '1.1.0'
3
+ VERSION = '1.2.0'
4
4
  end
5
5
  end
@@ -24,6 +24,8 @@ Gem::Specification.new do |gem|
24
24
  gem.add_dependency 'locomotive_liquid', '~> 2.4.1'
25
25
  gem.add_dependency 'RedCloth', '~> 4.2.9'
26
26
  gem.add_dependency 'dragonfly', '~> 0.9.12'
27
+ gem.add_dependency 'sprockets', '~> 2.0'
28
+ gem.add_dependency 'sprockets-sass', '~> 1.0.1'
27
29
  gem.add_dependency 'rack-cache', '~> 1.1'
28
30
  gem.add_dependency 'better_errors', '~> 0.7.2'
29
31
 
@@ -31,7 +33,7 @@ Gem::Specification.new do |gem|
31
33
 
32
34
  gem.add_dependency 'httmultiparty', '0.3.8'
33
35
  gem.add_dependency 'will_paginate', '~> 3.0.3'
34
- gem.add_dependency 'locomotivecms_mounter', '~> 1.1.0'
36
+ gem.add_dependency 'locomotivecms_mounter', '~> 1.2.0'
35
37
 
36
38
  gem.add_dependency 'faker', '~> 0.9.5'
37
39