locomotivecms_steam 1.0.0.pre.beta.2 → 1.0.0.pre.beta.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -1
  3. data/Gemfile.lock +22 -12
  4. data/bin/steam.rb +21 -7
  5. data/lib/locomotive/steam/adapters/mongodb.rb +1 -1
  6. data/lib/locomotive/steam/adapters/mongodb/query.rb +6 -0
  7. data/lib/locomotive/steam/configuration.rb +9 -0
  8. data/lib/locomotive/steam/errors.rb +3 -0
  9. data/lib/locomotive/steam/liquid/drops/content_types.rb +20 -2
  10. data/lib/locomotive/steam/liquid/filters/json.rb +42 -0
  11. data/lib/locomotive/steam/liquid/filters/misc.rb +4 -0
  12. data/lib/locomotive/steam/liquid/filters/number.rb +51 -0
  13. data/lib/locomotive/steam/liquid/tags/concerns/i18n_page.rb +0 -9
  14. data/lib/locomotive/steam/liquid/tags/concerns/path.rb +1 -1
  15. data/lib/locomotive/steam/liquid/tags/editable.rb +1 -0
  16. data/lib/locomotive/steam/liquid/tags/editable/base.rb +3 -1
  17. data/lib/locomotive/steam/liquid/tags/editable/model.rb +19 -0
  18. data/lib/locomotive/steam/liquid/tags/inherited_block.rb +25 -1
  19. data/lib/locomotive/steam/middlewares/site.rb +9 -5
  20. data/lib/locomotive/steam/models/entity.rb +9 -8
  21. data/lib/locomotive/steam/models/mapper.rb +22 -6
  22. data/lib/locomotive/steam/repositories/content_entry_repository.rb +15 -1
  23. data/lib/locomotive/steam/repositories/content_type_field_select_option_repository.rb +4 -0
  24. data/lib/locomotive/steam/repositories/page_repository.rb +7 -0
  25. data/lib/locomotive/steam/repositories/site_repository.rb +1 -2
  26. data/lib/locomotive/steam/server.rb +7 -0
  27. data/lib/locomotive/steam/services.rb +10 -11
  28. data/lib/locomotive/steam/services/page_finder_service.rb +21 -0
  29. data/lib/locomotive/steam/version.rb +1 -1
  30. data/spec/integration/repositories/content_entry_repository_spec.rb +5 -0
  31. data/spec/integration/repositories/page_repository_spec.rb +5 -0
  32. data/spec/support/liquid.rb +11 -0
  33. data/spec/unit/liquid/filters/json_spec.rb +57 -0
  34. data/spec/unit/liquid/filters/misc_spec.rb +14 -0
  35. data/spec/unit/liquid/filters/number_spec.rb +100 -0
  36. data/spec/unit/liquid/tags/editable/model_spec.rb +84 -0
  37. data/spec/unit/liquid/tags/inherited_block_spec.rb +13 -0
  38. data/spec/unit/liquid/tags/link_to_spec.rb +2 -2
  39. data/spec/unit/liquid/tags/path_to_spec.rb +3 -3
  40. data/spec/unit/middlewares/site_spec.rb +43 -0
  41. data/spec/unit/repositories/content_entry_repository_spec.rb +13 -12
  42. data/spec/unit/services_spec.rb +23 -1
  43. metadata +13 -2
@@ -2,11 +2,35 @@ module Locomotive
2
2
  module Steam
3
3
  module Liquid
4
4
  module Tags
5
+
6
+ # Blocks are used with the Extends tag to define
7
+ # the content of blocks. Nested blocks are allowed.
8
+ #
9
+ # {% extends home %}
10
+ # {% block content }Hello world{% endblock %}
11
+ #
12
+ # Options used to generate the UI/UX of the editable element inputs
13
+ # - short_name (Boolean): use just the name and skip the name of the nested blocks.
14
+ # - priority (Integer): allow blocks to be displayed before others
15
+ #
5
16
  class InheritedBlock < ::Liquid::InheritedBlock
6
17
 
18
+ def initialize(tag_name, markup, options)
19
+ super
20
+
21
+ @attributes = { short_name: false, priority: 0 }
22
+ markup.scan(::Liquid::TagAttributes) do |key, value|
23
+ @attributes[key.to_sym] = ::Liquid::Expression.parse(value)
24
+ end
25
+ end
26
+
7
27
  def parse(tokens)
8
28
  super.tap do
9
- ActiveSupport::Notifications.instrument('steam.parse.inherited_block', page: options[:page], name: @name, found_super: self.contains_super?(nodelist))
29
+ ActiveSupport::Notifications.instrument('steam.parse.inherited_block', {
30
+ page: options[:page],
31
+ name: @name,
32
+ found_super: self.contains_super?(nodelist)
33
+ }.merge(@attributes))
10
34
  end
11
35
  end
12
36
 
@@ -11,9 +11,7 @@ module Locomotive::Steam
11
11
  def _call
12
12
  site = find_site
13
13
 
14
- # render a simple message if the service was not able to find a site
15
- # based on the request.
16
- render_no_site if site.nil?
14
+ no_site! if site.nil?
17
15
 
18
16
  # log anyway
19
17
  log_site(site)
@@ -30,8 +28,14 @@ module Locomotive::Steam
30
28
  end
31
29
  end
32
30
 
33
- def render_no_site
34
- render_response('Hi, we are sorry but no site was found.', 404, 'text/html')
31
+ def no_site!
32
+ # render a simple message if the service was not able to find a site
33
+ # based on the request.
34
+ if services.configuration.render_404_if_no_site
35
+ render_response('Hi, we are sorry but no site was found.', 404, 'text/html')
36
+ else
37
+ raise NoSiteException.new
38
+ end
35
39
  end
36
40
 
37
41
  def log_site(site)
@@ -12,29 +12,30 @@ module Locomotive::Steam
12
12
  end
13
13
 
14
14
  def method_missing(name, *args, &block)
15
- if attributes.include?(name)
16
- self[name]
17
- elsif name.to_s.end_with?('=') && attributes.include?(name.to_s.chop)
18
- self[name.to_s.chop] = args.first
15
+ _name = name.to_s
16
+ if attributes.include?(_name)
17
+ self[_name]
18
+ elsif _name.end_with?('=') && attributes.include?(_name.chop)
19
+ self[_name.chop] = args.first
19
20
  else
20
21
  super
21
22
  end
22
23
  end
23
24
 
24
25
  def respond_to?(name, include_private = false)
25
- attributes.include?(name) || super
26
+ attributes.include?(name.to_s) || super
26
27
  end
27
28
 
28
29
  def _id
29
- self[:_id]
30
+ self['_id']
30
31
  end
31
32
 
32
33
  def []=(name, value)
33
- attributes[name.to_sym] = value
34
+ attributes[name] = value
34
35
  end
35
36
 
36
37
  def [](name)
37
- attributes[name.to_sym]
38
+ attributes[name]
38
39
  end
39
40
 
40
41
  def serialize
@@ -19,6 +19,8 @@ module Locomotive::Steam
19
19
  @default_attributes = []
20
20
  @associations = []
21
21
 
22
+ @entity_map = {}
23
+
22
24
  instance_eval(&block) if block_given?
23
25
  end
24
26
 
@@ -47,15 +49,17 @@ module Locomotive::Steam
47
49
  end
48
50
 
49
51
  def to_entity(attributes)
50
- entity_klass.new(deserialize(attributes)).tap do |entity|
51
- set_default_attributes(entity)
52
+ cache_entity(entity_klass, attributes) do
53
+ entity_klass.new(deserialize(attributes)).tap do |entity|
54
+ set_default_attributes(entity)
52
55
 
53
- entity.localized_attributes = @localized_attributes_hash || {}
54
- entity.associations = {}
56
+ entity.localized_attributes = @localized_attributes_hash || {}
57
+ entity.associations = {}
55
58
 
56
- attach_entity_to_associations(entity)
59
+ attach_entity_to_associations(entity)
57
60
 
58
- entity.base_url = @repository.base_url(entity)
61
+ entity.base_url = @repository.base_url(entity)
62
+ end
59
63
  end
60
64
  end
61
65
 
@@ -132,6 +136,18 @@ module Locomotive::Steam
132
136
  end
133
137
  end
134
138
 
139
+ def cache_entity(entity_klass, attributes, &block)
140
+ return yield if attributes['_id'].blank?
141
+
142
+ key = "#{entity_klass.to_s}-#{attributes['_id']}"
143
+
144
+ if (entity = @entity_map[key]).nil?
145
+ entity = @entity_map[key] = yield
146
+ end
147
+
148
+ entity
149
+ end
150
+
135
151
  end
136
152
 
137
153
  end
@@ -130,7 +130,21 @@ module Locomotive
130
130
  end
131
131
 
132
132
  def prepare_conditions(*conditions)
133
- super({ _visible: true }, conditions)
133
+ _conditions = conditions.first.try(:with_indifferent_access)
134
+
135
+ prepare_conditions_for_select_fields(_conditions) if _conditions
136
+
137
+ super({ _visible: true }, _conditions)
138
+ end
139
+
140
+ # select fields? if so, use the _id of the option instead of the option name
141
+ def prepare_conditions_for_select_fields(conditions)
142
+ self.content_type.fields.selects.each do |field|
143
+ if value = conditions[name = field.name.to_s]
144
+ conditions.delete(name)
145
+ conditions[name + '_id'] = field.select_options.by_name(value).try(:_id)
146
+ end
147
+ end
134
148
  end
135
149
 
136
150
  def add_localized_fields_to_mapper(mapper)
@@ -18,6 +18,10 @@ module Locomotive
18
18
  query { order_by(position: :asc) }.all
19
19
  end
20
20
 
21
+ def by_name(name)
22
+ query { where(name: name) }.first
23
+ end
24
+
21
25
  end
22
26
  end
23
27
  end
@@ -26,6 +26,13 @@ module Locomotive
26
26
  end.all
27
27
  end
28
28
 
29
+ def only_handle_and_fullpath
30
+ query do
31
+ where(k(:handle, :ne) => nil).
32
+ only(:_id, :title, :handle, :fullpath)
33
+ end.all
34
+ end
35
+
29
36
  def by_handle(handle)
30
37
  first { where(handle: handle) }
31
38
  end
@@ -11,8 +11,7 @@ module Locomotive
11
11
  end
12
12
 
13
13
  def by_domain(domain)
14
- conditions = { k(:domains, :in) => [*domain] }
15
- first { where(conditions) }
14
+ first { where(k(:domains, :in) => [*domain]) }
16
15
  end
17
16
 
18
17
  def by_handle_or_domain(handle, domain)
@@ -12,6 +12,11 @@ require 'dragonfly/middleware'
12
12
 
13
13
  require_relative 'middlewares'
14
14
 
15
+ if ENV['PROFILER']
16
+ require 'moped'
17
+ require 'rack-mini-profiler'
18
+ end
19
+
15
20
  module Locomotive::Steam
16
21
  module Server
17
22
 
@@ -38,6 +43,8 @@ module Locomotive::Steam
38
43
  use Rack::Lint
39
44
  use Rack::Session::Moneta, configuration.moneta
40
45
 
46
+ use Rack::MiniProfiler if ENV['PROFILER']
47
+
41
48
  server.steam_middleware_stack.each { |k| use k }
42
49
  }
43
50
  end
@@ -1,4 +1,4 @@
1
- require 'morphine'
1
+ require 'morphine'
2
2
 
3
3
  require_relative_all %w(concerns .), 'services'
4
4
 
@@ -15,22 +15,17 @@ module Locomotive
15
15
  end
16
16
  end
17
17
 
18
- class SiteProxy < SimpleDelegator
19
-
18
+ class Defer < SimpleDelegator
20
19
  def initialize(&block)
21
- @site = nil
22
- @default = block
23
- super(@site)
20
+ @constructor = block
21
+ super(nil)
24
22
  end
25
-
26
23
  def __getobj__
27
- super || @default.call
24
+ super || __setobj__(@constructor.call)
28
25
  end
29
-
30
26
  def nil?
31
27
  __getobj__.nil?
32
28
  end
33
-
34
29
  end
35
30
 
36
31
  class Instance < Struct.new(:request)
@@ -38,7 +33,7 @@ module Locomotive
38
33
  include Morphine
39
34
 
40
35
  register :current_site do
41
- repositories.current_site = SiteProxy.new { site_finder.find }
36
+ repositories.current_site = Defer.new { site_finder.find }
42
37
  end
43
38
 
44
39
  register :repositories do
@@ -134,6 +129,10 @@ module Locomotive
134
129
  self.current_site.__setobj__(site)
135
130
  end
136
131
 
132
+ def defer(name, &block)
133
+ send(:"#{name}=", Defer.new(&block))
134
+ end
135
+
137
136
  end
138
137
 
139
138
  end
@@ -17,8 +17,29 @@ module Locomotive
17
17
  end
18
18
  end
19
19
 
20
+ def by_handle(handle)
21
+ decorate { page_map[handle] }
22
+ end
23
+
20
24
  private
21
25
 
26
+ # Instead of hitting the DB each time we want a page from its handle,
27
+ # just get all the handles at once and cache the result. (up to 20% boost)
28
+ #
29
+ def page_map
30
+ @page_map ||= {}
31
+
32
+ return @page_map[repository.locale] if @page_map[repository.locale]
33
+
34
+ {}.tap do |map|
35
+ repository.only_handle_and_fullpath.each do |page|
36
+ map[page.handle] = page
37
+ end
38
+
39
+ @page_map[repository.locale] = map
40
+ end
41
+ end
42
+
22
43
  def path_combinations(path)
23
44
  _path_combinations(path.split('/'))
24
45
  end
@@ -3,6 +3,6 @@
3
3
  # 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
4
4
  module Locomotive
5
5
  module Steam
6
- VERSION = '1.0.0-beta.2'
6
+ VERSION = '1.0.0-beta.3'
7
7
  end
8
8
  end
@@ -50,6 +50,11 @@ describe Locomotive::Steam::ContentEntryRepository do
50
50
  it { expect(subject.name).to eq 'Alice in Chains' }
51
51
  end
52
52
 
53
+ describe 'filter by a select field' do
54
+ subject { repository.all(kind: 'grunge') }
55
+ it { expect(subject.map { |entry| entry[:name] }).to eq(['Alice in Chains', 'Pearl Jam']) }
56
+ end
57
+
53
58
  describe '#group_by_select_option' do
54
59
  subject { repository.group_by_select_option(:kind) }
55
60
  it { expect(subject.map { |h| h[:name] }).to eq(%w(grunge rock country)) }
@@ -37,6 +37,11 @@ describe Locomotive::Steam::PageRepository do
37
37
  it { expect(subject.title[:en]).to eq 'Music' }
38
38
  end
39
39
 
40
+ describe '#only_handle_and_fullpath' do
41
+ subject { repository.only_handle_and_fullpath }
42
+ it { expect(subject.size).to eq 3 }
43
+ end
44
+
40
45
  describe '#by_fullpath' do
41
46
  subject { repository.by_fullpath('archives/news') }
42
47
  it { expect(subject.title[:en]).to eq 'News archive' }
@@ -9,6 +9,17 @@ def parse_template(source, options = nil)
9
9
  end
10
10
 
11
11
  module Liquid
12
+
13
+ class TestDrop < Liquid::Drop
14
+ def initialize(source)
15
+ @_source = source
16
+ end
17
+
18
+ def before_method(meth)
19
+ @_source[meth.to_sym]
20
+ end
21
+ end
22
+
12
23
  class SimpleEventsListener
13
24
  def initialize
14
25
  ActiveSupport::Notifications.subscribe(/^steam\.parse\./) do |name, start, finish, id, payload|
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe Locomotive::Steam::Liquid::Filters::Json do
4
+
5
+ include Locomotive::Steam::Liquid::Filters::Json
6
+
7
+ let(:input) { nil }
8
+ subject { json(*input) }
9
+
10
+ describe 'adds quotes to a string' do
11
+
12
+ let(:input) { 'foo' }
13
+ it { expect(subject).to eq %("foo") }
14
+
15
+ end
16
+
17
+ context 'drop' do
18
+
19
+ describe 'includes only the fields specified' do
20
+
21
+ let(:input) { [Liquid::TestDrop.new(title: 'Acme', body: 'Lorem ipsum'), 'title'] }
22
+ it { expect(subject).to eq %("title":"Acme") }
23
+
24
+ end
25
+
26
+ end
27
+
28
+ context 'collections' do
29
+
30
+ describe 'adds brackets and quotes to a collection' do
31
+
32
+ let(:input) { [['foo', 'bar']] }
33
+ it { expect(subject).to eq %(["foo","bar"]) }
34
+
35
+ end
36
+
37
+ describe 'includes the first field' do
38
+
39
+ let(:input) {
40
+ [[Liquid::TestDrop.new(title: 'Acme', body: 'Lorem ipsum'),
41
+ Liquid::TestDrop.new(title: 'Hello world', body: 'Lorem ipsum')], 'title'] }
42
+ it { expect(subject).to eq %("Acme","Hello world") }
43
+
44
+ end
45
+
46
+ describe 'includes the specified fields' do
47
+
48
+ let(:input) {
49
+ [[Liquid::TestDrop.new(title: 'Acme', body: 'Lorem ipsum', date: '2013-12-13'),
50
+ Liquid::TestDrop.new(title: 'Hello world', body: 'Lorem ipsum', date: '2013-12-12')], 'title, body'] }
51
+ it { expect(subject).to eq %({"title":"Acme","body":"Lorem ipsum"},{"title":"Hello world","body":"Lorem ipsum"}) }
52
+
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -105,4 +105,18 @@ describe Locomotive::Steam::Liquid::Filters::Misc do
105
105
 
106
106
  end
107
107
 
108
+ describe '#hexdigest' do
109
+
110
+ let(:key) { 'key' }
111
+ let(:data) { 'The quick brown fox jumps over the lazy dog' }
112
+ let(:digest) { nil }
113
+
114
+ subject { hexdigest(data, key, digest) }
115
+
116
+ it 'returns the authentication code as a hex-encoded string' do
117
+ expect(subject).to eq 'de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9'
118
+ end
119
+
120
+ end
121
+
108
122
  end