locomotivecms_steam 0.1.1 → 0.1.2.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +10 -2
  5. data/Gemfile.lock +37 -49
  6. data/Rakefile +5 -10
  7. data/example/server.rb +10 -6
  8. data/lib/locomotive/steam.rb +46 -0
  9. data/lib/locomotive/steam/configuration.rb +13 -0
  10. data/lib/locomotive/steam/decorators.rb +1 -0
  11. data/lib/locomotive/steam/decorators/page_decorator.rb +50 -0
  12. data/lib/locomotive/steam/entities/content_type.rb +11 -0
  13. data/lib/locomotive/steam/entities/page.rb +136 -0
  14. data/lib/locomotive/steam/entities/site.rb +30 -0
  15. data/lib/locomotive/steam/initializers/dragonfly.rb +3 -4
  16. data/lib/locomotive/steam/liquid.rb +1 -12
  17. data/lib/locomotive/steam/liquid/drops/base.rb +1 -1
  18. data/lib/locomotive/steam/liquid/drops/content_entry.rb +3 -2
  19. data/lib/locomotive/steam/liquid/drops/content_types.rb +4 -2
  20. data/lib/locomotive/steam/liquid/drops/page.rb +4 -3
  21. data/lib/locomotive/steam/liquid/drops/site.rb +3 -2
  22. data/lib/locomotive/steam/liquid/patches.rb +4 -8
  23. data/lib/locomotive/steam/liquid/tags/nav.rb +19 -17
  24. data/lib/locomotive/steam/loaders/yml/pages_loader.rb +193 -0
  25. data/lib/locomotive/steam/loaders/yml/site_loader.rb +49 -0
  26. data/lib/locomotive/steam/loaders/yml/utils/localized_tree.rb +33 -0
  27. data/lib/locomotive/steam/loaders/yml/utils/yaml_front_matters_template.rb +66 -0
  28. data/lib/locomotive/steam/loaders/yml_loader.rb +33 -0
  29. data/lib/locomotive/steam/mapper.rb +86 -0
  30. data/lib/locomotive/steam/middlewares/base.rb +12 -10
  31. data/lib/locomotive/steam/middlewares/locale.rb +3 -4
  32. data/lib/locomotive/steam/middlewares/page.rb +18 -18
  33. data/lib/locomotive/steam/middlewares/renderer.rb +41 -15
  34. data/lib/locomotive/steam/middlewares/stack.rb +1 -1
  35. data/lib/locomotive/steam/monkey_patches.rb +1 -2
  36. data/lib/locomotive/steam/monkey_patches/haml.rb +1 -1
  37. data/lib/locomotive/steam/repositories/content_types_repository.rb +14 -0
  38. data/lib/locomotive/steam/repositories/pages_repository.rb +23 -0
  39. data/lib/locomotive/steam/repositories/sites_repository.rb +16 -0
  40. data/lib/locomotive/steam/server.rb +14 -11
  41. data/lib/locomotive/steam/services/external_api.rb +2 -1
  42. data/lib/locomotive/steam/services/markdown.rb +3 -12
  43. data/lib/locomotive/steam/standalone_server.rb +2 -2
  44. data/lib/locomotive/steam/version.rb +4 -1
  45. data/locomotivecms_steam.gemspec +11 -5
  46. data/spec/fixtures/default/app/views/pages/basic.liquid.haml +13 -0
  47. data/spec/fixtures/default/config/site.yml +2 -2
  48. data/spec/integration/server/basic_spec.rb +22 -17
  49. data/spec/integration/server/contact_form_spec.rb +1 -1
  50. data/spec/integration/server/liquid_spec.rb +2 -2
  51. data/spec/integration/server/with_scope_spec.rb +2 -2
  52. data/spec/spec_helper.rb +15 -4
  53. data/spec/support/helpers.rb +26 -8
  54. data/spec/unit/decorators/page_decorator_spec.rb +55 -0
  55. data/spec/unit/entities/page_spec.rb +50 -0
  56. data/spec/unit/entities/site_spec.rb +12 -0
  57. data/spec/unit/liquid/tags/nav_spec.rb +159 -0
  58. data/spec/unit/loaders/pages_loader_spec.rb +42 -0
  59. data/spec/unit/loaders/site_loader_spec.rb +21 -0
  60. data/spec/unit/loaders/utils/localized_tree_spec.rb +33 -0
  61. data/spec/unit/loaders/utils/yaml_front_matters_template_spec.rb +39 -0
  62. data/spec/unit/middlewares/base_spec.rb +20 -0
  63. data/spec/unit/middlewares/page_spec.rb +51 -0
  64. data/spec/unit/repositories/pages_spec.rb +11 -0
  65. metadata +128 -23
  66. data/bin/publish +0 -28
  67. data/lib/locomotive/steam/misc.rb +0 -10
  68. data/lib/locomotive/steam/monkey_patches/mounter.rb +0 -54
  69. data/lib/steam.rb +0 -3
@@ -0,0 +1,30 @@
1
+ module Locomotive
2
+ module Steam
3
+ module Entities
4
+ class Site
5
+ class NullObject
6
+ def method_missing *args
7
+ nil
8
+ end
9
+ end
10
+ include Locomotive::Entity
11
+
12
+ attributes :name, :locales, :subdomain, :domains, :seo_title,
13
+ :meta_keywords, :meta_description, :robots_txt, :timezone
14
+ ## methods ##
15
+
16
+ def default_locale
17
+ locales.first
18
+ end
19
+
20
+ def to_s
21
+ self.name
22
+ end
23
+
24
+ def to_liquid
25
+ ::Locomotive::Steam::Liquid::Drops::Site.new(self)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,4 @@
1
1
  require 'dragonfly'
2
- require 'common'
3
2
 
4
3
  # Configure
5
4
  Dragonfly.app(:steam).configure do
@@ -11,9 +10,9 @@ Dragonfly.app(:steam).configure do
11
10
 
12
11
  url_format '/images/dynamic/:job/:basename.:ext'
13
12
 
14
- fetch_file_whitelist /public/
13
+ fetch_file_whitelist(/public/)
15
14
 
16
- fetch_url_whitelist /.+/
15
+ fetch_url_whitelist(/.+/)
17
16
  end
18
17
 
19
- Dragonfly.logger = Locomotive::Common::Logger.instance
18
+ Dragonfly.logger = Locomotive::Common::Logger.instance
@@ -1,5 +1,5 @@
1
1
  require 'solid'
2
- require 'locomotive/mounter'
2
+ require 'locomotive/models'
3
3
 
4
4
  require_relative 'liquid/scopeable'
5
5
  require_relative 'liquid/drops/base'
@@ -9,14 +9,3 @@ require_relative 'liquid/tags/path_helper'
9
9
  %w{. drops tags filters}.each do |dir|
10
10
  Dir[File.join(File.dirname(__FILE__), 'liquid', dir, '*.rb')].each { |lib| require lib }
11
11
  end
12
-
13
- # add to_liquid methods to main models from the mounter
14
- %w{site page content_entry}.each do |name|
15
- klass = "Locomotive::Mounter::Models::#{name.classify}".constantize
16
-
17
- klass.class_eval <<-EOV
18
- def to_liquid
19
- ::Locomotive::Steam::Liquid::Drops::#{name.classify}.new(self)
20
- end
21
- EOV
22
- end
@@ -43,4 +43,4 @@ module Locomotive
43
43
  end
44
44
  end
45
45
  end
46
- end
46
+ end
@@ -3,8 +3,9 @@ module Locomotive
3
3
  module Liquid
4
4
  module Drops
5
5
  class ContentEntry < Base
6
+ extend Forwardable
6
7
 
7
- delegate :seo_title, :meta_keywords, :meta_description, to: :@_source
8
+ def_delegators :@_source, :seo_title, :meta_keywords, :meta_description
8
9
 
9
10
  def _label
10
11
  @_label ||= @_source._label
@@ -45,4 +46,4 @@ module Locomotive
45
46
  end
46
47
  end
47
48
  end
48
- end
49
+ end
@@ -5,7 +5,7 @@ module Locomotive
5
5
  class ContentTypes < ::Liquid::Drop
6
6
 
7
7
  def before_method(meth)
8
- type = self.mounting_point.content_types[meth.to_s]
8
+ type = Locomotive::Models['content_types'][meth.to_s]
9
9
  ProxyCollection.new(type)
10
10
  end
11
11
 
@@ -108,7 +108,9 @@ module Locomotive
108
108
  @context['with_scope']['order_by'] = @content_type.order_by + '.' + @content_type.order_direction
109
109
  end
110
110
 
111
- @collection = apply_scope(@content_type.entries)
111
+ @collection = @content_type.entries.where(@context['with_scope'])
112
+
113
+ # @collection = apply_scope(@content_type.entries)
112
114
  end
113
115
  end
114
116
  end
@@ -3,9 +3,10 @@ module Locomotive
3
3
  module Liquid
4
4
  module Drops
5
5
  class Page < Base
6
+ extend Forwardable
6
7
 
7
- delegate :title, :slug, :fullpath, :parent, :depth, :seo_title, :redirect_url, :meta_description, :meta_keywords,
8
- :templatized?, :published?, :redirect?, :listed?, :handle, to: :@_source
8
+ def_delegators :@_source, :title, :slug, :fullpath, :parent, :depth, :seo_title, :redirect_url, :meta_description, :meta_keywords,
9
+ :templatized?, :published?, :redirect?, :listed?, :handle
9
10
 
10
11
  def children
11
12
  _children = @_source.children || []
@@ -25,4 +26,4 @@ module Locomotive
25
26
  end
26
27
  end
27
28
  end
28
- end
29
+ end
@@ -4,8 +4,9 @@ module Locomotive
4
4
  module Drops
5
5
  class Site < Base
6
6
  include Scopeable
7
+ extend Forwardable
7
8
 
8
- delegate :name, :seo_title, :meta_description, :meta_keywords, to: :@_source
9
+ def_delegators :@_source, :name, :seo_title, :meta_description, :meta_keywords
9
10
 
10
11
  def index
11
12
  @index ||= self.mounting_point.pages['index']
@@ -23,4 +24,4 @@ module Locomotive
23
24
  end
24
25
  end
25
26
  end
26
- end
27
+ end
@@ -2,10 +2,6 @@ module Liquid
2
2
 
3
3
  class Drop
4
4
 
5
- def mounting_point
6
- @context.registers[:mounting_point]
7
- end
8
-
9
5
  def site
10
6
  @context.registers[:site]
11
7
  end
@@ -15,15 +11,15 @@ module Liquid
15
11
  class Template
16
12
 
17
13
  # creates a new <tt>Template</tt> object from liquid source code
18
- def parse_with_utf8(source, context = {})
14
+ parse_method = instance_method(:parse)
15
+
16
+ define_method :parse do |source, context={}|
19
17
  if RUBY_VERSION =~ /1\.9/
20
18
  source = source.force_encoding('UTF-8') if source.present?
21
19
  end
22
- self.parse_without_utf8(source, context)
20
+ parse_method.bind(self).call(source, context)
23
21
  end
24
22
 
25
- alias_method_chain :parse, :utf8
26
-
27
23
  end
28
24
 
29
25
  module StandardFilters
@@ -16,7 +16,7 @@ module Locomotive
16
16
 
17
17
  Syntax = /(#{::Liquid::Expression}+)?/
18
18
 
19
- attr_accessor :current_page, :mounting_point
19
+ attr_accessor :current_page, :site
20
20
 
21
21
  def initialize(tag_name, markup, tokens, options)
22
22
  if markup =~ Syntax
@@ -32,7 +32,6 @@ module Locomotive
32
32
 
33
33
  def render(context)
34
34
  self.set_accessors_from_context(context)
35
-
36
35
  entries = self.fetch_entries
37
36
  output = self.build_entries_output(entries)
38
37
 
@@ -72,17 +71,19 @@ module Locomotive
72
71
  # @return [ Array ] List of pages
73
72
  #
74
73
  def fetch_entries
75
- children = (case @source
76
- when 'site' then self.mounting_point.pages['index']
74
+ children = root.children.try(:clone) || []
75
+ children.delete_if { |p| !include_page?(p) }
76
+ end
77
+
78
+ def root
79
+ case @source
80
+ when 'site' then Locomotive::Models['pages']['index']
77
81
  when 'parent' then self.current_page.parent || self.current_page
78
82
  when 'page' then self.current_page
79
83
  else
80
- self.mounting_point.pages[@source]
81
- end).children.try(:clone) || []
82
-
83
- children.delete_if { |p| !include_page?(p) }
84
+ Locomotive::Models['pages'][@source]
85
+ end
84
86
  end
85
-
86
87
  # Determine whether or not a page should be a part of the menu.
87
88
  #
88
89
  # @param [ Object ] page The page
@@ -149,7 +150,7 @@ module Locomotive
149
150
  # @return [ String ] The localized url
150
151
  #
151
152
  def entry_url(page)
152
- if ::I18n.locale.to_s == self.mounting_point.default_locale.to_s
153
+ if ::I18n.locale.to_s == self.site.default_locale.to_s
153
154
  "/#{page.fullpath}"
154
155
  else
155
156
  "/#{::I18n.locale}/#{page.fullpath}"
@@ -165,7 +166,8 @@ module Locomotive
165
166
  #
166
167
  def entry_css(page, css = '')
167
168
  _css = 'link'
168
- _css += " #{page} #{@_options[:active_class]}" if self.page_selected?(page)
169
+ #_css += " #{page} #{@_options[:active_class]}" if self.page_selected?(page)
170
+ _css += " #{@_options[:active_class]}" if self.page_selected?(page)
169
171
 
170
172
  (_css + " #{css}").strip
171
173
  end
@@ -191,7 +193,7 @@ module Locomotive
191
193
  options = %{ class="dropdown-toggle" data-toggle="dropdown"}
192
194
  end
193
195
 
194
- self.render_tag(:li, id: "#{page.slug.to_s.dasherize}-link", css: css) do
196
+ self.render_tag(:li, id: "#{page.slug.dasherize}-link", css: css) do
195
197
  children_output = depth.succ <= @_options[:depth].to_i ? self.render_entry_children(page, depth.succ) : ''
196
198
  %{<a href="#{url}"#{options}>#{label}</a>} + children_output
197
199
  end
@@ -209,7 +211,7 @@ module Locomotive
209
211
  css = self.bootstrap? ? 'dropdown-menu' : ''
210
212
 
211
213
  unless entries.empty?
212
- self.render_tag(:ul, id: "#{@_options[:id]}-#{page.slug.to_s.dasherize}", css: css) do
214
+ self.render_tag(:ul, id: "#{@_options[:id]}-#{page.slug.dasherize}", css: css) do
213
215
  self.build_entries_output(entries, depth)
214
216
  end
215
217
  else
@@ -234,11 +236,11 @@ module Locomotive
234
236
  end
235
237
 
236
238
  # Avoid to call context.registers to get the current page
237
- # and the mounting point.
239
+ # and the site.
238
240
  #
239
241
  def set_accessors_from_context(context)
240
242
  self.current_page = context.registers[:page]
241
- self.mounting_point = context.registers[:mounting_point]
243
+ self.site = context.registers[:site]
242
244
  end
243
245
 
244
246
  # Parse the template of the snippet give in option of the tag.
@@ -249,7 +251,7 @@ module Locomotive
249
251
  source = if template_name.include?('{')
250
252
  template_name
251
253
  else
252
- context[:mounting_point].snippets[template_name].try(:source)
254
+ context[:site].snippets[template_name].try(:source)
253
255
  end
254
256
 
255
257
  source ? ::Liquid::Template.parse(source) : nil
@@ -284,4 +286,4 @@ module Locomotive
284
286
  end
285
287
  end
286
288
  end
287
- end
289
+ end
@@ -0,0 +1,193 @@
1
+ module Locomotive
2
+ module Steam
3
+ module Loader
4
+ module Yml
5
+ class PagesLoader
6
+ def initialize(path, mapper)
7
+ @root_path = path
8
+ @path = File.join(path, 'app', 'views', 'pages')
9
+ @mapper = mapper
10
+ end
11
+
12
+
13
+ # Build the tree of pages based on the filesystem structure
14
+ #
15
+ # @return [ Hash ] The pages organized as a Hash (using the fullpath as the key)
16
+ #
17
+ def load!
18
+ entity_class = @mapper.collection(:pages).entity
19
+ repository = @mapper.collection(:pages).repository
20
+ all.each do |record|
21
+ page = entity_class.new(record)
22
+ repository.create page
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def build_relationships(parent, list)
29
+ # do not use an empty template for other locales than the default one
30
+ parent.set_default_template_for_each_locale(self.default_locale)
31
+
32
+ list.dup.each do |page|
33
+ next unless self.is_subpage_of?(page.fullpath, parent.fullpath)
34
+
35
+ # attach the page to the parent (order by position), also set the parent
36
+ parent.add_child(page)
37
+
38
+ # localize the fullpath in all the locales
39
+ page.localize_fullpath
40
+
41
+ # remove the page from the list
42
+ list.delete(page)
43
+
44
+ # go under
45
+ self.build_relationships(page, list)
46
+ end
47
+ end
48
+
49
+ # Record pages found in file system
50
+ def all
51
+ [].tap do |page_records|
52
+ position, last_dirname = nil, nil
53
+ tree.each do |pagename, localizations|
54
+ filepath = localizations.delete(:default)
55
+ #next unless File.directory?(filepath) || filepath =~ /\.(#{Locomotive::Steam::TEMPLATE_EXTENSIONS.join('|')})$/
56
+ if last_dirname != File.dirname(filepath)
57
+ position, last_dirname = 100, File.dirname(filepath)
58
+ end
59
+ page = page_attributes(filepath, localizations)
60
+ page[:position] = position
61
+
62
+ page_records << page
63
+ position += 1
64
+ end
65
+ end
66
+ end
67
+
68
+ def tree
69
+ Locomotive::Steam::Utils::LocalizedTree
70
+ .new(all_files, Locomotive::Steam::TEMPLATE_EXTENSIONS)
71
+ .to_hash
72
+ end
73
+
74
+ def all_files
75
+ Dir.glob(File.join(root_dir, '**/*')).sort
76
+ end
77
+
78
+ def page_attributes(filepath, localizations)
79
+ {}.tap do |attributes|
80
+ add_base_attributes(attributes, filepath)
81
+ add_localized_attributes(attributes, filepath, :en)
82
+ localizations.each { |locale, locale_file| add_localized_attributes(attributes, locale_file, locale) }
83
+ end
84
+ end
85
+
86
+ def add_base_attributes(attributes, filepath)
87
+ attributes.merge! _base_attributes(filepath)
88
+ end
89
+
90
+ def add_localized_attributes(attributes, filepath, locale)
91
+ attributes.deep_merge! _localized_attributes(filepath, locale)
92
+ end
93
+
94
+ def _localized_attributes(filepath, locale)
95
+ fetch_raw_attrs(filepath).dup.select do |key, value|
96
+ %i{title slug fullpath redirect_url template
97
+ seo_title meta_keywords meta_description editable_elements}
98
+ .include? key
99
+
100
+ end.map { |key, item| {key => { "#{locale}".to_sym => item }} }.reduce(:merge)
101
+ end
102
+
103
+ def _base_attributes(filepath)
104
+ fetch_raw_attrs(filepath).dup.select do |key, value|
105
+ %i{content_type redirect_type handle listed searchable
106
+ templatized content_type published cache_strategy response_type
107
+ position}
108
+ .include? key
109
+ end
110
+ end
111
+
112
+ def fetch_raw_attrs(filepath)
113
+ raw_attrs[filepath]
114
+ end
115
+
116
+ def raw_attrs
117
+ @raw_attrs ||= Hash.new do |hsh, filepath|
118
+ attributes = {}
119
+ fullpath = self.filepath_to_fullpath(filepath)
120
+ attributes[:title] = File.basename(fullpath).humanize
121
+ attributes[:fullpath] = fullpath
122
+ attributes[:template] = OpenStruct.new(raw_source: '') if File.directory?(filepath)
123
+ attributes.merge!(get_attributes_from_header(filepath)) unless File.directory?(filepath)
124
+ if content_type_slug = attributes.delete('content_type')
125
+ attributes[:templatized] = true
126
+ attributes[:content_type] = Locomotive::Models[:content_types][content_type_slug]
127
+ end
128
+ hsh[filepath] = attributes
129
+ end
130
+ end
131
+ # Set attributes of a page from the information
132
+ # stored in the header of the template (YAML matters).
133
+ # It also stores the template.
134
+ #
135
+ # @param [ Object ] page The page
136
+ # @param [ String ] filepath The path of the template
137
+ #
138
+ def get_attributes_from_header(filepath)
139
+ {}.tap do |attributes|
140
+ template = Locomotive::Steam::Utils::YAMLFrontMattersTemplate.new(filepath)
141
+ attributes[:template] = template
142
+ if template.attributes
143
+ attributes.merge! template.attributes.symbolize_keys
144
+ end
145
+ end
146
+ end
147
+
148
+ # Return the directory where all the templates of
149
+ # pages are stored in the filesystem.
150
+ #
151
+ # @return [ String ] The root directory
152
+ #
153
+ def root_dir
154
+ @path
155
+ end
156
+
157
+ # Take the path to a file on the filesystem
158
+ # and return its matching value for a Page.
159
+ #
160
+ # @param [ String ] filepath The path to the file
161
+ #
162
+ # @return [ String ] The fullpath of the page
163
+ #
164
+ def filepath_to_fullpath(filepath)
165
+ fullpath = filepath.gsub(File.join(self.root_dir, '/'), '')
166
+
167
+ fullpath.gsub!(/^\.\//, '')
168
+
169
+ fullpath.split('.').first.dasherize
170
+ end
171
+
172
+ # Tell is a page described by its fullpath is a sub page of a parent page
173
+ # also described by its fullpath
174
+ #
175
+ # @param [ String ] fullpath The full path of the page to test
176
+ # @param [ String ] parent_fullpath The full path of the parent page
177
+ #
178
+ # @return [ Boolean] True if the page is a sub page of the parent one
179
+ #
180
+ def is_subpage_of?(fullpath, parent_fullpath)
181
+ return false if %w(index 404).include?(fullpath)
182
+
183
+ if parent_fullpath == 'index' && fullpath.split('/').size == 1
184
+ return true
185
+ end
186
+
187
+ File.dirname(fullpath.dasherize) == parent_fullpath.dasherize
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end