starting the implementation of a true adapter/repository design pattern

did committed Feb 21, 2015
commit 3d8c62d7bb8419bc49cabf172926c438fbb8d106
Showing 59 changed files with 1820 additions and 1 deletions
Rakefile +4 -0
@@ @@ -24,4 +24,8 @@ 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 +2 -0
@@ @@ -11,6 +11,8 @@ 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/services'
locomotive/steam/adapters/filesystem.rb b/lib/locomotive/steam/adapters/filesystem.rb +66 -0
@@ @@ -0,0 +1,66 @@
+ # require_relative_all 'yaml_loaders'
+ require_relative 'filesystem/dataset'
+ require_relative 'filesystem/condition'
+ require_relative 'filesystem/query'
+
+ require_relative 'filesystem/simple_cache_store'
+
+ require_relative 'filesystem/yaml_loader'
+ require_relative 'filesystem/yaml_loaders/site'
+
+ module Locomotive::Steam
+
+ class FilesystemAdapter
+
+ include Morphine
+
+ register :cache do
+ Locomotive::Steam::Adapters::Filesystem::SimpleCacheStore.new
+ end
+
+ def initialize(site_path)
+ @site_path = site_path
+ @datasets = {}
+ end
+
+ def all(mapper)
+ memoized_dataset(mapper)
+ end
+
+ def query(mapper, locale, &block)
+ Locomotive::Steam::Adapters::Filesystem::Query.new(all(mapper), locale, &block)
+ end
+
+ private
+
+ def memoized_dataset(mapper)
+ return @datasets[mapper.name] if @datasets[mapper.name]
+ dataset(mapper)
+ end
+
+ def dataset(mapper)
+ Locomotive::Steam::Adapters::Filesystem::Dataset.new(mapper.name).tap do |dataset|
+ @datasets[mapper.name] = dataset
+
+ collection(mapper).each do |attributes|
+ entity = mapper.to_entity(attributes)
+ dataset.insert(entity)
+ end
+ end
+ end
+
+ def collection(mapper)
+ yaml_loader(mapper.name).load
+ end
+
+ def yaml_loader(name)
+ _name = name.to_s.singularize.camelize
+ klass = "Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::#{_name}".constantize
+ klass.new(@site_path, cache)
+ end
+
+ end
+
+ end
+
+
locomotive/steam/adapters/filesystem/concerns/queryable.rb b/lib/locomotive/steam/adapters/filesystem/concerns/queryable.rb +65 -0
@@ @@ -0,0 +1,65 @@
+ # module Locomotive
+ # module Steam
+ # module Adapters
+ # module Filesystem
+ # module Concerns
+
+ # module Queryable
+
+ # extend ActiveSupport::Concern
+
+ # def query(*args, &block)
+ # _locale = respond_to?(:current_locale) ? current_locale : nil
+ # Filesystem::Query.new(memoized_collection(*args), _locale, &block)
+ # end
+
+ # private
+
+ # def localized_attribute(object, method)
+ # if (values = object.send(method)).is_a?(Hash)
+ # values[current_locale]
+ # else
+ # values
+ # end
+ # end
+
+ # def memoized_collection(*args)
+ # return @collection if @collection
+
+ # @collection = collection(*args)
+ # end
+
+ # def collection(*args)
+ # _collection = loader.list_of_attributes(*args).map do |attributes|
+ # collection_options[:model].new(attributes)
+ # end
+
+ # sanitize!(_collection)
+ # end
+
+ # def sanitize!(collection)
+ # sanitizer.try(:apply_to, collection) || collection
+ # end
+
+ # def sanitizer
+ # return unless (klass = collection_options[:sanitizer])
+ # klass.new(site.default_locale, site.locales)
+ # end
+
+ # module ClassMethods
+
+ # def set_collection(options = {})
+ # class_eval do
+ # define_method(:collection_options) { options }
+ # end
+ # end
+
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/adapters/filesystem/condition.rb b/lib/locomotive/steam/adapters/filesystem/condition.rb +100 -0
@@ @@ -0,0 +1,100 @@
+ module Locomotive
+ module Steam
+ module Adapters
+ module Filesystem
+
+ class Condition
+
+ class UnsupportedOperator < StandardError; end
+
+ OPERATORS = %i(== eq ne neq matches gt gte lt lte size all in nin).freeze
+
+ 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
+
+ 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
+ end
+
+ def to_s
+ "#{field} #{operator} #{@value.to_s}"
+ end
+
+ protected
+
+ def entry_value(entry)
+ case (value = entry.send(@field))
+ when Hash
+ value.fetch(@locale) { nil }
+ else
+ value
+ end
+ end
+
+ 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
+
+ @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
+ end
+
+ private
+
+ def check_operator!
+ raise UnsupportedOperator.new unless OPERATORS.include?(@operator)
+ end
+
+ def array_contains?(source, target)
+ source & target == target
+ end
+
+ end
+
+ end
+ end
+ end
+ end
locomotive/steam/adapters/filesystem/dataset.rb b/lib/locomotive/steam/adapters/filesystem/dataset.rb +77 -0
@@ @@ -0,0 +1,77 @@
+ 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
+
+ attr_reader :records, :name
+
+ def initialize(name)
+ @name = name
+ clear!
+ end
+
+ def insert(record)
+ @primary_key.increment! do |id|
+ record[identity] = id
+ records[id] = record
+ end
+ end
+
+ def update(record)
+ records[record[identity]] = records[record[identity]].deep_merge(record)
+ end
+
+ def delete(id)
+ records.delete(id)
+ end
+
+ def size
+ records.size
+ end
+
+ def all
+ records.values
+ end
+
+ 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
+
+ # def query
+ # Query.new(self)
+ # end
+
+ def clear!
+ @records = {}
+ @primary_key = PrimaryKey.new
+ end
+
+ private
+
+ def identity
+ @identity ||= :_id
+ end
+ end
+ end
+ end
+ end
+ end
locomotive/steam/adapters/filesystem/query.rb b/lib/locomotive/steam/adapters/filesystem/query.rb +111 -0
@@ @@ -0,0 +1,111 @@
+ require 'forwardable'
+
+ module Locomotive
+ module Steam
+ module Adapters
+ module Filesystem
+
+ class Query
+
+ include Enumerable
+ extend Forwardable
+
+ def_delegators :all, :each, :to_s, :to_a, :empty?, :size
+
+ alias :length :size
+ alias :count :size
+
+ 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 where(conditions = {})
+ @conditions += conditions.map { |name, value| Condition.new(name, value, @locale) }
+ 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
+ end
+
+ def limit(num)
+ @limit = num
+ self
+ end
+
+ def offset(num)
+ @offset = num
+ self
+ end
+
+ def ==(other)
+ if other.kind_of? Array
+ all == other
+ else
+ super
+ end
+ end
+
+ def all
+ limited sorted(filtered)
+ end
+
+ 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
+ end
+
+ 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
+ end
+
+ 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
+ end
+ accepted
+ end
+ end # filtered
+
+ end
+ end
+ end
+ end
+ end
locomotive/steam/adapters/filesystem/sanitizers/content_entry.rb b/lib/locomotive/steam/adapters/filesystem/sanitizers/content_entry.rb +66 -0
@@ @@ -0,0 +1,66 @@
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # module Filesystem
+ # module Sanitizers
+
+ # class ContentEntry < Struct.new(:default_locale, :locales)
+
+ # def apply_to(collection)
+ # collection.each do |entry|
+ # set_content_type(entry)
+ # add_label(entry)
+ # set_slug(entry, collection)
+ # end
+ # end
+
+ # def set_slug(entry, collection)
+ # if entry._label.is_a?(Hash)
+ # entry[:_slug] ||= {}
+ # entry._label.each do |locale, label|
+ # entry[:_slug][locale] ||= slugify(label, collection, locale)
+ # end
+ # else
+ # entry[:_slug] ||= slugify(entry._label, collection)
+ # end
+ # end
+
+ # def slugify(label, collection, locale = nil)
+ # base, index = label.permalink(false), nil
+ # _slugify = -> (i) { [base, i].compact.join('-') }
+
+ # while !is_slug_unique?(_slugify.call(index), collection, locale)
+ # index = index ? index + 1 : 1
+ # end
+
+ # _slugify.call(index)
+ # end
+
+ # def is_slug_unique?(slug, collection, locale)
+ # Filesystem::MemoryAdapter::Query.new(collection, locale) do
+ # where(_slug: slug)
+ # end.first.nil?
+ # end
+
+ # def set_content_type(entry)
+ # entry.content_type = entry.attributes.delete(:content_type)
+ # end
+
+ # def add_label(entry)
+ # value = entry.attributes.delete(:_label)
+ # name = entry.content_type.label_field_name
+
+ # if entry.attributes[name].is_a?(Hash) # localized?
+ # entry.attributes[name][default_locale] = value
+ # else
+ # entry.attributes[name] ||= value
+ # end
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/adapters/filesystem/sanitizers/content_type.rb b/lib/locomotive/steam/adapters/filesystem/sanitizers/content_type.rb +49 -0
@@ @@ -0,0 +1,49 @@
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # module Filesystem
+ # module Sanitizers
+
+ # class ContentType < Struct.new(:default_locale, :locales)
+
+ # def apply_to(collection)
+ # collection.each do |content_type|
+ # if list = content_type.attributes[:fields]
+ # content_type[:slug] = content_type[:slug].to_s
+ # content_type.fields = build_fields(list)
+ # end
+ # build_fields_by_name_shortcut(content_type)
+ # end
+ # end
+
+ # def build_fields(list)
+ # list.map do |attributes|
+ # name, _attributes = attributes.keys.first, attributes.values.first
+
+ # _attributes[:name] = name.to_sym
+
+ # if _attributes[:label].blank?
+ # _attributes[:label] = name.to_s.humanize
+ # end
+
+ # _attributes[:type] = _attributes[:type].try(:to_sym)
+
+ # Filesystem::Models::ContentTypeField.new(_attributes)
+ # end
+ # end
+
+ # def build_fields_by_name_shortcut(content_type)
+ # content_type.fields_by_name = {}
+
+ # (content_type.fields || []).each do |field|
+ # content_type.fields_by_name[field.name] = field
+ # end
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/adapters/filesystem/sanitizers/page.rb b/lib/locomotive/steam/adapters/filesystem/sanitizers/page.rb +145 -0
@@ @@ -0,0 +1,145 @@
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # module Filesystem
+ # module Sanitizers
+
+ # class Page < Struct.new(:default_locale, :locales)
+
+ # def initialize(default_locale, locales)
+ # super
+ # @content_types = {}
+ # @localized = {}
+ # locales.each { |locale| @localized[locale] = {} }
+ # end
+
+ # def apply_to(collection)
+ # sorted_collection(collection).each do |page|
+ # locales.each do |locale|
+ # set_fullpath_for(page, locale)
+ # modify_if_templatized(page, locale)
+ # build_editable_elements(page, locale)
+ # use_default_locale_template_path(page, locale)
+ # set_default_redirect_type(page, locale)
+ # end
+ # end
+ # end
+
+ # # If the page does not have a template in a locale
+ # # then use the template of the default locale
+ # #
+ # def use_default_locale_template_path(page, locale)
+ # paths = page.template_path
+
+ # if paths[locale] == false
+ # paths[locale] = paths[default_locale]
+ # end
+ # end
+
+ # def set_default_redirect_type(page, locale)
+ # if page.redirect_url[locale]
+ # page.attributes[:redirect_type] ||= 301
+ # end
+ # end
+
+ # def build_editable_elements(page, locale)
+ # elements = page.editable_elements[locale] || {}
+ # elements.stringify_keys!
+
+ # elements.each do |name, content|
+ # segments = name.split('/')
+ # block, slug = segments[0..-2].join('/'), segments.last
+ # block = nil if block.blank?
+
+ # elements[name] = Filesystem::Models::EditableElement.new(block, slug, content)
+ # end
+ # end
+
+ # def modify_if_templatized(page, locale)
+ # if page.templatized?
+ # # change the slug of a templatized page
+ # page[:slug][locale] = 'content-type-template'
+
+ # # this also means to change the fullpath
+ # if page[:fullpath][locale]
+ # page[:fullpath][locale].gsub!(/[^\/]+$/, 'content-type-template')
+ # end
+
+ # # make sure its children will have its content type
+ # set_content_type(page._fullpath, page.content_type)
+ # elsif content_type = fetch_content_type(parent_fullpath(page))
+ # # not a templatized page but it becomes one because
+ # # its parent is one of them
+ # page[:content_type] = content_type
+ # end
+ # end
+
+ # def set_fullpath_for(page, locale)
+ # page._fullpath ||= page.attributes.delete(:_fullpath)
+
+ # slug = fullpath = page.slug[locale].try(:dasherize)
+
+ # return if slug.blank?
+
+ # if page.depth > 1
+ # base = parent_fullpath(page)
+ # fullpath = (fetch_localized_fullpath(base, locale) || base) + '/' + slug
+ # end
+
+ # set_localized_fullpath(page._fullpath, fullpath, locale)
+ # page[:fullpath][locale] = fullpath
+ # end
+
+ # def depth(page)
+ # return page.depth if page.depth
+
+ # page.depth = page[:_fullpath].split('/').size
+
+ # slug = get_slug(page)
+
+ # if page.depth == 1 && %w(index 404).include?(slug)
+ # page.depth = 0
+ # end
+
+ # page.depth
+ # end
+
+ # def get_slug(page)
+ # if page.slug.is_a?(Hash)
+ # page.slug.values.compact.first
+ # else
+ # page.slug
+ # end
+ # end
+
+ # def sorted_collection(collection)
+ # collection.sort_by { |page| depth(page) }
+ # end
+
+ # def parent_fullpath(page)
+ # page._fullpath.split('/')[0..-2].join('/')
+ # end
+
+ # def fetch_content_type(fullpath)
+ # @content_types[fullpath]
+ # end
+
+ # def set_content_type(fullpath, value)
+ # @content_types[fullpath] = value
+ # end
+
+ # def fetch_localized_fullpath(fullpath, locale)
+ # @localized[locale][fullpath]
+ # end
+
+ # def set_localized_fullpath(fullpath, value, locale)
+ # @localized[locale][fullpath] = value
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/adapters/filesystem/sanitizers/snippet.rb b/lib/locomotive/steam/adapters/filesystem/sanitizers/snippet.rb +28 -0
@@ @@ -0,0 +1,28 @@
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # module Filesystem
+ # module Sanitizers
+
+ # class Snippet < Struct.new(:default_locale, :locales)
+
+ # def apply_to(collection)
+ # collection.each do |snippet|
+ # # if there a missing template in one of the locales,
+ # # then use the one from the default locale
+ # default = snippet.template_path[default_locale]
+
+ # locales.each do |locale|
+ # next if locale == default_locale
+ # snippet.template_path[locale] ||= default
+ # end
+ # end
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/adapters/filesystem/simple_cache_store.rb b/lib/locomotive/steam/adapters/filesystem/simple_cache_store.rb +40 -0
@@ @@ -0,0 +1,40 @@
+ module Locomotive
+ module Steam
+ module Adapters
+ module Filesystem
+
+ class SimpleCacheStore
+
+ @@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]
+ end
+
+ def write(name, value, options = nil)
+ @@store[name] = value
+ end
+
+ def clear
+ @@store.clear
+ end
+
+ #:nocov:
+ def _store
+ @@store
+ end
+
+ end
+
+ end
+ end
+ end
+ end
locomotive/steam/adapters/filesystem/yaml_loader.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loader.rb +39 -0
@@ @@ -0,0 +1,39 @@
+ module Locomotive
+ module Steam
+ module Adapters
+ module Filesystem
+
+ module YAMLLoader
+
+ 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"
+ {}
+ end
+ end
+
+ def template_extensions
+ @extensions ||= %w(liquid haml)
+ end
+
+ end
+
+ end
+ end
+ end
+ end
locomotive/steam/adapters/filesystem/yaml_loaders/content_entry.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/content_entry.rb +51 -0
@@ @@ -0,0 +1,51 @@
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # module Filesystem
+ # module YAMLLoaders
+
+ # class ContentEntry < Struct.new(:root_path, :cache)
+
+ # include YAMLLoaders::Concerns::Common
+
+ # def list_of_attributes(content_type)
+ # cache.fetch("data/#{content_type.slug}") { load_list(content_type) }
+ # end
+
+ # def write(content_type, attributes)
+ # list = cache.read("data/#{content_type.slug}")
+
+ # list << attributes.merge(content_type: content_type)
+ # end
+
+ # private
+
+ # def load_list(content_type)
+ # [].tap do |list|
+ # each(content_type.slug) do |label, attributes, position|
+ # default = { content_type: content_type, _position: position, _label: label.to_s }
+ # list << default.merge(attributes)
+ # end
+ # end
+ # end
+
+ # def each(slug, &block)
+ # position = 0
+ # load(File.join(path, "#{slug}.yml")).each do |element|
+ # label, attributes = element.keys.first, element.values.first
+ # yield(label, attributes, position)
+ # position += 1
+ # end
+ # end
+
+ # def path
+ # File.join(root_path, 'data')
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/adapters/filesystem/yaml_loaders/content_type.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/content_type.rb +42 -0
@@ @@ -0,0 +1,42 @@
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # module Filesystem
+ # module YAMLLoaders
+
+ # class ContentType < Struct.new(:root_path, :cache)
+
+ # include YAMLLoaders::Concerns::Common
+
+ # def list_of_attributes
+ # cache.fetch('app/content_types') { load_list }
+ # end
+
+ # private
+
+ # def load_list
+ # [].tap do |array|
+ # each_file do |filepath, slug|
+ # array << { slug: slug }.merge(load(filepath))
+ # end
+ # end
+ # end
+
+ # def each_file(&block)
+ # Dir.glob(File.join(path, "*.yml")).each do |filepath|
+ # slug = File.basename(filepath, '.yml')
+ # yield(filepath, slug)
+ # end
+ # end
+
+ # def path
+ # File.join(root_path, 'app', 'content_types')
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/adapters/filesystem/yaml_loaders/page.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/page.rb +108 -0
@@ @@ -0,0 +1,108 @@
+ # 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
locomotive/steam/adapters/filesystem/yaml_loaders/site.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/site.rb +23 -0
@@ @@ -0,0 +1,23 @@
+ module Locomotive
+ module Steam
+ module Adapters
+ module Filesystem
+ module YAMLLoaders
+
+ class Site < Struct.new(:site_path, :cache)
+
+ include Adapters::Filesystem::YAMLLoader
+
+ def load
+ fetch('config/site') do
+ [_load(File.join(site_path, 'config', 'site.yml'))]
+ end
+ end
+
+ end
+
+ end
+ end
+ end
+ end
+ end
locomotive/steam/adapters/filesystem/yaml_loaders/snippet.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/snippet.rb +65 -0
@@ @@ -0,0 +1,65 @@
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # module Filesystem
+ # module YAMLLoaders
+
+ # class Snippet < Struct.new(:root_path, :default_locale, :cache)
+
+ # include YAMLLoaders::Concerns::Common
+
+ # def list_of_attributes
+ # cache.fetch('app/views/snippets') { load_list }
+ # end
+
+ # private
+
+ # def load_list
+ # {}.tap do |hash|
+ # each_file do |filepath, slug, locale|
+ # _locale = locale || default_locale
+
+ # if element = hash[slug]
+ # update(element, filepath, _locale)
+ # else
+ # element = build(filepath, slug, _locale)
+ # end
+
+ # hash[slug] = element
+ # end
+ # end.values
+ # end
+
+ # def build(filepath, slug, locale)
+ # {
+ # name: slug.humanize,
+ # slug: slug,
+ # template_path: { locale => filepath }
+ # }
+ # end
+
+ # def update(element, filepath, locale)
+ # element[:template_path][locale] = filepath
+ # end
+
+ # def each_file(&block)
+ # Dir.glob(File.join(path, "*.{#{template_extensions.join(',')}}")).each do |filepath|
+
+ # slug, locale = File.basename(filepath).split('.')[0..1]
+ # locale = default_locale if template_extensions.include?(locale)
+
+ # yield(filepath, slug.permalink, locale.to_sym)
+ # end
+ # end
+
+ # def path
+ # File.join(root_path, 'app', 'views', 'snippets')
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/adapters/filesystem/yaml_loaders/translation.rb b/lib/locomotive/steam/adapters/filesystem/yaml_loaders/translation.rb +35 -0
@@ @@ -0,0 +1,35 @@
+ # module Locomotive
+ # module Steam
+ # module Repositories
+ # module Filesystem
+ # module YAMLLoaders
+
+ # class Translation < Struct.new(:root_path, :cache)
+
+ # include YAMLLoaders::Concerns::Common
+
+ # def list_of_attributes
+ # cache.fetch('config/translations') { load_array }
+ # end
+
+ # private
+
+ # def load_array
+ # [].tap do |array|
+ # load(path).each do |key, values|
+ # array << { key: key.to_s, values: HashConverter.to_string(values) }
+ # end
+ # end
+ # end
+
+ # def path
+ # File.join(root_path, 'config', 'translations.yml')
+ # end
+
+ # end
+
+ # end
+ # end
+ # end
+ # end
+ # end
locomotive/steam/entities.rb b/lib/locomotive/steam/entities.rb +49 -0
@@ @@ -0,0 +1,49 @@
+ 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 +58 -0
@@ @@ -0,0 +1,58 @@
+ 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/site.rb b/lib/locomotive/steam/entities/site.rb +32 -0
@@ @@ -0,0 +1,32 @@
+ module Locomotive
+ module Steam
+ module Entities
+
+ class Site
+
+ include Steam::Entity
+
+ def initialize(attributes = {})
+ super({
+ timezone: 'UTC',
+ prefix_default_locale: false
+ }.merge(attributes))
+ end
+
+ def default_locale
+ self.locales.try(:first) || :en
+ end
+
+ def locales
+ attributes[:locales].map(&:to_sym)
+ end
+
+ def to_liquid
+ Steam::Liquid::Drops::Site.new(self)
+ end
+
+ end
+
+ end
+ end
+ end
locomotive/steam/mapper.rb b/lib/locomotive/steam/mapper.rb +30 -0
@@ @@ -0,0 +1,30 @@
+ 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/repositories.rb b/lib/locomotive/steam/repositories.rb +69 -0
@@ @@ -1,6 +1,75 @@
+ module Locomotive
+ module Steam
+ module Repository
+
+ extend ActiveSupport::Concern
+
+ class RecordNotFound < StandardError; end
+
+ attr_reader :adapter
+
+ attr_accessor :current_locale
+
+ def initialize(adapter)
+ @adapter = adapter
+ end
+
+ def all
+ adapter.all(mapper)
+ end
+
+ # def find(id)
+ # adapter.find(mapper, id)
+ # end
+
+ def query(&block)
+ adapter.query(mapper, current_locale, &block)
+ end
+
+ # 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 ||= Steam::Mapper.new(name, options, &block)
+ 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
+
module Locomotive
module Steam
module Repositories
end
end
end
+
+ require_relative 'repositories/site_repository'
locomotive/steam/repositories/filesystem/models/concerns/validation.rb b/lib/locomotive/steam/repositories/filesystem/models/concerns/validation.rb +0 -1
@@ @@ -1,4 +1,3 @@
-
require 'forwardable'
module Locomotive
locomotive/steam/repositories/site_repository.rb b/lib/locomotive/steam/repositories/site_repository.rb +23 -0
@@ @@ -0,0 +1,23 @@
+ module Locomotive
+ module Steam
+
+ class SiteRepository
+
+ include Steam::Repository
+
+ mapping :sites, entity: Steam::Entities::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
+ else
+ query { where('domains.in' => [*domain]) }.first
+ end
+ end
+
+ end
+
+ end
+ end
locomotive/steam/services/site_finder.rb b/lib/locomotive/steam/services/site_finder.rb +1 -0
@@ @@ -5,6 +5,7 @@ module Locomotive
class SiteFinder < Struct.new(:repository, :request)
def find
+ # TODO: full uri instead?
repository.by_host(request.host)
end
locomotive_accounts.bson b/spec/fixtures/mongodb/locomotive_accounts.bson +0 -0
locomotive_accounts.metadata.json b/spec/fixtures/mongodb/locomotive_accounts.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.locomotive_accounts" } ] }
\ No newline at end of file
locomotive_content_assets.bson b/spec/fixtures/mongodb/locomotive_content_assets.bson +0 -0
locomotive_content_assets.metadata.json b/spec/fixtures/mongodb/locomotive_content_assets.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.locomotive_content_assets" } ] }
\ No newline at end of file
locomotive_content_entries.bson b/spec/fixtures/mongodb/locomotive_content_entries.bson +0 -0
locomotive_content_entries.metadata.json b/spec/fixtures/mongodb/locomotive_content_entries.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.locomotive_content_entries" } ] }
\ No newline at end of file
locomotive_content_types.bson b/spec/fixtures/mongodb/locomotive_content_types.bson +0 -0
locomotive_content_types.metadata.json b/spec/fixtures/mongodb/locomotive_content_types.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.locomotive_content_types" } ] }
\ No newline at end of file
locomotive_pages.bson b/spec/fixtures/mongodb/locomotive_pages.bson +0 -0
locomotive_pages.metadata.json b/spec/fixtures/mongodb/locomotive_pages.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.locomotive_pages" } ] }
\ No newline at end of file
locomotive_sites.bson b/spec/fixtures/mongodb/locomotive_sites.bson +0 -0
locomotive_sites.metadata.json b/spec/fixtures/mongodb/locomotive_sites.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.locomotive_sites" } ] }
\ No newline at end of file
locomotive_snippets.bson b/spec/fixtures/mongodb/locomotive_snippets.bson +0 -0
locomotive_snippets.metadata.json b/spec/fixtures/mongodb/locomotive_snippets.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.locomotive_snippets" } ] }
\ No newline at end of file
locomotive_theme_assets.bson b/spec/fixtures/mongodb/locomotive_theme_assets.bson +0 -0
locomotive_theme_assets.metadata.json b/spec/fixtures/mongodb/locomotive_theme_assets.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.locomotive_theme_assets" } ] }
\ No newline at end of file
sessions.bson b/spec/fixtures/mongodb/sessions.bson +0 -0
sessions.metadata.json b/spec/fixtures/mongodb/sessions.metadata.json +1 -0
@@ @@ -0,0 +1 @@
+ { "indexes" : [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "locomotive_engine_dev.sessions" } ] }
\ No newline at end of file
system.indexes.bson b/spec/fixtures/mongodb/system.indexes.bson +0 -0
connection_spec.rb b/spec/integration/mongodb/connection_spec.rb +7 -0
@@ @@ -0,0 +1,7 @@
+ require File.join(File.dirname(__FILE__), 'mongodb_helper')
+
+ describe 'Connection to MongoDB' do
+
+ it { expect(true).to eq(true) }
+
+ end
mongodb_helper.rb b/spec/integration/mongodb/mongodb_helper.rb +5 -0
@@ @@ -0,0 +1,5 @@
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
+
+ RSpec.configure do |config|
+ config.before(:all) { restore_mongodb }
+ end
spec/support/helpers.rb +6 -0
@@ @@ -18,6 +18,12 @@ module Spec
end
end
+ def restore_mongodb
+ path = File.join(File.expand_path(File.dirname(__FILE__)), '..', 'fixtures', 'mongodb')
+ # `mongo steam_test --eval "db.dropDatabase()"`
+ # `mongorestore -d steam_test #{path}`
+ end
+
def run_server
output = ENV['STEAM_VERBOSE'] ? nil : File.join(default_fixture_site_path, 'log/steam.log')
setup_common(output)
spec/unit/adapters/filesystem/condition_spec.rb +124 -0
@@ @@ -0,0 +1,124 @@
+ require 'spec_helper'
+
+ require_relative '../../../../lib/locomotive/steam/adapters/filesystem/condition.rb'
+
+ describe Locomotive::Steam::Adapters::Filesystem::Condition do
+
+ let(:entry) { instance_double('Site', { title: { en: 'Awesome Site' }, content: 'foo' }) }
+ let(:locale) { :en }
+ let(:field) { :title }
+ let(:operator) { :eq }
+ let(:name) { "#{field}.#{operator}"}
+ let(:value) { 'Awesome Site' }
+
+ subject { Locomotive::Steam::Adapters::Filesystem::Condition.new(name, value, locale) }
+
+ describe '#entry_value' do
+ context 'i18n' do
+ let(:name) { 'title.eq' }
+ let(:value) { 'Awesome Site' }
+
+ context 'single entry' do
+ specify('should be match') do
+ expect(subject.matches?(entry)).to eq true
+ end
+
+ specify('return value') do
+ expect(subject.send(:entry_value, entry)).to eq(value)
+ end
+ end
+ end
+ context 'regular way' do
+ let(:name) { 'content.eq' }
+ let(:value) { 'foo' }
+
+ context 'single entry' do
+ specify('should be match') do
+ expect(subject.matches?(entry)).to eq true
+ end
+
+ specify('return value') do
+ expect(subject.send(:entry_value, entry)).to eq(value)
+ end
+ end
+ end
+ end
+
+ describe '#decode_operator_and_field!' do
+ before { subject.send(:decode_operator_and_field!) }
+
+ context 'with normal value' do
+ specify('name should be left part of dot') { expect(subject.field).to eq(field) }
+ specify('operator should be right part of dot') { expect(subject.operator).to eq(operator) }
+ specify('right_operand should be value') { expect(subject.value).to eq(value) }
+ end
+
+ context 'with regex value' do
+ let(:value) { /^[a-z]$/ }
+ specify('operator should be matchtes') { expect(subject.operator).to eq(:matches) }
+ end
+ end
+
+ describe '#decode_operator_and_field!' do
+ context 'with unsupported operator' do
+ let(:name) { 'domains.unsupported' }
+ specify('should be throw Exception') do
+ expect do
+ subject.send(:decode_operator_and_field!)
+ end.to raise_error Locomotive::Steam::Adapters::Filesystem::Condition::UnsupportedOperator
+ end
+ end
+ end
+
+ describe '#adapt_operator!' do
+ let(:name) { 'domains.==' }
+ before do
+ subject.send(:decode_operator_and_field!)
+ subject.send(:adapt_operator!, value)
+ end
+ context 'with single value' do
+ let(:value) { 'sample.example.com' }
+ specify('operator should be :==') { expect(subject.operator).to eq(:==) }
+ end
+ context 'with array of values' do
+ let(:value) { ['sample.example.com'] }
+ specify('operator should be :in') { expect(subject.operator).to eq(:in) }
+ end
+ end
+
+ describe '#array_contains?' do
+ let(:source) { [1, 2, 3, 4] }
+ let(:target) { [1, 2, 3] }
+ context 'with target contains in source' do
+ specify('should be true') do
+ expect(subject.send(:array_contains?, source, target)).to eq true
+ end
+ end
+ end
+
+ describe '#value_in_right_operand?' do
+ context 'value contains in right operand' do
+ let(:value) { [1, 2, 3, 4] }
+ let(:right_operand) { [1, 2, 3] }
+
+ before do
+ allow(subject).to receive(:operator).and_return(operator)
+ allow(subject).to receive(:right_operand).and_return(right_operand)
+ end
+
+ context 'with operator :in' do
+ let(:operator) { :in }
+ specify('should return true') do
+ expect(subject.send(:value_is_in_entry_value?, value)).to eq true
+ end
+ end
+
+ context 'with other operator' do
+ let(:operator) { :nin }
+ specify('should not return true') do
+ expect(subject.send(:value_is_in_entry_value?, value)).to eq false
+ end
+ end
+ end
+ end
+ end
spec/unit/adapters/filesystem/dataset_spec.rb +73 -0
@@ @@ -0,0 +1,73 @@
+ require 'spec_helper'
+
+ require_relative '../../../../lib/locomotive/steam/adapters/filesystem/dataset.rb'
+
+ describe Locomotive::Steam::Adapters::Filesystem::Dataset do
+
+ let(:john) do
+ {
+ firstname: 'John',
+ lastname: 'Doe',
+ email: 'john@example.com',
+ age: 24
+ }
+ end
+
+ let(:jane) do
+ {
+ firstname: 'Jane',
+ lastname: 'Doe',
+ email: 'jane@example.com',
+ age: 20
+ }
+ end
+
+ let(:alex) do
+ {
+ firstname: 'Alex',
+ lastname: 'Turam',
+ email: 'alex@example.com',
+ age: 26
+ }
+ end
+
+ subject { Locomotive::Steam::Adapters::Filesystem::Dataset.new(:foo) } #(loader) }
+
+ before do
+ [john.to_hash, jane.to_hash, alex.to_hash].each do |record|
+ subject.insert record
+ end
+ end
+
+ describe '#all' do
+ it { expect(subject.all).to eq [john.to_hash, jane.to_hash, alex.to_hash] }
+ end
+
+ describe '#find' do
+ specify do
+ expect(subject.find(john[:_id])).to eq(john.to_hash)
+ end
+ end
+
+ describe '#update' do
+ before do
+ subject.update(jane.to_hash.merge(lastname: 'birkin'))
+ end
+
+ specify do
+ expect(subject.find(jane[:_id]).fetch(:lastname)).to eq('birkin')
+ end
+ end
+
+ describe '#exists?' do
+ let(:dataset) { Locomotive::Steam::Adapters::Filesystem::Dataset.new(:dummy) }
+ before do
+ dataset.instance_variable_set('@records', { 1 => 'Record 1', 2 => 'Record 2' })
+ end
+
+ it { expect(dataset.exists?(2)).to eq true }
+ it { expect(dataset.exists?(3)).to eq false }
+ it { expect(dataset.exists?(nil)).to eq false }
+
+ end
+ end
spec/unit/adapters/filesystem/query_spec.rb +64 -0
@@ @@ -0,0 +1,64 @@
+ require 'spec_helper'
+
+ require_relative '../../../../lib/locomotive/steam/adapters/filesystem/dataset.rb'
+ require_relative '../../../../lib/locomotive/steam/adapters/filesystem/condition.rb'
+ require_relative '../../../../lib/locomotive/steam/adapters/filesystem/query.rb'
+
+ describe Locomotive::Steam::Adapters::Filesystem::Query do
+
+ let(:entry_1) { OpenStruct.new(name: 'foo', id: 1) }
+ let(:entry_2) { OpenStruct.new(name: 'bar', id: 2) }
+ let(:entry_3) { OpenStruct.new(name: 'zone', id: 3) }
+ let(:records) { { 1 => entry_1, 2 => entry_2, 3 => entry_3 } }
+ let(:dataset) { Locomotive::Steam::Adapters::Filesystem::Dataset.new(:test) }
+ let(:locale) { :en }
+
+ let(:query) { Locomotive::Steam::Adapters::Filesystem::Query }
+
+ before { allow(dataset).to receive(:records).and_return(records) }
+
+ describe '#limited' do
+ specify do
+ expect(
+ query.new(dataset, locale) do
+ limit(1)
+ end.all
+ ).to eq([entry_1])
+ end
+ end
+
+ describe '#order_by' do
+
+ context 'asc' do
+ specify do
+ expect(
+ query.new(dataset, locale) do
+ order_by('name asc')
+ end.all.map(&:name)
+ ).to eq(['bar', 'foo', 'zone'])
+ end
+ end
+
+ context 'desc' do
+ specify do
+ expect(
+ query.new(dataset, locale) do
+ order_by('name desc')
+ end.all.map(&:name)
+ ).to eq(['zone', 'foo', 'bar'])
+ end
+ end
+ end
+
+ describe '#where' do
+ specify do
+ expect(
+ query.new(dataset, locale) do
+ where('name.eq' => 'foo').
+ where('id.lt' => 2)
+ end.all.map(&:name)
+ ).to eq(['foo'])
+ end
+ end
+
+ end
spec/unit/adapters/filesystem/yaml_loaders/content_entry_spec.rb +22 -0
@@ @@ -0,0 +1,22 @@
+ # require 'spec_helper'
+
+ # describe Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::ContentEntry do
+
+ # let(:root_path) { default_fixture_site_path }
+ # let(:cache) { NoCacheStore.new }
+ # let(:content_type) { instance_double('Articles', slug: 'bands') }
+ # let(:loader) { Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::ContentEntry.new(root_path, cache) }
+
+ # describe '#list_of_attributes' do
+
+ # subject { loader.list_of_attributes(content_type).sort { |a, b| a[:_label] <=> b[:_label] } }
+
+ # it 'tests various stuff' do
+ # expect(subject.size).to eq 3
+ # expect(subject.first[:_label]).to eq 'Alice in Chains'
+ # expect(subject.first[:content_type]).to eq content_type
+ # end
+
+ # end
+
+ # end
spec/unit/adapters/filesystem/yaml_loaders/content_type_spec.rb +20 -0
@@ @@ -0,0 +1,20 @@
+ # require 'spec_helper'
+
+ # describe Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::ContentType do
+
+ # let(:root_path) { default_fixture_site_path }
+ # let(:cache) { NoCacheStore.new }
+ # let(:loader) { Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::ContentType.new(root_path, cache) }
+
+ # describe '#list_of_attributes' do
+
+ # subject { loader.list_of_attributes.sort { |a, b| a[:slug] <=> b[:slug] } }
+
+ # it 'tests various stuff' do
+ # expect(subject.size).to eq 5
+ # expect(subject.first[:slug]).to eq('bands')
+ # end
+
+ # end
+
+ # end
spec/unit/adapters/filesystem/yaml_loaders/page_spec.rb +21 -0
@@ @@ -0,0 +1,21 @@
+ # require 'spec_helper'
+
+ # describe Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::Page do
+
+ # let(:root_path) { default_fixture_site_path }
+ # let(:default_locale) { :en }
+ # let(:cache) { NoCacheStore.new }
+ # let(:loader) { Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::Page.new(root_path, default_locale, cache) }
+
+ # describe '#list_of_attributes' do
+
+ # subject { loader.list_of_attributes.sort { |a, b| a[:_fullpath] <=> b[:_fullpath] } }
+
+ # it 'tests various stuff' do
+ # expect(subject.size).to eq 21
+ # expect(subject.first[:title]).to eq({ en: 'Page not found' })
+ # end
+
+ # end
+
+ # end
spec/unit/adapters/filesystem/yaml_loaders/site_spec.rb +19 -0
@@ @@ -0,0 +1,19 @@
+ require 'spec_helper'
+
+ require_relative '../../../lib/locomotive/steam/adapters/filesystem/yaml_loaders/site.rb'
+
+ describe Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Site do
+
+ let(:root_path) { default_fixture_site_path }
+ let(:cache) { NoCacheStore.new }
+ let(:loader) { Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Site.new(root_path, cache) }
+
+ describe '#load' do
+
+ subject { loader.load }
+
+ it { expect(subject.first[:name]).to eq 'Sample website' }
+
+ end
+
+ end
spec/unit/adapters/filesystem/yaml_loaders/snippet_spec.rb +23 -0
@@ @@ -0,0 +1,23 @@
+ # require 'spec_helper'
+
+ # describe Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::Snippet do
+
+ # let(:root_path) { default_fixture_site_path }
+ # let(:default_locale) { :en }
+ # let(:cache) { NoCacheStore.new }
+ # let(:loader) { Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::Snippet.new(root_path, default_locale, cache) }
+
+ # describe '#list_of_attributes' do
+
+ # subject { loader.list_of_attributes.sort { |a, b| a[:name] <=> b[:name] } }
+
+ # it 'tests various stuff' do
+ # expect(subject.size).to eq 4
+ # expect(subject.first[:slug]).to eq('a_complicated-one')
+ # expect(subject[1][:name]).to eq('Footer')
+ # expect(subject[1][:slug]).to eq('footer')
+ # end
+
+ # end
+
+ # end
spec/unit/adapters/filesystem/yaml_loaders/translation_spec.rb +21 -0
@@ @@ -0,0 +1,21 @@
+ # require 'spec_helper'
+
+ # describe Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::Translation do
+
+ # let(:root_path) { default_fixture_site_path }
+ # let(:cache) { NoCacheStore.new }
+ # let(:loader) { Locomotive::Steam::Repositories::Filesystem::YAMLLoaders::Translation.new(root_path, cache) }
+
+ # describe '#list_of_attributes' do
+
+ # subject { loader.list_of_attributes }
+
+ # it 'tests various stuff' do
+ # expect(subject.size).to eq 1
+ # expect(subject.first[:key]).to eq('powered_by')
+ # expect(subject.first[:values]).to eq({ 'en' => 'Powered by', 'fr' => 'Propulsé par' })
+ # end
+
+ # end
+
+ # end
spec/unit/repositories/site_repository_spec.rb +28 -0
@@ @@ -0,0 +1,28 @@
+ require 'spec_helper'
+
+ require_relative '../../../lib/locomotive/steam/adapters/filesystem.rb'
+
+ describe Locomotive::Steam::SiteRepository do
+
+ let(:adapter) { Locomotive::Steam::FilesystemAdapter.new(default_fixture_site_path) }
+ let(:repository) { Locomotive::Steam::SiteRepository.new(adapter) }
+
+ describe '#all' do
+
+ subject { repository.all }
+
+ it { expect(subject.size).to eq 1 }
+
+ end
+
+ describe '#query' do
+
+ subject do
+ repository.query { where(subdomain: 'sample') }.first
+ end
+
+ it { expect(subject.name).to eq 'Sample website' }
+
+ end
+
+ end