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 |