intermediate commit: very simple MongoDB adapter, refactor each repository one by one. Tests are broken

did committed Feb 21, 2015
commit 66ab7acb016c6b472294346876584192ae376585
Showing 38 changed files with 1529 additions and 605 deletions
Gemfile.lock +10 -0
@@ @@ -50,6 +50,7 @@ GEM
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.3.7)
+ bson (2.3.0)
builder (3.2.2)
byebug (3.5.1)
columnize (~> 0.8)
@@ @@ -78,6 +79,7 @@ GEM
sass (>= 3.3.0, < 3.5)
compass-import-once (1.0.5)
sass (>= 3.2, < 3.5)
+ connection_pool (2.1.1)
coveralls (0.7.3)
multi_json (~> 1.10)
rest-client (~> 1.7)
@@ @@ -129,6 +131,10 @@ GEM
mini_portile (0.6.2)
minitest (5.5.1)
moneta (0.8.0)
+ moped (2.0.4)
+ bson (~> 2.2)
+ connection_pool (~> 2.0)
+ optionable (~> 0.2.0)
morphine (0.1.1)
multi_json (1.10.1)
multi_xml (0.5.5)
@@ @@ -137,6 +143,8 @@ GEM
mini_portile (~> 0.6.0)
nokogumbo (1.2.0)
nokogiri
+ optionable (0.2.0)
+ origin (1.0.11)
pry (0.10.1)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
@@ @@ -223,6 +231,8 @@ DEPENDENCIES
i18n-spec (~> 0.6.0)
json_spec (~> 1.1.4)
locomotivecms_steam!
+ moped (~> 2.0.4)
+ origin (~> 1.0.4)
pry-byebug
rake (~> 10.4.2)
rspec (~> 3.1.0)
Rakefile +0 -4
@@ @@ -24,8 +24,4 @@ RSpec::Core::RakeTask.new('spec:unit') do |spec|
spec.pattern = 'spec/unit/**/*_spec.rb'
end
- RSpec::Core::RakeTask.new('spec:adapters') do |spec|
- spec.pattern = 'spec/unit/adapters/**/*_spec.rb'
- end
-
task default: :spec
locomotive/steam.rb b/lib/locomotive/steam.rb +7 -3
@@ @@ -11,9 +11,13 @@ require_relative_all 'steam/monkey_patches'
require_relative_all 'steam/decorators'
require_relative 'steam/liquid'
- require_relative 'steam/entities'
- require_relative 'steam/mapper'
- require_relative 'steam/repositories'
+ require_relative 'steam/models'
+ require_relative_all 'steam/entities'
+
+ # TODO
+ require_relative 'steam/repositories/site_repository'
+ require_relative 'steam/repositories/page_repository'
+
require_relative 'steam/services'
module Locomotive
locomotive/steam/adapters/filesystem.rb b/lib/locomotive/steam/adapters/filesystem.rb +11 -3
@@ @@ -1,4 +1,3 @@
- # require_relative_all 'yaml_loaders'
require_relative 'filesystem/dataset'
require_relative 'filesystem/condition'
require_relative 'filesystem/query'
@@ @@ -7,6 +6,7 @@ require_relative 'filesystem/simple_cache_store'
require_relative 'filesystem/yaml_loader'
require_relative 'filesystem/yaml_loaders/site'
+ require_relative 'filesystem/yaml_loaders/page'
module Locomotive::Steam
@@ @@ -27,12 +27,20 @@ module Locomotive::Steam
memoized_dataset(mapper)
end
- def query(mapper, locale, &block)
- Locomotive::Steam::Adapters::Filesystem::Query.new(all(mapper), locale, &block)
+ def query(mapper, scope, &block)
+ _query(mapper, scope.locale, &block).tap do |default|
+ if scope.site
+ default + _query(mapper) { where(site_id: scope.site.id) }
+ end
+ end
end
private
+ def _query(mapper, locale = nil, &block)
+ Locomotive::Steam::Adapters::Filesystem::Query.new(all(mapper), locale, &block)
+ end
+
def memoized_dataset(mapper)
return @datasets[mapper.name] if @datasets[mapper.name]
dataset(mapper)
locomotive/steam/adapters/filesystem/condition.rb b/lib/locomotive/steam/adapters/filesystem/condition.rb +74 -71
@@ @@ -1,100 +1,103 @@
- module Locomotive
- module Steam
- module Adapters
- module Filesystem
+ module Locomotive::Steam
+ module Adapters
+ module Filesystem
- class Condition
+ class Condition
- class UnsupportedOperator < StandardError; end
+ class UnsupportedOperator < StandardError; end
- OPERATORS = %i(== eq ne neq matches gt gte lt lte size all in nin).freeze
+ OPERATORS = %i(== eq ne neq matches gt gte lt lte size all in nin).freeze
- attr_reader :field, :operator, :value
+ attr_reader :field, :operator, :value
- def initialize(operator_and_field, value, locale)
- @locale = locale.try(:to_sym)
- @operator_and_field, @value = operator_and_field, value
- @operator, @field = :==, operator_and_field
+ def initialize(operator_and_field, value, locale)
+ @locale = locale.try(:to_sym)
+ @operator_and_field, @value = operator_and_field, value
+ @operator, @field = :==, operator_and_field
- decode_operator_and_field!
- end
+ decode_operator_and_field!
+ end
- def matches?(entry)
- entry_value = entry_value(entry)
-
- adapt_operator!(entry_value)
- case @operator
- when :== then entry_value == @value
- when :eq then entry_value == @value
- when :ne then entry_value != @value
- when :neq then entry_value != @value
- when :matches then @value =~ entry_value
- when :gt then entry_value > @value
- when :gte then entry_value >= @value
- when :lt then entry_value < @value
- when :lte then entry_value <= @value
- when :size then entry_value.size == @value
- when :all then array_contains?([*@value], entry_value)
- when :in, :nin then value_is_in_entry_value?(entry_value)
- else
- raise UnknownConditionInScope.new("#{@operator} is unknown or not implemented.")
- end
+ def matches?(entry)
+ entry_value = entry_value(entry)
+
+ adapt_operator!(entry_value)
+ case @operator
+ when :== then entry_value == @value
+ when :eq then entry_value == @value
+ when :ne then entry_value != @value
+ when :neq then entry_value != @value
+ when :matches then @value =~ entry_value
+ when :gt then entry_value > @value
+ when :gte then entry_value >= @value
+ when :lt then entry_value < @value
+ when :lte then entry_value <= @value
+ when :size then entry_value.size == @value
+ when :all then array_contains?([*@value], entry_value)
+ when :in, :nin then value_is_in_entry_value?(entry_value)
+ else
+ raise UnknownConditionInScope.new("#{@operator} is unknown or not implemented.")
end
+ end
- def to_s
- "#{field} #{operator} #{@value.to_s}"
- end
+ def to_s
+ "#{field} #{operator} #{@value.to_s}"
+ end
- protected
+ protected
- def entry_value(entry)
- case (value = entry.send(@field))
- when Hash
- value.fetch(@locale) { nil }
- else
- value
- end
- end
+ def entry_value(entry)
+ value = entry.send(@field)
- def decode_operator_and_field!
- if match = @operator_and_field.match(/^(?<field>[a-z0-9_-]+)\.(?<operator>.*)$/)
- @field = match[:field].to_sym
- @operator = match[:operator].to_sym
- check_operator!
- end
+ if value.respond_to?(:translations)
+ value[@locale]
+ else
+ value
+ end
+ end
- @operator = :matches if @value.is_a?(Regexp)
+ def decode_operator_and_field!
+ if match = @operator_and_field.match(/^(?<field>[a-z0-9_-]+)\.(?<operator>.*)$/)
+ @field = match[:field].to_sym
+ @operator = match[:operator].to_sym
+ check_operator!
end
- def adapt_operator!(value)
- case value
- when Array
- @operator = :in if @operator == :==
- end
+ @operator = :matches if @value.is_a?(Regexp)
+ end
+
+ def adapt_operator!(value)
+ case value
+ when Array
+ @operator = :in if @operator == :==
end
+ end
- def value_is_in_entry_value?(value)
- _matches = if value.is_a?(Array)
- array_contains?([*value], [*@value])
- else
- [*@value].include?(value)
- end
- @operator == :in ? _matches : !_matches
+ def value_is_in_entry_value?(value)
+ _matches = if value.is_a?(Array)
+ array_contains?([*value], [*@value])
+ else
+ [*@value].include?(value)
end
+ @operator == :in ? _matches : !_matches
+ end
- private
+ private
- def check_operator!
- raise UnsupportedOperator.new unless OPERATORS.include?(@operator)
- end
+ def check_operator!
+ raise UnsupportedOperator.new unless OPERATORS.include?(@operator)
+ end
- def array_contains?(source, target)
+ def array_contains?(source, target)
+ if target.size == 0
+ source.size == 0
+ else
source & target == target
end
-
end
end
+
end
end
end
locomotive/steam/adapters/filesystem/dataset.rb b/lib/locomotive/steam/adapters/filesystem/dataset.rb +53 -55
@@ @@ -1,75 +1,73 @@
- module Locomotive
- module Steam
- module Adapters
- module Filesystem
-
- class Dataset
-
- class PrimaryKey
- def initialize
- @current = 0
- end
-
- def increment!
- yield(@current += 1)
- @current
- end
- end
+ module Locomotive::Steam
+ module Adapters
+ module Filesystem
- attr_reader :records, :name
+ class Dataset
- def initialize(name)
- @name = name
- clear!
+ class PrimaryKey
+ def initialize
+ @current = 0
end
- def insert(record)
- @primary_key.increment! do |id|
- record[identity] = id
- records[id] = record
- end
+ def increment!
+ yield(@current += 1)
+ @current
end
+ end
- def update(record)
- records[record[identity]] = records[record[identity]].deep_merge(record)
- end
+ attr_reader :records, :name
- def delete(id)
- records.delete(id)
- end
+ def initialize(name)
+ @name = name
+ clear!
+ end
- def size
- records.size
+ def insert(record)
+ @primary_key.increment! do |id|
+ record[identity] = id
+ records[id] = record
end
+ end
- def all
- records.values
- end
+ def update(record)
+ records[record[identity]] = records[record[identity]].deep_merge(record)
+ end
- def find(id)
- records.fetch(id) do
- raise Locomotive::Steam::Repository::RecordNotFound, "could not find #{name} with #{identity} = #{id}"
- end
- end
+ def delete(id)
+ records.delete(id)
+ end
- def exists?(id)
- !!id && records.has_key?(id)
- end
+ def size
+ records.size
+ end
- # def query
- # Query.new(self)
- # end
+ def all
+ records.values
+ end
- def clear!
- @records = {}
- @primary_key = PrimaryKey.new
+ def find(id)
+ records.fetch(id) do
+ raise Locomotive::Steam::Repository::RecordNotFound, "could not find #{name} with #{identity} = #{id}"
end
+ end
+
+ def exists?(id)
+ !!id && records.has_key?(id)
+ end
- private
+ # def query
+ # Query.new(self)
+ # end
- def identity
- @identity ||= :_id
- end
+ def clear!
+ @records = {}
+ @primary_key = PrimaryKey.new
+ end
+
+ private
+
+ def identity
+ @identity ||= :_id
end
end
end
locomotive/steam/adapters/filesystem/query.rb b/lib/locomotive/steam/adapters/filesystem/query.rb +77 -79
@@ @@ -1,110 +1,108 @@
require 'forwardable'
- module Locomotive
- module Steam
- module Adapters
- module Filesystem
+ module Locomotive::Steam
+ module Adapters
+ module Filesystem
- class Query
+ class Query
- include Enumerable
- extend Forwardable
+ include Enumerable
+ extend Forwardable
- def_delegators :all, :each, :to_s, :to_a, :empty?, :size
+ def_delegators :all, :each, :to_s, :to_a, :empty?, :size
- alias :length :size
- alias :count :size
+ alias :length :size
+ alias :count :size
- attr_reader :conditions
+ attr_reader :conditions
- def initialize(dataset, locale = nil, &block)
- @dataset = dataset
- @conditions = []
- @sorting = nil
- @limit = nil
- @offset = 0
- @locale = locale
- instance_eval(&block) if block_given?
- end
+ def initialize(dataset, locale = nil, &block)
+ @dataset = dataset
+ @conditions = []
+ @sorting = nil
+ @limit = nil
+ @offset = 0
+ @locale = locale
+ instance_eval(&block) if block_given?
+ end
- def where(conditions = {})
- @conditions += conditions.map { |name, value| Condition.new(name, value, @locale) }
- self
- end
+ def where(conditions = {})
+ @conditions += conditions.map { |name, value| Condition.new(name, value, @locale) }
+ self
+ end
- def +(query)
- @conditions += query.conditions
- self
- end
+ def +(query)
+ @conditions += query.conditions
+ self
+ end
- def order_by(order_string)
- unless order_string.blank?
- pattern = order_string.include?('.') ? '.' : ' '
- @sorting = order_string.downcase.split(pattern).map(&:to_sym)
- end
- self
+ def order_by(order_string)
+ unless order_string.blank?
+ pattern = order_string.include?('.') ? '.' : ' '
+ @sorting = order_string.downcase.split(pattern).map(&:to_sym)
end
+ self
+ end
- def limit(num)
- @limit = num
- self
- end
+ def limit(num)
+ @limit = num
+ self
+ end
- def offset(num)
- @offset = num
- self
- end
+ def offset(num)
+ @offset = num
+ self
+ end
- def ==(other)
- if other.kind_of? Array
- all == other
- else
- super
- end
+ def ==(other)
+ if other.kind_of? Array
+ all == other
+ else
+ super
end
+ end
- def all
- limited sorted(filtered)
- end
+ def all
+ limited sorted(filtered)
+ end
- def sorted(entries)
- return entries if @sorting.nil?
+ def sorted(entries)
+ return entries if @sorting.nil?
- name, direction = @sorting.first, (@sorting.last || :asc)
- if direction == :asc
- entries.sort { |a, b| a.send(name) <=> b.send(name) }
- else
- entries.sort { |a, b| b.send(name) <=> a.send(name) }
- end
+ name, direction = @sorting.first, (@sorting.last || :asc)
+ if direction == :asc
+ entries.sort { |a, b| a.send(name) <=> b.send(name) }
+ else
+ entries.sort { |a, b| b.send(name) <=> a.send(name) }
end
+ end
- def limited(entries)
- return [] if @limit == 0
- return entries if @offset == 0 && @limit.nil?
+ def limited(entries)
+ return [] if @limit == 0
+ return entries if @offset == 0 && @limit.nil?
- subentries = entries.drop(@offset || 0)
- if @limit.kind_of? Integer
- subentries.take(@limit)
- else
- subentries
- end
+ subentries = entries.drop(@offset || 0)
+ if @limit.kind_of? Integer
+ subentries.take(@limit)
+ else
+ subentries
end
+ end
- def filtered
- @dataset.all.dup.find_all do |entry|
- accepted = true
+ def filtered
+ @dataset.all.dup.find_all do |entry|
+ accepted = true
- @conditions.each do |_condition|
- unless _condition.matches?(entry)
- accepted = false
- break # no to go further
- end
+ @conditions.each do |_condition|
+ unless _condition.matches?(entry)
+ accepted = false
+ break # no to go further
end
- accepted
end
- end # filtered
+ accepted
+ end
+ end # filtered
- end
end
end
end
locomotive/steam/adapters/filesystem/simple_cache_store.rb b/lib/locomotive/steam/adapters/filesystem/simple_cache_store.rb +24 -26
@@ @@ -1,40 +1,38 @@
- module Locomotive
- module Steam
- module Adapters
- module Filesystem
+ module Locomotive::Steam
+ module Adapters
+ module Filesystem
- class SimpleCacheStore
+ class SimpleCacheStore
- @@store = {}
+ @@store = {}
- def fetch(name, options = nil, &block)
- if block_given?
- read(name) || write(name, yield)
- else
- read(name)
- end
- end
-
- def read(name, options = nil)
- @@store[name]
+ def fetch(name, options = nil, &block)
+ if block_given?
+ read(name) || write(name, yield)
+ else
+ read(name)
end
+ end
- def write(name, value, options = nil)
- @@store[name] = value
- end
+ def read(name, options = nil)
+ @@store[name]
+ end
- def clear
- @@store.clear
- end
+ def write(name, value, options = nil)
+ @@store[name] = value
+ end
- #:nocov:
- def _store
- @@store
- end
+ def clear
+ @@store.clear
+ end
+ #:nocov:
+ def _store
+ @@store
end
end
+
end
end
end
locomotive/steam/adapters/filesystem/yaml_loader.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loader.rb +24 -26
@@ @@ -1,39 +1,37 @@
- module Locomotive
- module Steam
- module Adapters
- module Filesystem
+ module Locomotive::Steam
+ module Adapters
+ module Filesystem
- module YAMLLoader
+ module YAMLLoader
- def fetch(key, &block)
- cache.nil? ? yield : cache.fetch(key, &block)
- end
+ def fetch(key, &block)
+ cache.nil? ? yield : cache.fetch(key, &block)
+ end
- def _load(path, frontmatter = false, &block)
- if File.exists?(path)
- yaml = File.open(path).read.force_encoding('utf-8')
- template = nil
-
- if frontmatter && match = yaml.match(FRONTMATTER_REGEXP)
- yaml, template = match[:yaml], match[:template]
- end
-
- HashConverter.to_sym(YAML.load(yaml)).tap do |attributes|
- block.call(attributes, template) if block_given?
- end
- else
- Locomotive::Common::Logger.error "No #{path} file found"
- {}
+ def _load(path, frontmatter = false, &block)
+ if File.exists?(path)
+ yaml = File.open(path).read.force_encoding('utf-8')
+ template = nil
+
+ if frontmatter && match = yaml.match(FRONTMATTER_REGEXP)
+ yaml, template = match[:yaml], match[:template]
end
- end
- def template_extensions
- @extensions ||= %w(liquid haml)
+ HashConverter.to_sym(YAML.load(yaml)).tap do |attributes|
+ block.call(attributes, template) if block_given?
+ end
+ else
+ Locomotive::Common::Logger.error "No #{path} file found"
+ {}
end
+ end
+ def template_extensions
+ @extensions ||= %w(liquid haml)
end
end
+
end
end
end
locomotive/steam/adapters/filesystem/yaml_loaders/page.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/page.rb +109 -108
@@ @@ -1,108 +1,109 @@
- # module Locomotive
- # module Steam
- # module Repositories
- # module Filesystem
- # module YAMLLoaders
-
- # class Page < Struct.new(:root_path, :default_locale, :cache)
-
- # include YAMLLoaders::Concerns::Common
-
- # def list_of_attributes
- # cache.fetch('app/views/pages') { load_tree }
- # end
-
- # private
-
- # def path
- # @path ||= File.join(root_path, 'app', 'views', 'pages')
- # end
-
- # def load_tree
- # {}.tap do |hash|
- # each_file do |filepath, relative_path, fullpath, locale|
-
- # if leaf = hash[fullpath]
- # update(leaf, filepath, fullpath, locale)
- # else
- # leaf = build(filepath, fullpath, locale)
- # end
-
- # hash[fullpath] = leaf
- # end
- # end.values
- # end
-
- # def build(filepath, fullpath, locale)
- # slug = fullpath.split('/').last
- # attributes = get_attributes(filepath, fullpath)
-
- # {
- # title: { locale => attributes.delete(:title) || (default_locale == locale ? slug.humanize : nil) },
- # slug: { locale => attributes.delete(:slug) || slug },
- # editable_elements: { locale => attributes.delete(:editable_elements) },
- # template_path: { locale => template_path(filepath, attributes, locale) },
- # redirect_url: { locale => attributes.delete(:redirect_url) },
- # _fullpath: fullpath
- # }.merge(attributes)
- # end
-
- # def update(leaf, filepath, fullpath, locale)
- # slug = fullpath.split('/').last
- # attributes = get_attributes(filepath, fullpath)
-
- # leaf[:title][locale] = attributes.delete(:title) || slug.humanize
- # leaf[:slug][locale] = attributes.delete(:slug) || slug
- # leaf[:editable_elements][locale] = attributes.delete(:editable_elements)
- # leaf[:template_path][locale] = template_path(filepath, attributes, locale)
- # leaf[:redirect_url][locale] = attributes.delete(:redirect_url)
-
- # leaf.merge!(attributes)
- # end
-
- # def get_attributes(filepath, fullpath)
- # load(filepath, true) do |attributes, template|
- # # make sure index/404 are the slugs of the index/404 pages
- # attributes.delete(:slug) if %w(index 404).include?(fullpath)
-
- # # trick to use the template of the default locale (if available)
- # attributes[:template_path] = false if template.blank?
- # end
- # end
-
- # def each_file(&block)
- # Dir.glob(File.join(path, '**', '*')).each do |filepath|
- # next unless is_liquid_file?(filepath)
-
- # relative_path = get_relative_path(filepath)
-
- # fullpath, locale = relative_path.split('.')[0..1]
- # locale = default_locale if template_extensions.include?(locale)
-
- # yield(filepath, relative_path, fullpath, locale.to_sym)
- # end
- # end
-
- # def is_liquid_file?(filepath)
- # filepath =~ /\.(#{template_extensions.join('|')})$/
- # end
-
- # def template_path(filepath, attributes, locale)
- # if attributes.delete(:template_path) == false && locale != default_locale
- # false
- # else
- # filepath
- # end
- # end
-
- # def get_relative_path(filepath)
- # filepath.gsub(path, '').gsub(/^\//, '')
- # end
-
- # end
-
- # end
- # end
- # end
- # end
- # end
+ module Locomotive
+ module Steam
+ module Adapters
+ module Filesystem
+ module YAMLLoaders
+
+ # class Page < Struct.new(:root_path, :default_locale, :cache)
+ class Page < Struct.new(:site_path, :default_locale, :cache)
+
+ include Adapters::Filesystem::YAMLLoader
+
+ def load
+ fetch('app/views/pages') { load_tree }
+ end
+
+ private
+
+ def path
+ @path ||= File.join(root_path, 'app', 'views', 'pages')
+ end
+
+ def load_tree
+ {}.tap do |hash|
+ each_file do |filepath, relative_path, fullpath, locale|
+
+ if leaf = hash[fullpath]
+ update(leaf, filepath, fullpath, locale)
+ else
+ leaf = build(filepath, fullpath, locale)
+ end
+
+ hash[fullpath] = leaf
+ end
+ end.values
+ end
+
+ def build(filepath, fullpath, locale)
+ slug = fullpath.split('/').last
+ attributes = get_attributes(filepath, fullpath)
+
+ {
+ title: { locale => attributes.delete(:title) || (default_locale == locale ? slug.humanize : nil) },
+ slug: { locale => attributes.delete(:slug) || slug },
+ editable_elements: { locale => attributes.delete(:editable_elements) },
+ template_path: { locale => template_path(filepath, attributes, locale) },
+ redirect_url: { locale => attributes.delete(:redirect_url) },
+ _fullpath: fullpath
+ }.merge(attributes)
+ end
+
+ def update(leaf, filepath, fullpath, locale)
+ slug = fullpath.split('/').last
+ attributes = get_attributes(filepath, fullpath)
+
+ leaf[:title][locale] = attributes.delete(:title) || slug.humanize
+ leaf[:slug][locale] = attributes.delete(:slug) || slug
+ leaf[:editable_elements][locale] = attributes.delete(:editable_elements)
+ leaf[:template_path][locale] = template_path(filepath, attributes, locale)
+ leaf[:redirect_url][locale] = attributes.delete(:redirect_url)
+
+ leaf.merge!(attributes)
+ end
+
+ def get_attributes(filepath, fullpath)
+ _load(filepath, true) do |attributes, template|
+ # make sure index/404 are the slugs of the index/404 pages
+ attributes.delete(:slug) if %w(index 404).include?(fullpath)
+
+ # trick to use the template of the default locale (if available)
+ attributes[:template_path] = false if template.blank?
+ end
+ end
+
+ def each_file(&block)
+ Dir.glob(File.join(path, '**', '*')).each do |filepath|
+ next unless is_liquid_file?(filepath)
+
+ relative_path = get_relative_path(filepath)
+
+ fullpath, locale = relative_path.split('.')[0..1]
+ locale = default_locale if template_extensions.include?(locale)
+
+ yield(filepath, relative_path, fullpath, locale.to_sym)
+ end
+ end
+
+ def is_liquid_file?(filepath)
+ filepath =~ /\.(#{template_extensions.join('|')})$/
+ end
+
+ def template_path(filepath, attributes, locale)
+ if attributes.delete(:template_path) == false && locale != default_locale
+ false
+ else
+ filepath
+ end
+ end
+
+ def get_relative_path(filepath)
+ filepath.gsub(path, '').gsub(/^\//, '')
+ end
+
+ end
+
+ end
+ end
+ end
+ end
+ end
locomotive/steam/adapters/mongodb.rb b/lib/locomotive/steam/adapters/mongodb.rb +46 -0
@@ @@ -0,0 +1,46 @@
+ require 'moped'
+ require 'origin'
+
+ require_relative 'mongodb/origin'
+ require_relative 'mongodb/query'
+
+ module Locomotive::Steam
+
+ class MongoDBAdapter < Struct.new(:database, :hosts)
+
+ def all(mapper, selector = nil)
+ dataset(mapper, selector)
+ end
+
+ def query(mapper, scope, &block)
+ query = query_klass.new(scope, mapper.localized_attributes, &block)
+ all(mapper, query.selector)
+ end
+
+ private
+
+ def query_klass
+ Locomotive::Steam::Adapters::MongoDB::Query
+ end
+
+ def dataset(mapper, selector = nil)
+ collection(mapper).find(selector).map do |attributes|
+ entity = mapper.to_entity(attributes)
+ end
+ end
+
+ def collection(mapper)
+ session["locomotive_#{mapper.name}"]
+ end
+
+ def session
+ Moped::Session.new([*hosts]).tap do |session|
+ session.use database
+ end
+ end
+
+ end
+
+ end
+
+
locomotive/steam/adapters/mongodb/origin.rb b/lib/locomotive/steam/adapters/mongodb/origin.rb +3 -0
@@ @@ -0,0 +1,3 @@
+ class Origin::Query
+ include Origin::Queryable
+ end
locomotive/steam/adapters/mongodb/query.rb b/lib/locomotive/steam/adapters/mongodb/query.rb +35 -0
@@ @@ -0,0 +1,35 @@
+ module Locomotive::Steam
+ module Adapters
+ module MongoDB
+
+ class Query
+
+ def initialize(scope, localized_attributes, &block)
+ @query = ::Origin::Query.new
+ @scope = scope
+ @localized_attributes = localized_attributes
+
+ apply_default_scope
+
+ instance_eval(&block) if block_given?
+ end
+
+ def where(criterion = nil)
+ @query = @query.where(criterion)
+ end
+
+ def selector
+ @query.selector
+ end
+
+ private
+
+ def apply_default_scope
+ where(site_id: @scope.site.id) if @scope.site
+ end
+
+ end
+
+ end
+ end
+ end
locomotive/steam/entities.rb b/lib/locomotive/steam/entities.rb +0 -49
@@ @@ -1,49 +0,0 @@
- require_relative 'entities/concerns/validation.rb'
-
- module Locomotive
- module Steam
-
- module Entities; end
-
- module Entity
-
- include Steam::Entities::Concerns::Validation
-
- attr_accessor :attributes
-
- def initialize(attributes)
- @attributes = attributes
- end
-
- def method_missing(name, *args, &block)
- if attributes.include?(name)
- self[name]
- else
- super
- end
- end
-
- def []=(name, value)
- attributes[name.to_sym] = value
- end
-
- def [](name)
- attributes[name.to_sym]
- end
-
- def self.set_localized_attributes(list)
- singleton = class << self; self; end
- singleton.class_eval do
- define_method(:localized_attributes) { list }
- end
-
- class_eval do
- define_method(:localized_attributes) { list }
- end
- end
-
- end
- end
- end
-
- require_relative_all 'entities'
locomotive/steam/entities/concerns/validation.rb b/lib/locomotive/steam/entities/concerns/validation.rb +0 -58
@@ @@ -1,58 +0,0 @@
- require 'forwardable'
-
- module Locomotive
- module Steam
- module Entities
- module Concerns
-
- module Validation
-
- def errors
- @errors ||= Errors.new(self)
- end
-
- def valid?
- true
- end
-
- class Errors
-
- include Enumerable
- extend Forwardable
-
- attr_accessor :messages
-
- def_delegators :@messages, :[], :clear, :empty?, :each, :size
-
- alias_method :blank?, :empty?
-
- def initialize(base)
- @base = base
- @messages = HashWithIndifferentAccess.new({})
- end
-
- def add_on_blank(attribute)
- value = @base.send(attribute)
- add(attribute, :blank) if value.blank?
- end
-
- def add(attribute, message)
- (@messages[attribute] ||= []) << generate_message(message)
- end
-
- def generate_message(message)
- case message
- when :blank, :unique then I18n.t(message, scope: 'errors.messages')
- else
- message
- end
- end
-
- end
-
- end
-
- end
- end
- end
- end
locomotive/steam/entities/page.rb b/lib/locomotive/steam/entities/page.rb +44 -0
@@ @@ -0,0 +1,44 @@
+ module Locomotive::Steam
+
+ class Page
+
+ include Locomotive::Steam::Models::Entity
+
+ attr_accessor :depth, :_fullpath, :content_entry
+
+ def initialize(attributes)
+ super({
+ handle: nil,
+ listed: false,
+ published: true,
+ fullpath: {},
+ content_type: nil,
+ position: 99,
+ template: {},
+ editable_elements: {},
+ redirect_url: {}
+ }.merge(attributes))
+ end
+
+ def listed?; !!listed; end
+ def published?; !!published; end
+
+ def templatized?
+ !!content_type
+ end
+
+ def index?
+ attributes[:fullpath].values.first == 'index'
+ end
+
+ def not_found?
+ attributes[:fullpath].values.first == '404'
+ end
+
+ def to_liquid
+ Steam::Liquid::Drops::Page.new(self)
+ end
+
+ end
+
+ end
locomotive/steam/entities/site.rb b/lib/locomotive/steam/entities/site.rb +28 -21
@@ @@ -1,32 +1,39 @@
- module Locomotive
- module Steam
- module Entities
+ module Locomotive::Steam
- class Site
+ class Site
- include Steam::Entity
+ include Locomotive::Steam::Models::Entity
- def initialize(attributes = {})
- super({
- timezone: 'UTC',
- prefix_default_locale: false
- }.merge(attributes))
- end
+ def initialize(attributes = {})
+ super({
+ prefix_default_locale: false
+ }.merge(attributes))
+ end
- def default_locale
- self.locales.try(:first) || :en
- end
+ def handle
+ self[:handle] || self[:subdomain]
+ end
- def locales
- attributes[:locales].map(&:to_sym)
- end
+ def default_locale
+ locales.try(:first) || :en
+ end
- def to_liquid
- Steam::Liquid::Drops::Site.new(self)
- end
+ def locales
+ self[:locales].map(&:to_sym)
+ end
- end
+ def timezone_name
+ self[:timezone] || self[:timezone_name] || 'UTC'
+ end
+ def timezone
+ @timezone ||= ActiveSupport::TimeZone.new(timezone_name)
end
+
+ def to_liquid
+ Steam::Liquid::Drops::Site.new(self)
+ end
+
end
+
end
locomotive/steam/mapper.rb b/lib/locomotive/steam/mapper.rb +0 -30
@@ @@ -1,30 +0,0 @@
- module Locomotive
- module Steam
-
- class Mapper
-
- attr_reader :name, :options, :localized_attributes
-
- def initialize(name, options, &block)
- @name, @options = name, options
- @localized_attributes = []
-
- instance_eval(&block) if block_given?
- end
-
- def set_localized_attributes(*args)
- @localized_attributes += [*args]
- end
-
- def to_entity(attributes)
- entity_klass.new(attributes)
- end
-
- def entity_klass
- options[:entity]
- end
-
- end
-
- end
- end
locomotive/steam/models.rb b/lib/locomotive/steam/models.rb +6 -0
@@ @@ -0,0 +1,6 @@
+ require_relative 'models/concerns/validation'
+ require_relative 'models/i18n_field'
+ require_relative 'models/entity'
+ require_relative 'models/mapper'
+ require_relative 'models/scope'
+ require_relative 'models/repository'
locomotive/steam/models/concerns/validation.rb b/lib/locomotive/steam/models/concerns/validation.rb +58 -0
@@ @@ -0,0 +1,58 @@
+ require 'forwardable'
+
+ module Locomotive
+ module Steam
+ module Models
+ module Concerns
+
+ module Validation
+
+ def errors
+ @errors ||= Errors.new(self)
+ end
+
+ def valid?
+ true
+ end
+
+ class Errors
+
+ include Enumerable
+ extend Forwardable
+
+ attr_accessor :messages
+
+ def_delegators :@messages, :[], :clear, :empty?, :each, :size
+
+ alias_method :blank?, :empty?
+
+ def initialize(base)
+ @base = base
+ @messages = HashWithIndifferentAccess.new({})
+ end
+
+ def add_on_blank(attribute)
+ value = @base.send(attribute)
+ add(attribute, :blank) if value.blank?
+ end
+
+ def add(attribute, message)
+ (@messages[attribute] ||= []) << generate_message(message)
+ end
+
+ def generate_message(message)
+ case message
+ when :blank, :unique then I18n.t(message, scope: 'errors.messages')
+ else
+ message
+ end
+ end
+
+ end
+
+ end
+
+ end
+ end
+ end
+ end
locomotive/steam/models/entity.rb b/lib/locomotive/steam/models/entity.rb +32 -0
@@ @@ -0,0 +1,32 @@
+ module Locomotive::Steam
+ module Models
+
+ module Entity
+
+ include Locomotive::Steam::Models::Concerns::Validation
+
+ attr_accessor :attributes
+
+ def initialize(attributes)
+ @attributes = attributes.with_indifferent_access
+ end
+
+ def method_missing(name, *args, &block)
+ if attributes.include?(name)
+ self[name]
+ else
+ super
+ end
+ end
+
+ def []=(name, value)
+ attributes[name.to_sym] = value
+ end
+
+ def [](name)
+ attributes[name.to_sym]
+ end
+
+ end
+ end
+ end
locomotive/steam/models/i18n_field.rb b/lib/locomotive/steam/models/i18n_field.rb +25 -0
@@ @@ -0,0 +1,25 @@
+ module Locomotive::Steam
+ module Models
+
+ class I18nField
+
+ attr_reader :name, :translations
+
+ def initialize(name, translations)
+ @name = name
+
+ if translations.respond_to?(:fetch)
+ @translations = translations.with_indifferent_access
+ else
+ @translations = Hash.new { translations }
+ end
+ end
+
+ def [](locale)
+ @translations[locale]
+ end
+
+ end
+
+ end
+ end
locomotive/steam/models/mapper.rb b/lib/locomotive/steam/models/mapper.rb +37 -0
@@ @@ -0,0 +1,37 @@
+ module Locomotive::Steam
+ module Models
+
+ class Mapper
+
+ attr_reader :name, :options, :localized_attributes
+
+ def initialize(name, options, &block)
+ @name, @options = name, options
+ @localized_attributes = []
+
+ instance_eval(&block) if block_given?
+ end
+
+ def set_localized_attributes(*args)
+ @localized_attributes += [*args]
+ end
+
+ def to_entity(attributes)
+ entity_klass.new(serialize(attributes))
+ end
+
+ def serialize(attributes)
+ localized_attributes.each do |name|
+ attributes[name] = I18nField.new(name, attributes[name])
+ end
+ attributes
+ end
+
+ def entity_klass
+ options[:entity]
+ end
+
+ end
+
+ end
+ end
locomotive/steam/models/repository.rb b/lib/locomotive/steam/models/repository.rb +70 -0
@@ @@ -0,0 +1,70 @@
+ module Locomotive::Steam
+ module Models
+
+ module Repository
+
+ extend ActiveSupport::Concern
+
+ class RecordNotFound < StandardError; end
+
+ attr_accessor :adapter, :current_site, :current_locale
+
+ def initialize(adapter, current_site = nil, current_locale = nil)
+ @adapter = adapter
+ @current_site = current_site
+ @current_locale = current_locale
+ end
+
+ def find(id)
+ adapter.find(mapper, scope, id)
+ end
+
+ def query(&block)
+ adapter.query(mapper, scope, &block)
+ end
+
+ alias :all :query
+
+ # def create(entity)
+ # entity.id = adapter.create(collection_name, entity)
+ # end
+
+ # def persisted?(entity)
+ # !!entity.id && adapter.persisted?(collection_name, entity)
+ # end
+
+ # def update(entity)
+ # adapter.update(collection_name, entity)
+ # end
+
+ # def destroy(entity)
+ # adapter.destroy(collection_name, entity)
+ # end
+
+ def mapper
+ name, options, block = mapper_options
+ @mapper ||= Mapper.new(name, options, &block)
+ end
+
+ def scope
+ @scope ||= Scope.new(current_site, current_locale)
+ end
+
+ # def collection_name
+ # mapper.name
+ # end
+
+ module ClassMethods
+
+ def mapping(name, options = {}, &block)
+ class_eval do
+ define_method(:mapper_options) { [name, options, block] }
+ end
+ end
+
+ end
+
+ end
+
+ end
+ end
locomotive/steam/models/scope.rb b/lib/locomotive/steam/models/scope.rb +17 -0
@@ @@ -0,0 +1,17 @@
+ module Locomotive::Steam
+ module Models
+
+ class Scope < Struct.new(:site, :locale)
+
+ def default_locale
+ site.try(:default_locale)
+ end
+
+ def locales
+ site.try(:locales)
+ end
+
+ end
+
+ end
+ end
locomotive/steam/repositories.rb b/lib/locomotive/steam/repositories.rb +62 -55
@@ @@ -1,75 +1,82 @@
- module Locomotive
- module Steam
- module Repository
+ raise 'NOT GOOD'
- extend ActiveSupport::Concern
+ # module Locomotive
+ # module Steam
+ # module Repository
- class RecordNotFound < StandardError; end
+ # extend ActiveSupport::Concern
- attr_reader :adapter
+ # class RecordNotFound < StandardError; end
- attr_accessor :current_locale
+ # attr_accessor :adapter, :current_site, :current_locale
- def initialize(adapter)
- @adapter = adapter
- end
+ # def initialize(adapter, current_site = nil, current_locale = nil)
+ # @adapter = adapter
+ # @current_site = current_site
+ # @current_locale = current_locale
+ # end
- def all
- adapter.all(mapper)
- end
+ # def all
+ # adapter.all(mapper)
+ # end
- # def find(id)
- # adapter.find(mapper, id)
- # end
+ # # def find(id)
+ # # adapter.find(mapper, id)
+ # # end
- def query(&block)
- adapter.query(mapper, current_locale, &block)
- end
+ # def query(&block)
+ # adapter.query(mapper, current_locale, &block)
+ # end
- # def create(entity)
- # entity.id = adapter.create(collection_name, entity)
- # end
+ # # def create(entity)
+ # # entity.id = adapter.create(collection_name, entity)
+ # # end
- # def persisted?(entity)
- # !!entity.id && adapter.persisted?(collection_name, entity)
- # end
+ # # def persisted?(entity)
+ # # !!entity.id && adapter.persisted?(collection_name, entity)
+ # # end
- # def update(entity)
- # adapter.update(collection_name, entity)
- # end
+ # # def update(entity)
+ # # adapter.update(collection_name, entity)
+ # # end
- # def destroy(entity)
- # adapter.destroy(collection_name, entity)
- # end
+ # # def destroy(entity)
+ # # adapter.destroy(collection_name, entity)
+ # # end
- def mapper
- name, options, block = mapper_options
- @mapper ||= Steam::Mapper.new(name, options, &block)
- end
+ # def mapper
+ # name, options, block = mapper_options
+ # @mapper ||= Steam::Mapper.new(name, options, &block)
+ # end
- # def collection_name
- # mapper.name
- # end
+ # def scope
+ # Steam::
+ # end
- module ClassMethods
+ # # def collection_name
+ # # mapper.name
+ # # end
- def mapping(name, options = {}, &block)
- class_eval do
- define_method(:mapper_options) { [name, options, block] }
- end
- end
+ # module ClassMethods
- end
+ # def mapping(name, options = {}, &block)
+ # class_eval do
+ # define_method(:mapper_options) { [name, options, block] }
+ # end
+ # end
- end
- end
- end
+ # end
- module Locomotive
- module Steam
- module Repositories
- end
- end
- end
+ # end
+ # end
+ # end
- require_relative 'repositories/site_repository'
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # end
+ # end
+ # end
+
+ # require_relative 'repositories/site_repository'
+ # require_relative 'repositories/page_repository'
locomotive/steam/repositories/filesystem/page.rb b/lib/locomotive/steam/repositories/filesystem/page.rb +1 -0
@@ @@ -47,6 +47,7 @@ module Locomotive
def parent_of(page)
return nil if page.nil? || page.index?
+ # TODO: parent_id property
segments = localized_attribute(page, :fullpath).split('/')
path = segments[0..-2].join('/')
path = 'index' if path.blank?
locomotive/steam/repositories/page_repository.rb b/lib/locomotive/steam/repositories/page_repository.rb +104 -0
@@ @@ -0,0 +1,104 @@
+ module Locomotive
+ module Steam
+
+ class PageRepository < Struct.new(:adapter, :site, :current_locale)
+
+ include Models::Repository
+
+ mapping :pages, entity: Page do
+ set_localized_attributes :title, :slug, :permalink, :editable_elements, :template, :template_path, :redirect_url, :fullpath, :seo_title, :meta_description, :meta_keywords
+ end
+
+ # Engine: site.pages.ordered_pages(conditions)
+ def all(conditions = {})
+ default_order = 'depth_and_position asc'
+ query { where(conditions || {}).order_by(default_order) }.all
+ end
+
+ # Engine: site.pages.where(handle: handle).first
+ def by_handle(handle)
+ query { where(handle: handle) }.first
+ end
+
+ def by_fullpath(path)
+ query { where(fullpath: path) }.first
+ end
+
+ def matching_fullpath(list)
+ all('fullpath.in' => list)
+ end
+
+ # Engine: ???
+ def template_for(entry, handle = nil)
+ conditions = { templatized?: true, content_type: entry.try(:content_type_slug) }
+
+ conditions[:handle] = handle if handle
+
+ query { where(conditions) }.first.tap do |page|
+ page.content_entry = entry if page
+ end
+ end
+
+ def root
+ query { where(fullpath: 'index') }.first
+ end
+
+ # Engine: page.parent
+ def parent_of(page)
+ return nil if page.nil? || page.index?
+
+ # TODO: parent_id property
+ segments = localized_attribute(page, :fullpath).split('/')
+ path = segments[0..-2].join('/')
+ path = 'index' if path.blank?
+
+ by_fullpath(path)
+ end
+
+ # Engine: page.ancestors_and_self
+ def ancestors_of(page)
+ return [] if page.nil?
+
+ # Example: foo/bar/test
+ # ['foo', 'foo/bar', 'foo/bar/test']
+ segments = localized_attribute(page, :fullpath).split('/')
+ paths = 0.upto(segments.size - 1).map { |i| segments[0..i].join('/') }
+
+ all('fullpath.in' => ['index'] + paths)
+ end
+
+ # Engine: page.children
+ def children_of(page)
+ return [] if page.nil?
+
+ conditions = { 'slug.ne' => nil, depth: page.depth + 1 }
+
+ unless page.index?
+ conditions[:fullpath] = /^#{localized_attribute(page, :fullpath)}\//
+ end
+
+ all(conditions)
+ end
+
+ # Engine: page.editable_elements
+ def editable_elements_of(page)
+ return nil if page.nil?
+ localized_attribute(page, :editable_elements).values
+ end
+
+ # Engine: page.editable_elements.where(block: block, slug: slug).first
+ def editable_element_for(page, block, slug)
+ return nil if page.nil?
+
+ if elements = localized_attribute(page, :editable_elements)
+ name = [block, slug].compact.join('/')
+ elements[name]
+ else
+ nil
+ end
+ end
+
+ end
+
+ end
+ end
locomotive/steam/repositories/site_repository.rb b/lib/locomotive/steam/repositories/site_repository.rb +4 -4
@@ @@ -3,17 +3,17 @@ module Locomotive
class SiteRepository
- include Steam::Repository
+ include Models::Repository
- mapping :sites, entity: Steam::Entities::Site do
+ mapping :sites, entity: Site do
set_localized_attributes :seo_title, :meta_description, :meta_keywords
end
def by_handle_or_domain(handle, domain)
if handle.nil?
- query { where(handle: handle) }.first
+ query { where('domains.in' => domain) }.first
else
- query { where('domains.in' => [*domain]) }.first
+ query { where(handle: handle) }.first
end
end
locomotivecms_steam.gemspec +3 -0
@@ @@ -19,6 +19,9 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'bundler', '~> 1.7'
spec.add_development_dependency 'rake', '~> 10.4.2'
+ spec.add_development_dependency 'moped', '~> 2.0.4'
+ spec.add_development_dependency 'origin', '~> 1.0.4'
+
spec.add_dependency 'activesupport', '~> 4.2.0'
spec.add_dependency 'stringex', '~> 2.5.2'
spec.add_dependency 'sanitize', '~> 3.1.0'
spec/integration/repositories/site_repository_spec.rb +42 -0
@@ @@ -0,0 +1,42 @@
+ require 'spec_helper'
+
+ require_relative '../../../lib/locomotive/steam/adapters/filesystem.rb'
+ require_relative '../../../lib/locomotive/steam/adapters/mongodb.rb'
+
+ describe Locomotive::Steam::SiteRepository do
+
+ let(:repository) { Locomotive::Steam::SiteRepository.new(adapter) }
+
+ context 'MongoDB' do
+
+ let(:adapter) { Locomotive::Steam::MongoDBAdapter.new('steam_test', ['127.0.0.1:27017']) }
+
+ describe '#all' do
+ subject { repository.all }
+ it { expect(subject.size).to eq 1 }
+ end
+
+ describe '#query' do
+ subject { repository.query { where(handle: 'acme') }.first }
+ it { expect(subject.name).to eq 'My portfolio' }
+ end
+
+ end
+
+ context 'Filesystem' do
+
+ let(:adapter) { Locomotive::Steam::FilesystemAdapter.new(default_fixture_site_path) }
+
+ describe '#all' do
+ subject { repository.all }
+ it { expect(subject.size).to eq 1 }
+ end
+
+ describe '#query' do
+ subject { repository.query { where(subdomain: 'sample') }.first }
+ it { expect(subject.name).to eq 'Sample website' }
+ end
+
+ end
+
+ end
spec/spec_helper.rb +5 -4
@@ @@ -14,9 +14,9 @@ SimpleCov.start do
add_filter 'spec/'
add_group "Middlewares", "lib/locomotive/steam/middlewares"
- add_group "Liquid Filters", "lib/locomotive/steam/liquid/filters"
- add_group "Liquid Tags", "lib/locomotive/steam/liquid/tags"
- add_group "Liquid Drops", "lib/locomotive/steam/liquid/drops"
+ add_group "Liquid", "lib/locomotive/steam/liquid"
+ add_group "Adapters", "lib/locomotive/steam/adapters"
+ add_group "Entities", "lib/locomotive/steam/entities"
add_group "Repositories", "lib/locomotive/steam/repositories"
add_group "Services", "lib/locomotive/steam/services"
end
@@ @@ -27,7 +27,8 @@ require 'bundler/setup'
require 'i18n-spec'
require_relative '../lib/locomotive/steam'
- require_relative '../lib/locomotive/steam/repositories/filesystem'
+ # TODO
+ # require_relative '../lib/locomotive/steam/repositories/filesystem'
require_relative 'support'
Locomotive::Steam.configure do |config|
spec/unit/adapters/filesystem_spec.rb +45 -0
@@ @@ -0,0 +1,45 @@
+ require 'spec_helper'
+
+ require_relative '../../../lib/locomotive/steam/adapters/filesystem.rb'
+
+ describe Locomotive::Steam::FilesystemAdapter do
+
+ let(:mapper) { instance_double('Mapper', name: :test) }
+ let(:scope) { instance_double('Scope', site: site, locale: nil) }
+ let(:adapter) { Locomotive::Steam::FilesystemAdapter.new(nil) }
+
+ describe '#query' do
+
+ let(:collection) { [OpenStruct.new(site_id: 42, name: 'Hello world')] }
+
+ before do
+ allow(mapper).to receive(:to_entity) { |arg| arg }
+ allow(adapter).to receive(:collection).and_return(collection)
+ end
+
+ subject { adapter.query(mapper, scope) { where(name: 'Hello world') } }
+
+ context 'not scoped by a site' do
+
+ let(:site) { nil }
+ it { expect(subject.first.name).to eq 'Hello world' }
+
+ end
+
+ context 'scoped by a site' do
+
+ let(:site) { instance_double('Site', id: 42) }
+ it { expect(subject.first.name).to eq 'Hello world' }
+
+ context 'unknown site id' do
+
+ let(:site) { instance_double('Site', id: 1) }
+ it { expect(subject.first).to eq nil }
+
+ end
+
+ end
+
+ end
+
+ end
spec/unit/entities/page_spec.rb +43 -0
@@ @@ -0,0 +1,43 @@
+ require 'spec_helper'
+
+ describe Locomotive::Steam::Page do
+
+ let(:attributes) { {} }
+ let(:page) { Locomotive::Steam::Page.new(attributes) }
+
+ describe '#index?' do
+
+ let(:attributes) { { fullpath: { en: 'foo/index' } } }
+
+ subject { page.index? }
+ it { is_expected.to eq false }
+
+ context 'true' do
+ let(:attributes) { { fullpath: { en: 'index' } } }
+ it { is_expected.to eq true }
+ end
+
+ end
+
+ describe '#not_found?' do
+
+ let(:attributes) { { fullpath: { en: 'index' } } }
+
+ subject { page.not_found? }
+ it { is_expected.to eq false }
+
+ context 'true' do
+ let(:attributes) { { fullpath: { en: '404' } } }
+ it { is_expected.to eq true }
+ end
+
+ end
+
+ describe '#valid?' do
+
+ subject { page.valid? }
+ it { is_expected.to eq true }
+
+ end
+
+ end
spec/unit/entities/site_spec.rb +63 -0
@@ @@ -0,0 +1,63 @@
+ require 'spec_helper'
+
+ describe Locomotive::Steam::Site do
+
+ let(:attributes) { {} }
+ let(:site) { Locomotive::Steam::Site.new(attributes) }
+
+ describe '#handle' do
+
+ let(:attributes) { { handle: 'acme' } }
+
+ subject { site.handle }
+ it { is_expected.to eq 'acme' }
+
+ context 'from a subdomain if the handle property is nil' do
+
+ let(:attributes) { { subdomain: 'acme' } }
+ it { is_expected.to eq 'acme' }
+
+ end
+
+ end
+
+ describe '#locales' do
+
+ let(:attributes) { { locales: %w(en fr) } }
+
+ subject { site.locales }
+ it { is_expected.to eq [:en, :fr] }
+
+ end
+
+ describe '#default_locale' do
+
+ let(:attributes) { { locales: %w(en fr) } }
+
+ subject { site.default_locale }
+ it { is_expected.to eq :en }
+
+ end
+
+ describe '#timezone_name' do
+
+ subject { site.timezone_name }
+ it { is_expected.to eq 'UTC' }
+
+ context 'not blank' do
+
+ let(:attributes) { { timezone: 'CDT' } }
+ it { is_expected.to eq 'CDT' }
+
+ end
+
+ context 'from the timezone_name attribute itself' do
+
+ let(:attributes) { { timezone_name: 'CDT' } }
+ it { is_expected.to eq 'CDT' }
+
+ end
+
+ end
+
+ end
spec/unit/models/mapper_spec.rb +41 -0
@@ @@ -0,0 +1,41 @@
+ require 'spec_helper'
+
+ describe Locomotive::Steam::Models::Mapper do
+
+ let(:name) { 'pages' }
+ let(:options) { { entity: MyPage } }
+ let(:block) { nil }
+ let(:mapper) { Locomotive::Steam::Models::Mapper.new(name, options, &block) }
+
+ describe '#localized attributes' do
+
+ let(:block) { ->(_) { set_localized_attributes(:foo, :bar) } }
+
+ subject { mapper.localized_attributes }
+ it { is_expected.to eq [:foo, :bar] }
+
+ end
+
+ describe '#to_entity' do
+
+ let(:block) { ->(_) { set_localized_attributes(:title) } }
+ let(:attributes) { { title: { 'en' => 'Hello world' } } }
+
+ subject { mapper.to_entity(attributes) }
+ it { expect(subject.attributes[:title].class).to eq Locomotive::Steam::Models::I18nField }
+ it { expect(subject.attributes[:title][:en]).to eq('Hello world') }
+
+ context 'string value for the localized field' do
+
+ let(:attributes) { { title: 'Hello world' } }
+
+ it { expect(subject.attributes[:title][:en]).to eq('Hello world') }
+ it { expect(subject.attributes[:title][:fr]).to eq('Hello world') }
+
+ end
+
+ end
+
+ class MyPage < Struct.new(:attributes); end
+
+ end
spec/unit/repositories/page_repository_spec.rb +307 -0
@@ @@ -0,0 +1,307 @@
+ require 'spec_helper'
+
+ require_relative '../../../lib/locomotive/steam/adapters/filesystem.rb'
+
+ describe Locomotive::Steam::PageRepository do
+
+ let(:locale) { :en }
+ let(:site) { instance_double('Site', _id: 1, default_locale: :en, locales: %i(en fr)) }
+ let(:adapter) { Locomotive::Steam::FilesystemAdapter.new(nil) }
+ let(:repository) { Locomotive::Steam::PageRepository.new(adapter, site, locale) }
+
+ # describe '#collection' do
+
+ # subject { repository.send(:collection).first }
+
+ # it { expect(subject.class).to eq Locomotive::Steam::Repositories::Filesystem::Models::Page }
+
+ # it 'applies the sanitizer' do
+ # expect(subject[:fullpath]).to eq({ en: 'index' })
+ # expect(subject.depth).to eq 0
+ # end
+
+ # end
+
+ describe '#all' do
+
+ let(:pages) do
+ [
+ { title: { en: 'Contact' }, slug: { en: 'contact' }, _fullpath: 'contact', template_path: { en: 'contact.liquid' } },
+ { title: { en: 'About us' }, position: 2, slug: { en: 'about-us' }, _fullpath: 'about-us', template_path: { en: 'about-us.liquid' } },
+ { title: { en: 'Jane Doe' }, slug: { en: 'jane-doe' }, _fullpath: 'team/jane-doe', template_path: { en: 'team/jane-doe.liquid' } },
+ { title: { en: 'John Doe' }, position: 1, slug: { en: 'john-doe' }, _fullpath: 'team/john-doe', template_path: { en: 'team/john-doe.liquid' } },
+ { title: { en: 'Home' }, slug: { en: 'index' }, _fullpath: 'index', template_path: { en: 'index.liquid' } }
+ ]
+ end
+
+ let(:conditions) { nil }
+
+ subject { repository.all(conditions) }
+
+ it { expect(subject.size).to eq 5 }
+
+ describe 'default order' do
+
+ subject { repository.all(conditions).map { |p| p.title.values.first } }
+
+ it { is_expected.to eq ['Home', 'About us', 'Contact', 'John Doe', 'Jane Doe'] }
+
+ end
+
+ describe 'filter' do
+
+ let(:conditions) { { slug: /-doe$/ } }
+ it { expect(subject.size).to eq 2 }
+
+ end
+
+ end
+
+ describe '#by_fullpath' do
+
+ let(:path) { nil }
+ subject { repository.by_fullpath(path) }
+
+ it { is_expected.to eq nil }
+
+ context 'existing page' do
+
+ let(:path) { 'index' }
+ it { expect(subject.title).to eq({ en: 'Home' }) }
+
+ end
+
+ end
+
+ describe '#by_handle' do
+
+ let(:handle) { nil }
+ subject { repository.by_handle(handle) }
+
+ it { is_expected.to eq nil }
+
+ context 'existing page' do
+
+ let(:handle) { 'home' }
+ it { expect(subject.title).to eq({ en: 'Home' }) }
+
+ end
+
+ end
+
+ describe '#matching_fullpath' do
+
+ let(:paths) { nil }
+ subject { repository.matching_fullpath(paths) }
+
+ it { is_expected.to eq [] }
+
+ context 'existing page' do
+
+ let(:paths) { ['index', '404'] }
+ it { expect(subject.first.title).to eq({ en: 'Home' }) }
+
+ end
+
+ context 'templatized page' do
+
+ let(:paths) { ['articles/content-type-template', 'content-type-template/hello-world', 'articles/hello-world'] }
+
+ let(:pages) do
+ [{ title: { en: 'Templatized article' }, slug: { en: 'template' }, content_type: 'articles', _fullpath: 'articles/template', template_path: { en: 'articles/template.liquid' } }]
+ end
+
+ it { expect(subject.first.title).to eq({ en: 'Templatized article' }) }
+
+ end
+
+ end
+
+ describe '#template_for' do
+
+ let(:pages) do
+ [
+ { title: { en: 'Article template' }, content_type: 'articles', slug: { en: 'articles/content_type_template' }, _fullpath: 'articles/template', template_path: { en: 'articles/template.liquid' } },
+ { title: { en: 'Archived article template' }, handle: 'archive', content_type: 'articles', slug: { en: 'archived/articles/content_type_template' }, _fullpath: 'archived/articles/template', template_path: { en: 'archived/articles/template.liquid' } },
+ { title: { en: 'Home' }, handle: 'home', slug: { en: 'index' }, _fullpath: 'index', template_path: { en: 'index.liquid' } }
+ ]
+ end
+ let(:entry) { nil }
+ let(:handle) { nil }
+
+ subject { repository.template_for(entry, handle) }
+
+ it { is_expected.to eq nil }
+
+ context 'both existing entry and page' do
+
+ let(:entry) { instance_double('Article', content_type_slug: 'articles', _slug: { en: 'hello-world' }) }
+ it { expect(subject.title).to eq({ en: 'Article template' }) }
+ it { expect(subject.content_entry).to eq entry }
+
+ context 'with a handle' do
+
+ let(:handle) { 'archive' }
+ it { expect(subject.title).to eq({ en: 'Archived article template' }) }
+ it { expect(subject.content_entry).to eq entry }
+
+ end
+
+ end
+
+ context 'unknown content type' do
+
+ let(:entry) { instance_double('Project', content_type_slug: 'projects', _slug: { en: 'hello-world' }) }
+ it { is_expected.to eq nil }
+
+ end
+
+ end
+
+ describe '#root' do
+
+ subject { repository.root }
+ it { expect(subject.title).to eq({ en: 'Home' }) }
+
+ end
+
+ describe '#parent_of' do
+
+ let(:page) { nil }
+ subject { repository.parent_of(page) }
+
+ it { is_expected.to eq nil }
+
+ context 'index' do
+
+ let(:page) { instance_double('Page', index?: true) }
+ it { is_expected.to eq nil }
+
+ end
+
+ context 'page not nil' do
+
+ let(:page) { instance_double('Page', index?: false, fullpath: { en: 'about-us' }) }
+ it { expect(subject.title).to eq({ en: 'Home' }) }
+
+ end
+
+ context 'nested pages' do
+
+ let(:pages) do
+ [
+ { title: { en: 'Somewhere' }, slug: { en: 'somewhere' }, _fullpath: 'somewhere', template_path: { en: 'somewhere.liquid' } },
+ { title: { en: 'Home' }, slug: { en: 'index' }, _fullpath: 'index', template_path: { en: 'index.liquid' } }
+ ]
+ end
+ let(:page) { instance_double('Page', index?: false, fullpath: { en: 'somewhere/hello-world' }) }
+
+ it { expect(subject.title).to eq({ en: 'Somewhere' }) }
+
+ end
+
+ end
+
+ describe '#ancestors_of' do
+
+ let(:page) { nil }
+ subject { repository.ancestors_of(page) }
+
+ it { is_expected.to eq [] }
+
+ context 'index' do
+
+ let(:page) { instance_double('Page', fullpath: 'index') }
+ it { expect(subject.map(&:title)).to eq([{ en: 'Home' }]) }
+
+ end
+
+ context 'nested pages' do
+
+ let(:pages) do
+ [
+ { title: { en: 'Foo' }, slug: { en: 'foo' }, _fullpath: 'bar/foo', template_path: { en: 'bar/foo.liquid' } },
+ { title: { en: 'Bar' }, slug: { en: 'bar' }, _fullpath: 'bar', template_path: { en: 'bar.liquid' } },
+ { title: { en: 'Home' }, slug: { en: 'index' }, _fullpath: 'index', template_path: { en: 'index.liquid' } }
+ ]
+ end
+ let(:page) { instance_double('Page', title: { en: 'Foo' }, index?: false, fullpath: { en: 'bar/foo' }) }
+
+ it { expect(subject.map { |p| p.title.values.first }).to eq ['Home', 'Bar', 'Foo'] }
+
+ end
+
+ end
+
+ describe '#children_of' do
+
+ let(:page) { nil }
+ subject { repository.children_of(page) }
+
+ it { is_expected.to eq [] }
+
+ context 'with pages' do
+
+ let(:pages) do
+ [
+ { title: { en: 'Foo' }, slug: { en: 'foo' }, _fullpath: 'bar/foo', template_path: { en: 'bar/foo.liquid' } },
+ { title: { en: 'Bar' }, slug: { en: 'bar' }, _fullpath: 'bar', template_path: { en: 'bar.liquid' } },
+ { title: { en: 'Home' }, slug: { en: 'index' }, _fullpath: 'index', template_path: { en: 'index.liquid' } }
+ ]
+ end
+ let(:page) { instance_double('Page', title: { en: 'Home' }, depth: 0, index?: true, fullpath: { en: 'index' }) }
+
+ it { expect(subject.map { |p| p.title.values.first }).to eq ['Bar'] }
+
+ context 'from a nested page' do
+
+ let(:page) { instance_double('Page', title: { en: 'Bar' }, index?: false, depth: 1, fullpath: { en: 'bar' }) }
+ it { expect(subject.map { |p| p.title.values.first }).to eq ['Foo'] }
+
+ end
+
+ end
+
+ describe '#editable_elements_of' do
+
+ let(:page) { nil }
+ subject { repository.editable_elements_of(page) }
+
+ it { is_expected.to eq nil }
+
+ context 'page with editable elements' do
+
+ let(:elements) { { 'title' => instance_double('Element', content: 'Stuff here') } }
+ let(:page) { instance_double('Page', editable_elements: { en: elements }) }
+
+ it { expect(subject.map(&:content)).to eq ['Stuff here'] }
+
+ end
+
+ end
+
+ describe '#editable_element_for' do
+
+ let(:page) { nil }
+ let(:block) { nil }
+ let(:slug) { nil }
+ subject { repository.editable_element_for(page, block, slug) }
+
+ it { is_expected.to eq nil }
+
+ context 'page with editable elements' do
+
+ let(:elements) { { 'title' => instance_double('Element', content: 'Stuff here') } }
+ let(:page) { instance_double('Page', editable_elements: { en: elements }) }
+ let(:block) { nil }
+ let(:slug) { 'title' }
+
+ it { expect(subject.content).to eq 'Stuff here' }
+
+ end
+
+ end
+
+ end
+
+ end
spec/unit/repositories/site_repository_spec.rb +19 -9
@@ @@ -4,24 +4,34 @@ require_relative '../../../lib/locomotive/steam/adapters/filesystem.rb'
describe Locomotive::Steam::SiteRepository do
- let(:adapter) { Locomotive::Steam::FilesystemAdapter.new(default_fixture_site_path) }
+ let(:adapter) { Locomotive::Steam::FilesystemAdapter.new(nil) }
let(:repository) { Locomotive::Steam::SiteRepository.new(adapter) }
- describe '#all' do
+ before { allow(adapter).to receive(:collection).and_return([{ name: 'Acme', handle: 'acme', domains: ['example.org'] }]) }
- subject { repository.all }
+ describe '#by_handle_or_domain' do
- it { expect(subject.size).to eq 1 }
+ let(:handle) { nil }
+ let(:domains) { nil }
- end
+ subject { repository.by_handle_or_domain(handle, domains) }
+
+ it { expect(subject).to eq nil }
+
+ context 'handle' do
- describe '#query' do
+ let(:handle) { 'acme' }
+ it { expect(subject.class).to eq Locomotive::Steam::Site }
+ it { expect(subject.name).to eq 'Acme' }
- subject do
- repository.query { where(subdomain: 'sample') }.first
end
- it { expect(subject.name).to eq 'Sample website' }
+ context 'domain' do
+
+ let(:domains) { 'example.org' }
+ it { expect(subject.name).to eq 'Acme' }
+
+ end
end