searching for content entries with a date/date time/belongs_to criteria works now + get correctly redirected when submitting an invalid entry (when mounted in the Engine) + Pony requires symbolized keys options
did
committed Mar 31, 2016
commit fd93894ce9e35c701a8a9dadb2fa414df29e63f1
Showing 17
changed files with
192 additions
and 23 deletions
locomotive/steam/adapters/filesystem.rb b/lib/locomotive/steam/adapters/filesystem.rb
+4
-0
| @@ | @@ -68,6 +68,10 @@ module Locomotive::Steam |
| '' | |
| end | |
| + | def make_id(value) |
| + | value |
| + | end |
| + | |
| def count(mapper, scope, &block) | |
| query(mapper, scope, &block).count | |
| end | |
locomotive/steam/adapters/mongodb.rb b/lib/locomotive/steam/adapters/mongodb.rb
+6
-1
| @@ | @@ -27,7 +27,8 @@ module Locomotive::Steam |
| end | |
| def find(mapper, scope, id) | |
| - | query(mapper, scope) { where(_id: BSON::ObjectId.from_string(id)) }.first |
| + | _id = make_id(id) |
| + | query(mapper, scope) { where(_id: _id) }.first |
| end | |
| def create(mapper, scope, entity) | |
| @@ | @@ -50,6 +51,10 @@ module Locomotive::Steam |
| name.to_sym.__send__(operator.to_sym) | |
| end | |
| + | def make_id(id) |
| + | BSON::ObjectId.from_string(id) |
| + | end |
| + | |
| def base_url(mapper, scope, entity = nil) | |
| return nil if scope.site.nil? | |
locomotive/steam/entities/content_entry.rb b/lib/locomotive/steam/entities/content_entry.rb
+5
-1
| @@ | @@ -83,7 +83,7 @@ module Locomotive::Steam |
| _attributes += content_type.persisted_field_names | |
| _attributes.inject({}) do |hash, name| | |
| - | hash[name.to_s] = send(name) |
| + | hash[name.to_s] = send(name) rescue nil |
| hash | |
| end.tap do |hash| | |
| # errors? | |
| @@ | @@ -184,6 +184,10 @@ module Locomotive::Steam |
| base.blank? ? filename : "#{base}/#{filename}" | |
| end | |
| + | def to_hash |
| + | { 'url' => url, 'filename' => filename, 'size' => size, 'updated_at' => updated_at } |
| + | end |
| + | |
| def to_json | |
| url | |
| end | |
locomotive/steam/liquid/tags/action.rb b/lib/locomotive/steam/liquid/tags/action.rb
+10
-5
| @@ | @@ -12,11 +12,16 @@ module Locomotive |
| # | |
| # Usage: | |
| # | |
| - | # {% action "" %} |
| - | # {% for post in blog.posts %} |
| - | # {{ post.title }} |
| - | # {% endfor %} |
| - | # {% endconsume %} |
| + | # {% action "My javascript action" %} |
| + | # var lastPost = allEntries('posts', { 'posted_at.lte': getProp('today'), published: true, order_by: 'posted_at desc' })[0]; |
| + | # var views = lastPost.views + 1; |
| + | # |
| + | # updateEntry('posts', lastPost._id, { views: views }); |
| + | # |
| + | # setProp('views', views); |
| + | # {% endaction %} |
| + | # |
| + | # <p>Number of views for the last published post: {{ views }}</p> |
| # | |
| class Action < ::Liquid::Block | |
locomotive/steam/middlewares/entry_submission.rb b/lib/locomotive/steam/middlewares/entry_submission.rb
+1
-1
| @@ | @@ -59,7 +59,7 @@ module Locomotive::Steam |
| if error_location =~ HTTP_REGEXP | |
| redirect_to error_location | |
| else | |
| - | env['PATH_INFO'] = error_location |
| + | env['PATH_INFO'] = make_local_path(error_location) |
| store_in_liquid(entry) | |
| self.next | |
| end | |
locomotive/steam/middlewares/helpers.rb b/lib/locomotive/steam/middlewares/helpers.rb
+8
-0
| @@ | @@ -37,6 +37,14 @@ module Locomotive::Steam |
| path | |
| end | |
| + | # make sure the location passed in parameter doesn't |
| + | # include the "mounted_on" parameter. |
| + | # If so, returns the location without the "mounted_on" string. |
| + | def make_local_path(location) |
| + | return location if mounted_on.blank? |
| + | location.gsub(Regexp.new('^' + mounted_on), '') |
| + | end |
| + | |
| def mounted_on | |
| request.env['steam.mounted_on'] | |
| end | |
locomotive/steam/middlewares/renderer.rb b/lib/locomotive/steam/middlewares/renderer.rb
+2
-1
| @@ | @@ -72,7 +72,8 @@ module Locomotive::Steam |
| 'now' => Time.zone.now, | |
| 'today' => Date.today, | |
| 'mode' => Locomotive::Steam.configuration.mode, | |
| - | 'wagon' => Locomotive::Steam.configuration.mode == :test |
| + | 'wagon' => Locomotive::Steam.configuration.mode == :test, |
| + | 'live_editing' => !!env['steam.live_editing'] |
| } | |
| end | |
locomotive/steam/repositories/content_entry_repository.rb b/lib/locomotive/steam/repositories/content_entry_repository.rb
+23
-4
| @@ | @@ -131,7 +131,7 @@ module Locomotive |
| end | |
| def prepare_conditions(*conditions) | |
| - | _conditions = Conditions.new(conditions.first, self.content_type.fields).prepare |
| + | _conditions = Conditions.new(adapter, conditions.first, self.content_type.fields).prepare |
| super({ _visible: true }, _conditions) | |
| end | |
| @@ | @@ -185,8 +185,10 @@ module Locomotive |
| class Conditions | |
| - | def initialize(conditions = {}, fields) |
| - | @conditions, @fields, @operators = conditions.try(:with_indifferent_access) || {}, fields, {} |
| + | def initialize(adapter, conditions = {}, fields) |
| + | @adapter = adapter |
| + | @conditions = conditions.try(:with_indifferent_access) || {} |
| + | @fields, @operators = fields, {} |
| @conditions.each do |name, value| | |
| _name, operator = name.to_s.split('.') | |
| @@ | @@ -200,6 +202,9 @@ module Locomotive |
| field.select_options.by_name(value).try(:_id) | |
| end | |
| + | # date |
| + | _prepare(@fields.dates_and_date_times) { |field, value| value_to_date(value, field.type) } |
| + | |
| # belongs_to | |
| _prepare(@fields.belongs_to) { |field, value| value_to_id(value) } | |
| @@ | @@ -233,11 +238,25 @@ module Locomotive |
| end | |
| def value_to_id(value) | |
| - | if value.respond_to?(:each) # array |
| + | _value = if value.is_a?(Hash) |
| + | value['_id'] || value[:_id] |
| + | elsif value.respond_to?(:each) # array |
| values_to_ids(value) | |
| else | |
| value.respond_to?(:_id) ? value._id : value | |
| end | |
| + | |
| + | @adapter.make_id(_value) |
| + | end |
| + | |
| + | def value_to_date(value, type) |
| + | _value = if value.is_a?(String) |
| + | Chronic.time_class = Time.zone |
| + | Chronic.parse(value) |
| + | else |
| + | value |
| + | end |
| + | type == :date ? _value.to_date : _value.to_datetime |
| end | |
| def values_to_ids(value) | |
locomotive/steam/repositories/content_type_field_repository.rb b/lib/locomotive/steam/repositories/content_type_field_repository.rb
+4
-0
| @@ | @@ -26,6 +26,10 @@ module Locomotive |
| query { where(type: :file) }.all | |
| end | |
| + | def dates_and_date_times |
| + | query { where(k(:type, :in) => %i(date date_time)) }.all |
| + | end |
| + | |
| def belongs_to | |
| query { where(type: :belongs_to) }.all | |
| end | |
locomotive/steam/services/action_service.rb b/lib/locomotive/steam/services/action_service.rb
+1
-1
| @@ | @@ -51,7 +51,7 @@ module Locomotive |
| end | |
| def send_email_lambda(liquid_context) | |
| - | -> (options) { email.send_email(options.with_indifferent_access, liquid_context) } |
| + | -> (options) { !!email.send_email(options, liquid_context) } |
| end | |
| def get_prop_lambda(liquid_context) | |
locomotive/steam/services/email_service.rb b/lib/locomotive/steam/services/email_service.rb
+3
-2
| @@ | @@ -8,12 +8,12 @@ module Locomotive |
| attr_accessor_initialize :page_finder_service, :liquid_parser, :asset_host, :simulation | |
| def send_email(options, context) | |
| - | build_body(options, context, options.delete(:html)) |
| + | build_body(options.symbolize_keys!, context, options.delete(:html)) |
| extract_attachment(options) | |
| options[:via] ||= :smtp | |
| - | options[:via_options] ||= options.delete(:smtp) |
| + | options[:via_options] ||= options.delete(:smtp).try(:symbolize_keys) |
| log(options, simulation) | |
| @@ | @@ -80,6 +80,7 @@ module Locomotive |
| message << "From: #{options[:from]}" | |
| message << "To: #{options[:to]}" | |
| message << "Subject: #{options[:subject]}" | |
| + | message << "Attachments: #{options[:attachments]}" |
| message << "-----------" | |
| message << (options[:body] || options[:html_body]).gsub("\n", "\n\t") | |
| message << "-----------" | |
spec/unit/adapters/filesystem_adapter_spec.rb
+10
-0
| @@ | @@ -68,4 +68,14 @@ describe Locomotive::Steam::FilesystemAdapter do |
| end | |
| + | describe '#make_id' do |
| + | |
| + | let(:id) { '42' } |
| + | |
| + | subject { adapter.make_id(id) } |
| + | |
| + | it { is_expected.to eq('42') } |
| + | |
| + | end |
| + | |
| end | |
spec/unit/adapters/mongodb_adapter_spec.rb
+18
-0
| @@ | @@ -14,4 +14,22 @@ describe Locomotive::Steam::MongoDBAdapter do |
| end | |
| + | describe '#make_id' do |
| + | |
| + | let(:id) { '56fd9f48a2f42217744a85d7' } |
| + | |
| + | subject { adapter.make_id(id) } |
| + | |
| + | it { is_expected.to eq(BSON::ObjectId.from_string('56fd9f48a2f42217744a85d7')) } |
| + | |
| + | context 'passing a BSON::ObjectId' do |
| + | |
| + | let(:id) { BSON::ObjectId.from_string('56fd9f48a2f42217744a85d7') } |
| + | |
| + | it { is_expected.to eq(BSON::ObjectId.from_string('56fd9f48a2f42217744a85d7')) } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| end | |
spec/unit/entities/content_entry_spec.rb
+19
-0
| @@ | @@ -99,6 +99,25 @@ describe Locomotive::Steam::ContentEntry do |
| end | |
| + | describe '#as_json' do |
| + | |
| + | let(:fields) { [instance_double('TitleField', name: :title, type: :string), instance_double('PictureField', name: :picture, type: :file, localized: true)] } |
| + | let(:attributes) { { id: 42, title: 'Hello world', _slug: 'hello-world', picture: Locomotive::Steam::Models::I18nField.new(:picture, fr: 'foo.png', en: 'bar.png'), custom_fields_recipe: ['hello', 'world'], _type: 'Entry' } } |
| + | let(:decorated) { Locomotive::Steam::Decorators::I18nDecorator.new(content_entry, :fr, :en) } |
| + | |
| + | before do |
| + | allow(type).to receive(:fields_by_name).and_return({ title: fields.first, picture: fields.last }) |
| + | allow(type).to receive(:persisted_field_names).and_return([:title, :picture]) |
| + | allow(content_entry).to receive(:localized_attributes).and_return({ picture: true }) |
| + | allow(content_entry).to receive(:base_url).and_return('/assets') |
| + | end |
| + | |
| + | subject { decorated.as_json } |
| + | |
| + | it { expect(subject['picture']['url']).to eq '/assets/foo.png' } |
| + | |
| + | end |
| + | |
| describe 'dynamic attributes' do | |
| let(:field_type) { :string } | |
spec/unit/middlewares/helpers_spec.rb
+29
-0
| @@ | @@ -7,6 +7,35 @@ describe Locomotive::Steam::Middlewares::Helpers do |
| let(:middleware) { Class.new { include Locomotive::Steam::Middlewares::Helpers } } | |
| let(:instance) { middleware.new } | |
| + | describe '#make_local_path' do |
| + | |
| + | let(:mounted_on) { nil } |
| + | let(:location) { '/foo/bar' } |
| + | |
| + | before { allow(instance).to receive(:mounted_on).and_return(mounted_on) } |
| + | |
| + | subject { instance.make_local_path(location) } |
| + | |
| + | it { is_expected.to eq '/foo/bar' } |
| + | |
| + | context 'mounted_on is not blank' do |
| + | |
| + | let(:mounted_on) { '/my_app' } |
| + | |
| + | it { is_expected.to eq '/foo/bar' } |
| + | |
| + | context 'path including mounted_on' do |
| + | |
| + | let(:location) { '/my_app/foo/bar' } |
| + | |
| + | it { is_expected.to eq '/foo/bar' } |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| describe '#redirect_to' do | |
| subject { instance.redirect_to(location)[1]['Location'] } | |
spec/unit/repositories/content_entry_repository_spec.rb
+39
-7
| @@ | @@ -4,7 +4,7 @@ require_relative '../../../lib/locomotive/steam/adapters/filesystem.rb' |
| describe Locomotive::Steam::ContentEntryRepository do | |
| - | let(:_fields) { instance_double('Fields', selects: [], belongs_to: [], many_to_many: []) } |
| + | let(:_fields) { instance_double('Fields', selects: [], belongs_to: [], many_to_many: [], dates_and_date_times: []) } |
| let(:type) { build_content_type('Articles', label_field_name: :title, localized_names: [:title], fields: _fields, fields_by_name: { title: instance_double('Field', name: :title, type: :string) }, fields_with_default: []) } | |
| let(:entries) { [{ content_type_id: 1, _position: 0, _label: 'Update #1', title: { fr: 'Mise a jour #1' }, text: { en: 'added some free stuff', fr: 'phrase FR' }, date: '2009/05/12', category: 'General' }] } | |
| let(:locale) { :en } | |
| @@ | @@ -274,7 +274,7 @@ describe Locomotive::Steam::ContentEntryRepository do |
| let(:other_type) { build_content_type('Authors', _id: 2, label_field_name: :name, fields: _fields, fields_by_name: { name: instance_double('Field', name: :name, type: :string) }, fields_with_default: []) } | |
| let(:other_entries) { [{ content_type_id: 2, _id: 'john-doe', name: 'John Doe' }] } | |
| - | let(:type_repository) { instance_double('ArticleBelongsToRepository', selects: [], belongs_to: [], many_to_many: []) } |
| + | let(:type_repository) { instance_double('ArticleBelongsToRepository', selects: [], belongs_to: [], many_to_many: [], dates_and_date_times: []) } |
| before do | |
| allow(type).to receive(:fields).and_return(type_repository) | |
| @@ | @@ -307,7 +307,7 @@ describe Locomotive::Steam::ContentEntryRepository do |
| ] | |
| } | |
| - | let(:type_repository) { instance_double('AuthorRepository', selects: [], belongs_to: [], many_to_many: []) } |
| + | let(:type_repository) { instance_double('AuthorRepository', selects: [], belongs_to: [], many_to_many: [], dates_and_date_times: []) } |
| before do | |
| allow(type).to receive(:fields).and_return(type_repository) | |
| @@ | @@ -340,7 +340,7 @@ describe Locomotive::Steam::ContentEntryRepository do |
| ] | |
| } | |
| - | let(:type_repository) { instance_double('AuthorRepository', selects: [], belongs_to: [], many_to_many: []) } |
| + | let(:type_repository) { instance_double('AuthorRepository', selects: [], belongs_to: [], many_to_many: [], dates_and_date_times: []) } |
| before do | |
| allow(type).to receive(:fields).and_return(type_repository) | |
| @@ | @@ -373,18 +373,42 @@ describe Locomotive::Steam::ContentEntryRepository do |
| let(:option) { instance_double('Option', _id: 42)} | |
| let(:options) { instance_double('OptionRepository', by_name: option) } | |
| let(:field) { instance_double('SelectField', name: 'category', persisted_name: 'category_id', select_options: options) } | |
| - | let(:_fields) { instance_double('Fields', selects: [field], belongs_to: [], many_to_many: []) } |
| + | let(:_fields) { instance_double('Fields', selects: [field], belongs_to: [], many_to_many: [], dates_and_date_times: []) } |
| let(:conditions) { { 'category' => value } } | |
| it { is_expected.to eq([{ _visible: true, content_type_id: 1, 'category_id' => 42 }, nil]) } | |
| end | |
| + | context 'date fields' do |
| + | |
| + | let(:value) { '2009/09/10' } |
| + | let(:field) { instance_double('DateField', name: 'launched_at', persisted_name: 'launched_at', type: :date) } |
| + | let(:_fields) { instance_double('Fields', selects: [], belongs_to: [], many_to_many: [], dates_and_date_times: [field]) } |
| + | let(:conditions) { { 'launched_at' => value } } |
| + | |
| + | it { is_expected.to eq([{ _visible: true, content_type_id: 1, 'launched_at' => Date.parse('2009/09/10') }, nil]) } |
| + | |
| + | end |
| + | |
| + | context 'date time fields' do |
| + | |
| + | before { Time.zone = 'Paris' } |
| + | |
| + | let(:value) { '2007/06/29 21:15:00' } |
| + | let(:field) { instance_double('DateField', name: 'launched_at', persisted_name: 'launched_at', type: :date_time) } |
| + | let(:_fields) { instance_double('Fields', selects: [], belongs_to: [], many_to_many: [], dates_and_date_times: [field]) } |
| + | let(:conditions) { { 'launched_at' => value } } |
| + | |
| + | it { is_expected.to eq([{ _visible: true, content_type_id: 1, 'launched_at' => Time.zone.parse('2007/06/29 21:15:00').to_datetime }, nil]) } |
| + | |
| + | end |
| + | |
| context 'belongs_to fields' do | |
| let(:value) { 42 } | |
| let(:field) { instance_double('BelongsToField', name: 'person', persisted_name: 'person_id') } | |
| - | let(:_fields) { instance_double('Fields', selects: [], belongs_to: [field], many_to_many: []) } |
| + | let(:_fields) { instance_double('Fields', selects: [], belongs_to: [field], many_to_many: [], dates_and_date_times: []) } |
| let(:conditions) { { 'person' => value } } | |
| it { is_expected.to eq([{ _visible: true, content_type_id: 1, 'person_id' => 42 }, nil]) } | |
| @@ | @@ -397,6 +421,14 @@ describe Locomotive::Steam::ContentEntryRepository do |
| end | |
| + | context 'the target is a hash' do |
| + | |
| + | let(:value) { { '_id' => 42 } } |
| + | |
| + | it { is_expected.to eq([{ _visible: true, content_type_id: 1, 'person_id' => 42 }, nil]) } |
| + | |
| + | end |
| + | |
| context 'the target value is an arry of content entry' do | |
| let(:value) { [instance_double('TargetContentEntry', _id: 1), instance_double('TargetContentEntry', _id: 2)] } | |
| @@ | @@ -419,7 +451,7 @@ describe Locomotive::Steam::ContentEntryRepository do |
| let(:value) { 42 } | |
| let(:field) { instance_double('ManyToManyField', name: 'tags', persisted_name: 'tag_ids') } | |
| - | let(:_fields) { instance_double('Fields', selects: [], belongs_to: [], many_to_many: [field]) } |
| + | let(:_fields) { instance_double('Fields', selects: [], belongs_to: [], many_to_many: [field], dates_and_date_times: []) } |
| let(:conditions) { { 'tags.in' => value } } | |
| it { is_expected.to eq([{ _visible: true, content_type_id: 1, 'tag_ids.in' => [42] }, nil]) } | |
spec/unit/repositories/content_type_field_repository_spec.rb
+10
-0
| @@ | @@ -70,4 +70,14 @@ describe Locomotive::Steam::ContentTypeFieldRepository do |
| end | |
| + | describe '#dates_and_date_times' do |
| + | |
| + | let(:collection) { [{ name: 'name', type: 'string' }, { name: 'launched_at', type: 'date' }, { name: 'updated_at', type: 'date_time' }] } |
| + | |
| + | subject { repository.dates_and_date_times } |
| + | |
| + | it { expect(subject.map(&:name)).to eq(['launched_at', 'updated_at']) } |
| + | |
| + | end |
| + | |
| end | |