Renamed to Wagon ;D
Rodrigo Alvarez
committed Feb 27, 2013
commit ae585cf0eae3e4178ecda0034d6904015fb91db0
Showing 145
changed files with
3524 additions
and 3489 deletions
Gemfile
+1
-1
| @@ | @@ -1,6 +1,6 @@ |
| source 'https://rubygems.org' | |
| - | # Specify your gem's dependencies in builder.gemspec |
| + | # Specify your gem's dependencies in wagon.gemspec |
| gemspec | |
| gem 'locomotivecms_mounter', path: '../gems/mounter', require: false | |
README.md
+10
-10
| @@ | @@ -1,6 +1,6 @@ |
| - | # LocomotiveCMS::Builder |
| + | # LocomotiveCMS::Wagon |
| - | The LocomotiveCMS builder is a site generator for the LocomotiveCMS engine powered by all the efficient and modern HTML development tools (Haml, SASS, Compass, Less). |
| + | The LocomotiveCMS wagon is a site generator for the LocomotiveCMS engine powered by all the efficient and modern HTML development tools (Haml, SASS, Compass, Less). |
| (TO BE COMPLETED) | |
| @@ | @@ -15,10 +15,10 @@ Also, please, keep in mind, that is nearly an alpha version so it is not stable |
| Note: If you want to contribute, you may consider to fork it instead | |
| - | ### Get the source of the builder |
| + | ### Get the source of the wagon |
| - | $ git clone git://github.com/locomotivecms/builder.git |
| - | $ cd builder |
| + | $ git clone git://github.com/locomotivecms/wagon.git |
| + | $ cd wagon |
| $ git checkout wip | |
| Note: Again, if you want to contribute, you may consider to fork it instead | |
| @@ | @@ -31,15 +31,15 @@ Also, please, keep in mind, that is nearly an alpha version so it is not stable |
| #### Run the server with a default site | |
| - | $ bundle exec bin/builder server <path to the mounter gem>/spec/fixtures/default |
| + | $ bundle exec bin/wagon server <path to the mounter gem>/spec/fixtures/default |
| #### Push a site (WIP) | |
| - | $ bundle exec bin/builder push <path to your LocomotiveCMS local site> <url of your remote LocomotiveCMS site> <email of your admin account> <password> |
| + | $ bundle exec bin/wagon push <path to your LocomotiveCMS local site> <url of your remote LocomotiveCMS site> <email of your admin account> <password> |
| #### Pull a site (Not tested yet) | |
| - | $ bundle exec bin/builder pull <url of your remote LocomotiveCMS site> <email of your admin account> <password> |
| + | $ bundle exec bin/wagon pull <url of your remote LocomotiveCMS site> <email of your admin account> <password> |
| ## Contributing | |
| @@ | @@ -53,7 +53,7 @@ Also, please, keep in mind, that is nearly an alpha version so it is not stable |
| Add this line to your application's Gemfile: | |
| - | gem 'locomotive_builder' |
| + | gem 'locomotive_wagon' |
| And then execute: | |
| @@ | @@ -61,7 +61,7 @@ And then execute: |
| Or install it yourself as: | |
| - | $ gem install locomotive_builder |
| + | $ gem install locomotive_wagon |
| ## Usage | |
Rakefile
+6
-6
| @@ | @@ -12,17 +12,17 @@ require 'rubygems/package_task' |
| $LOAD_PATH.unshift File.expand_path('../lib', __FILE__) | |
| - | require 'locomotive/builder' |
| - | require 'locomotive/builder/version' |
| + | require 'locomotive/wagon' |
| + | require 'locomotive/wagon/version' |
| - | gemspec = eval(File.read('locomotivecms_builder.gemspec')) |
| + | gemspec = eval(File.read('locomotivecms_wagon.gemspec')) |
| Gem::PackageTask.new(gemspec) do |pkg| | |
| pkg.gem_spec = gemspec | |
| end | |
| desc 'build the gem and release it to rubygems.org' | |
| task :release => :gem do | |
| - | sh "gem push pkg/locomotivecms_builder-#{gemspec.version}.gem" |
| + | sh "gem push pkg/locomotivecms_wagon-#{gemspec.version}.gem" |
| end | |
| namespace :development do | |
| @@ | @@ -38,9 +38,9 @@ namespace :development do |
| FileUtils.rm_rf(File.join(File.dirname(__FILE__), 'site')) | |
| VCR.use_cassette('pull') do | |
| - | exit unless Locomotive::Builder.clone("site", {"host" => "http://locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive") |
| + | exit unless Locomotive::Wagon.clone("site", {"host" => "http://locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive") |
| end | |
| - | Locomotive::Builder.push("site", {"host" => "http://locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive", "force" => true, "data" => true) |
| + | Locomotive::Wagon.push("site", {"host" => "http://locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive", "force" => true, "data" => true) |
| end | |
| end | |
TODO
+2
-2
| @@ | @@ -36,7 +36,7 @@ x params to launch the thin server (port, address ?) |
| - pull | |
| - translations (Rod) | |
| - version checkings: | |
| - | - builder when running it |
| + | - wagon when running it |
| - engine when pushing a site | |
| - nice error page (replace the default exception middleware) to display: | |
| - liquid errors | |
| @@ | @@ -48,7 +48,7 @@ x params to launch the thin server (port, address ?) |
| - refactoring: | |
| x generators/sites -> generators/site | |
| x list is only for site generators | |
| - | x move the call to the CC generator directly in the builder.rb file |
| + | x move the call to the CC generator directly in the wagon.rb file |
| *** FEATURES *** | |
bin/builder
+0
-9
| @@ | @@ -1,9 +0,0 @@ |
| - | #!/usr/bin/env ruby |
| - | |
| - | # needed if you launch it without bundler |
| - | $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib')) |
| - | |
| - | require 'locomotive/builder' |
| - | require 'locomotive/builder/cli' |
| - | |
| - | Locomotive::Builder::CLI::Main.start |
| \ No newline at end of file | |
bin/wagon
+9
-0
| @@ | @@ -0,0 +1,9 @@ |
| + | #!/usr/bin/env ruby |
| + | |
| + | # needed if you launch it without bundler |
| + | $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib')) |
| + | |
| + | require 'locomotive/wagon' |
| + | require 'locomotive/wagon/cli' |
| + | |
| + | Locomotive::Wagon::CLI::Main.start |
| \ No newline at end of file | |
generators/blank/Gemfile.tt
+1
-1
| @@ | @@ -2,7 +2,7 @@ source 'https://rubygems.org' |
| # ruby '1.9.3' | |
| - | gem 'locomotivecms_builder', '<%= config[:version] -%>' |
| + | gem 'locomotivecms_wagon', '<%= config[:version] -%>' |
| group :development do | |
| # Mac OS X | |
generators/blank/config.ru
+2
-2
| @@ | @@ -1,3 +1,3 @@ |
| - | require 'locomotive/builder/standalone_server' |
| + | require 'locomotive/wagon/standalone_server' |
| - | run Locomotive::Builder::StandaloneServer.new(File.expand_path('.')) |
| \ No newline at end of file | |
| + | run Locomotive::Wagon::StandaloneServer.new(File.expand_path('.')) |
| \ No newline at end of file | |
generators/bootstrap/Gemfile.tt
+1
-1
| @@ | @@ -2,7 +2,7 @@ source 'https://rubygems.org' |
| # ruby '1.9.3' | |
| - | gem 'locomotivecms_builder', '<%= config[:version] -%>' |
| + | gem 'locomotivecms_wagon', '<%= config[:version] -%>' |
| group :development do | |
| # Mac OS X | |
generators/bootstrap/config.ru
+2
-2
| @@ | @@ -1,3 +1,3 @@ |
| - | require 'locomotive/builder/standalone_server' |
| + | require 'locomotive/wagon/standalone_server' |
| - | run Locomotive::Builder::StandaloneServer.new(File.expand_path('.')) |
| \ No newline at end of file | |
| + | run Locomotive::Wagon::StandaloneServer.new(File.expand_path('.')) |
| \ No newline at end of file | |
locomotive/builder.rb b/lib/locomotive/builder.rb
+0
-163
| @@ | @@ -1,163 +0,0 @@ |
| - | require 'locomotive/builder/version' |
| - | require 'locomotive/builder/logger' |
| - | require 'locomotive/builder/exceptions' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | |
| - | # Create a site from a site generator. |
| - | # |
| - | # @param [ String ] name The name of the site (underscored) |
| - | # @param [ String ] path The destination path of the site |
| - | # @param [ Object ] generator The wrapping class of the generator itself |
| - | # |
| - | def self.init(name, path, generator) |
| - | generator.klass.start [name, path] |
| - | end |
| - | |
| - | # Start the thin server which serves the LocomotiveCMS site from the system. |
| - | # |
| - | # @param [ String ] path The path of the site |
| - | # @param [ Hash ] options The options for the thin server (host, port) |
| - | # |
| - | def self.serve(path, options) |
| - | if reader = self.require_mounter(path, true) |
| - | Bundler.require 'misc' |
| - | |
| - | require 'thin' |
| - | require 'locomotive/builder/server' |
| - | |
| - | server = Thin::Server.new(options[:host], options[:port], Locomotive::Builder::Server.new(reader)) |
| - | server.threaded = true # TODO: make it an option ? |
| - | server.start |
| - | end |
| - | end |
| - | |
| - | # Generate components for the LocomotiveCMS site such as content types, snippets, pages. |
| - | # |
| - | # @param [ Symbol ] name The name of the generator |
| - | # @param [ Array ] *args The arguments for the generator |
| - | # |
| - | def self.generate(name, *args) |
| - | Bundler.require 'misc' |
| - | |
| - | lib = "locomotive/builder/generators/#{name}" |
| - | require lib |
| - | |
| - | generator = lib.camelize.constantize.new(args, {}, {}) |
| - | generator.invoke_all |
| - | end |
| - | |
| - | # Push a site to a remote LocomotiveCMS engine described |
| - | # by the config/deploy.yml file of the site and for a specific environment. |
| - | # |
| - | # @param [ String ] path The path of the site |
| - | # @param [ Hash ] connection_info The information to get connected to the remote site |
| - | # @param [ Hash ] options The options passed to the push process |
| - | # |
| - | def self.push(path, connection_info, options = {}) |
| - | if reader = self.require_mounter(path, true) |
| - | Bundler.require 'misc' |
| - | |
| - | writer = Locomotive::Mounter::Writer::Api.instance |
| - | |
| - | connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api" |
| - | |
| - | _options = { mounting_point: reader.mounting_point, console: true }.merge(options).symbolize_keys |
| - | _options[:only] = _options.delete(:resources) |
| - | |
| - | writer.run!(_options.merge(connection_info)) |
| - | end |
| - | end |
| - | |
| - | # Pull a site from a remote LocomotiveCMS engine described |
| - | # by the config/deploy.yml file of the site and for a specific environment. |
| - | # |
| - | # @param [ String ] path The path of the site |
| - | # @param [ Hash ] connection_info The information to get connected to the remote site |
| - | # @param [ Hash ] options The options passed to the pull process |
| - | # |
| - | def self.pull(path, connection_info, options = {}) |
| - | self.require_mounter(path) |
| - | |
| - | Bundler.require 'misc' |
| - | |
| - | connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api" |
| - | |
| - | _options = { console: true }.merge(options) |
| - | _options[:only] = _options.delete(:resources) |
| - | |
| - | reader = Locomotive::Mounter::Reader::Api.instance |
| - | reader.run!(_options.merge(connection_info)) |
| - | |
| - | writer = Locomotive::Mounter::Writer::FileSystem.instance |
| - | writer.run!(mounting_point: reader.mounting_point, target_path: path) |
| - | rescue Exception => e |
| - | puts e.backtrace |
| - | end |
| - | |
| - | def self.clone(path, connection_info, options = {}) |
| - | if File.exists?(path) |
| - | puts "Path already exists. If it's an existing site, you might want to pull instead of clone." |
| - | return false |
| - | end |
| - | require 'locomotive/mounter' |
| - | |
| - | connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api" |
| - | |
| - | _options = options.dup |
| - | _options[:only] = _options.delete(:resources) |
| - | |
| - | reader = Locomotive::Mounter::Reader::Api.instance |
| - | reader.run!(_options.merge(connection_info)) |
| - | |
| - | writer = Locomotive::Mounter::Writer::FileSystem.instance |
| - | writer.run!(mounting_point: reader.mounting_point, target_path: path) |
| - | # rescue Exception => e |
| - | # puts e.backtrace |
| - | end |
| - | |
| - | # Destroy a remote site |
| - | # |
| - | # @param [ String ] path The path of the site |
| - | # @param [ Hash ] connection_info The information to get connected to the remote site |
| - | # @param [ Hash ] options The options passed to the push process |
| - | # |
| - | def self.destroy(path, connection_info, options = {}) |
| - | self.require_mounter(path) |
| - | |
| - | connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api" |
| - | |
| - | Locomotive::Mounter::EngineApi.set_token connection_info.symbolize_keys |
| - | Locomotive::Mounter::EngineApi.delete('/current_site.json') |
| - | end |
| - | |
| - | # Load the Locomotive::Mounter lib and set it up (logger, ...etc). |
| - | # If the second parameter is set to true, then the method builds |
| - | # an instance of the reader from the path passed in first parameter. |
| - | # |
| - | # @param [ String ] path The path to the local site |
| - | # @param [ Boolean ] get_reader Tell if it builds an instance of the reader. |
| - | # |
| - | # @param [ Object ] An instance of the reader is the get_reader parameter has been set. |
| - | # |
| - | def self.require_mounter(path, get_reader = false) |
| - | Locomotive::Builder::Logger.setup(path, false) |
| - | |
| - | require 'locomotive/mounter' |
| - | |
| - | Locomotive::Mounter.logger = Locomotive::Builder::Logger.instance.logger |
| - | |
| - | if get_reader |
| - | begin |
| - | reader = Locomotive::Mounter::Reader::FileSystem.instance |
| - | reader.run!(path: path) |
| - | reader |
| - | rescue Exception => e |
| - | raise Locomotive::Builder::MounterException.new "Unable to read the local LocomotiveCMS site. Please check the logs.", e |
| - | end |
| - | end |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/cli.rb b/lib/locomotive/builder/cli.rb
+0
-229
| @@ | @@ -1,229 +0,0 @@ |
| - | require 'thor' |
| - | require 'thor/runner' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module CLI |
| - | |
| - | module CheckPath |
| - | |
| - | protected |
| - | |
| - | # Check if the path given in option ('.' by default) points to a LocomotiveCMS |
| - | # site. It is also possible to pass a path other than the one from the options. |
| - | # |
| - | # @param [ String ] path The optional path instead of options['path'] |
| - | # |
| - | # @return [ String ] The fullpath to the LocomotiveCMS site or nil if it is not a valid site. |
| - | # |
| - | def check_path!(path = nil) |
| - | path ||= options['path'] |
| - | |
| - | path = path == '.' ? Dir.pwd : File.expand_path(path) |
| - | |
| - | (File.exists?(File.join(path, 'config', 'site.yml')) ? path : nil).tap do |_path| |
| - | if _path.nil? |
| - | say 'The path does not point to a LocomotiveCMS site', :red |
| - | end |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | class Generate < Thor |
| - | |
| - | include Locomotive::Builder::CLI::CheckPath |
| - | |
| - | class_option :path, aliases: '-p', type: :string, default: '.', optional: true, desc: 'if your LocomotiveCMS site is not in the current path' |
| - | |
| - | desc 'content_type NAME FIELDS', 'Create a content type with NAME as the slug and FIELDS as the list of fields.' |
| - | long_desc <<-LONGDESC |
| - | Create a content type with NAME as the slug and FIELDS as the list of fields. |
| - | The fields follows that schema: |
| - | |
| - | field_1[:type][:required] field_2[:type][:required] |
| - | |
| - | Examples: |
| - | |
| - | * builder generate content_type songs name:string duration:string |
| - | |
| - | * builder generate content_type posts title body:text:true published_at:date |
| - | LONGDESC |
| - | def content_type(name, *fields) |
| - | say('The fields are missing', :red) and return false if fields.empty? |
| - | |
| - | if check_path! |
| - | Locomotive::Builder.generate :content_type, name, self.options['path'], fields |
| - | end |
| - | end |
| - | |
| - | desc 'page FULLPATH', 'Create a page. No need to pass an extension to the FULLPATH arg' |
| - | long_desc <<-LONGDESC |
| - | Create a page. The generator will ask for the extension (liquid or haml) and also |
| - | if the page is localized or not. |
| - | |
| - | Examples: |
| - | |
| - | * builder generate page contact |
| - | |
| - | * builder generate page about_us/me |
| - | LONGDESC |
| - | def page(fullpath) |
| - | if check_path! |
| - | Locomotive::Builder.generate :page, fullpath, self.options['path'] |
| - | end |
| - | end |
| - | |
| - | desc 'snippet SLUG', 'Create a snippet' |
| - | long_desc <<-LONGDESC |
| - | Create a snippet. The generator will ask for the extension (liquid or haml) and also |
| - | if the snippet is localized or not. |
| - | |
| - | Example: |
| - | |
| - | * builder generate snippet footer |
| - | LONGDESC |
| - | def snippet(slug) |
| - | if check_path! |
| - | Locomotive::Builder.generate :snippet, slug, self.options['path'] |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | class Main < Thor |
| - | |
| - | include Locomotive::Builder::CLI::CheckPath |
| - | |
| - | desc 'version', 'Version of the LocomotiveCMS builder' |
| - | def version |
| - | require 'locomotive/builder/version' |
| - | say Locomotive::Builder::VERSION |
| - | end |
| - | |
| - | desc 'init NAME [PATH]', 'Create a brand new LocomotiveCMS site' |
| - | method_option :template, aliases: '-t', type: 'string', default: 'blank', desc: 'instead of building from a blank site, you can have a pre-fetched site with form a template (see the templates command)' |
| - | def init(name, path = '.') |
| - | require 'locomotive/builder/generators/site' |
| - | generator = Locomotive::Builder::Generators::Site.get(options[:template]) |
| - | if generator.nil? |
| - | say "Unknown site template '#{options[:template]}'", :red |
| - | else |
| - | begin |
| - | Locomotive::Builder.init(name, path, generator) |
| - | rescue GeneratorException => e |
| - | say e.message, :red |
| - | end |
| - | end |
| - | end |
| - | |
| - | desc 'generate TYPE ...ARGS', 'Generate resources (content_types, page, snippets) for a LocomotiveCMS site' |
| - | subcommand 'generate', Generate |
| - | |
| - | desc 'list_templates', 'List all the templates to create either a site or a content type' |
| - | def list_templates |
| - | require 'locomotive/builder/generators/site' |
| - | if Locomotive::Builder::Generators::Site.empty? |
| - | say 'No templates', :red |
| - | else |
| - | Locomotive::Builder::Generators::Site.list.each do |info| |
| - | say info.name, :bold, false |
| - | say " - #{info.description}" unless info.description.blank? |
| - | end |
| - | end |
| - | end |
| - | |
| - | desc 'serve [PATH]', 'Serve a LocomotiveCMS site from the file system' |
| - | method_option :host, aliases: '-h', type: 'string', default: '0.0.0.0', desc: 'The host (address) of the Thin server' |
| - | method_option :port, aliases: '-p', type: 'string', default: '3333', desc: 'The port of the Thin server' |
| - | def serve(path = '.') |
| - | if check_path!(path) |
| - | begin |
| - | Locomotive::Builder.serve(path, options) |
| - | rescue Exception => e |
| - | say e.message, :red |
| - | end |
| - | end |
| - | end |
| - | |
| - | desc 'push ENV [PATH]', 'Push a site to a remote LocomotiveCMS engine' |
| - | method_option :resources, aliases: '-r', type: 'array', default: nil, desc: 'Only push the resource(s) passed in argument' |
| - | method_option :force, aliases: '-f', type: 'boolean', default: false, desc: 'Force the push of a resource' |
| - | method_option :data, aliases: '-d', type: 'boolean', default: false, desc: 'Push the content entries and the editable elements (by default, they are not)' |
| - | def push(env, path = '.') |
| - | if check_path!(path) |
| - | if connection_info = self.retrieve_connection_info(env, path) |
| - | begin |
| - | Locomotive::Builder.push(path, connection_info, options) |
| - | rescue Exception => e |
| - | say e.message, :red |
| - | end |
| - | end |
| - | end |
| - | end |
| - | |
| - | desc 'pull ENV [PATH]', 'Pull a site from a remote LocomotiveCMS engine' |
| - | method_option :resources, aliases: '-r', type: 'array', default: nil, desc: 'Only pull the resource(s) passed in argument' |
| - | # method_option :force, aliases: '-f', type: 'boolean', default: false, desc: 'Force the push of a resource' |
| - | # method_option :data, aliases: '-d', type: 'boolean', default: false, desc: 'Push the content entries and the editable elements (by default, they are not)' |
| - | def pull(env, path = '.') |
| - | if check_path!(path) |
| - | if connection_info = self.retrieve_connection_info(env, path) |
| - | begin |
| - | Locomotive::Builder.pull(path, connection_info, options) |
| - | rescue Exception => e |
| - | say e.message, :red |
| - | end |
| - | end |
| - | end |
| - | end |
| - | |
| - | desc 'destroy ENV [PATH]', 'Destroy a remote LocomotiveCMS engine' |
| - | def destroy(env, path = '.') |
| - | if check_path!(path) |
| - | if connection_info = self.retrieve_connection_info(env, path) |
| - | if ask('Are you sure ?', limited_to: %w(yes no)) == 'yes' |
| - | Locomotive::Builder.destroy(path, connection_info) |
| - | else |
| - | say 'The destroy operation has been cancelled', :red |
| - | end |
| - | end |
| - | end |
| - | end |
| - | |
| - | # desc "pull NAME SITE_URL EMAIL PASSWORD", "Pull an existing LocomotiveCMS site powered by the engine" |
| - | # def pull(name, site_url, email, password) |
| - | # say("ERROR: \"#{name}\" directory already exists", :red) and return if File.exists?(name) |
| - | # Locomotive::Builder.pull(name, site_url, email, password) |
| - | # end |
| - | |
| - | protected |
| - | |
| - | # From a site specified by a path, retrieve the information of the connection |
| - | # for a environment located in the config/deploy.yml file of the site. |
| - | # |
| - | # @param [ String ] env The environment (development, staging, production, ...etc) |
| - | # @param [ String ] path The path of the local site |
| - | # |
| - | # @return [ Hash ] The information of the connection or nil if errors |
| - | # |
| - | def retrieve_connection_info(env, path) |
| - | connection_info = nil |
| - | begin |
| - | path_to_deploy_file = File.join(path, 'config', 'deploy.yml') |
| - | connection_info = YAML::load(File.open(path_to_deploy_file).read)[env.to_s] |
| - | |
| - | if connection_info.nil? |
| - | raise "No #{env.to_s} environment found in the config/deploy.yml file" |
| - | end |
| - | rescue Exception => e |
| - | say "Unable to read the information about the remote LocomotiveCMS site (#{e.message})", :red |
| - | end |
| - | connection_info |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/exceptions.rb b/lib/locomotive/builder/exceptions.rb
+0
-35
| @@ | @@ -1,35 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | |
| - | class DefaultException < ::Exception |
| - | |
| - | def initialize(message = nil, parent_exception = nil) |
| - | self.log_backtrace(parent_exception) if parent_exception |
| - | |
| - | super(message) |
| - | end |
| - | |
| - | protected |
| - | |
| - | def log_backtrace(parent_exception) |
| - | full_error_message = "#{parent_exception.message}\n\t" |
| - | full_error_message += parent_exception.backtrace.join("\n\t") |
| - | full_error_message += "\n\n" |
| - | Locomotive::Builder::Logger.fatal full_error_message |
| - | end |
| - | |
| - | end |
| - | |
| - | class MounterException < DefaultException |
| - | end |
| - | |
| - | class GeneratorException < DefaultException |
| - | |
| - | def log_backtrace(parent_exception) |
| - | # Logger not initialized at this step |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
locomotive/builder/generators/content_type.rb b/lib/locomotive/builder/generators/content_type.rb
+0
-47
| @@ | @@ -1,47 +0,0 @@ |
| - | require 'thor/group' |
| - | require 'ostruct' |
| - | require 'active_support' |
| - | require 'active_support/core_ext' |
| - | require 'faker' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module Generators |
| - | class ContentType < Thor::Group |
| - | |
| - | include Thor::Actions |
| - | |
| - | argument :name |
| - | argument :target_path |
| - | argument :fields |
| - | |
| - | def copy_sources |
| - | directory('.', target_path, { recursive: true }, { |
| - | name: self.name, |
| - | fields: extract_fields(fields) |
| - | }) |
| - | end |
| - | |
| - | def self.source_root |
| - | File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'content_type') |
| - | end |
| - | |
| - | protected |
| - | |
| - | def extract_fields(fields) |
| - | fields.map do |raw_attributes| |
| - | name, type, required = raw_attributes.split(':') |
| - | |
| - | OpenStruct.new({ |
| - | name: name, |
| - | type: type || 'string', |
| - | required: %w(true required).include?(required) |
| - | }) |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/generators/page.rb b/lib/locomotive/builder/generators/page.rb
+0
-57
| @@ | @@ -1,57 +0,0 @@ |
| - | require 'thor/group' |
| - | require 'active_support' |
| - | require 'active_support/core_ext' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module Generators |
| - | class Page < Thor::Group |
| - | |
| - | include Thor::Actions |
| - | |
| - | argument :slug |
| - | argument :target_path # path to the site |
| - | |
| - | attr_accessor :haml, :locales |
| - | |
| - | def ask_for_haml_and_locales |
| - | self.locales = [] |
| - | self.haml = yes?('Do you prefer a HAML template ?') |
| - | |
| - | if yes?('Is your page localized ?') |
| - | self.locales = ask('What are the locales other than the default one (comma separated) ?').split(',').map(&:strip) |
| - | end |
| - | end |
| - | |
| - | def create_page |
| - | extension = self.haml ? 'liquid.haml' : 'liquid' |
| - | |
| - | segments = self.slug.split('/') |
| - | while segment = segments.pop do |
| - | options = { slug: segment, translated: false } |
| - | file_path = File.join(pages_path, segments, segment) |
| - | |
| - | template "template.#{extension}.tt", "#{file_path}.#{extension}", options |
| - | |
| - | self.locales.each do |locale| |
| - | options[:translated] = true |
| - | template "template.#{extension}.tt", "#{file_path}.#{locale}.#{extension}", options |
| - | end |
| - | end |
| - | end |
| - | |
| - | def self.source_root |
| - | File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'page') |
| - | end |
| - | |
| - | protected |
| - | |
| - | def pages_path |
| - | File.join(target_path, 'app', 'views', 'pages') |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/generators/site.rb b/lib/locomotive/builder/generators/site.rb
+0
-98
| @@ | @@ -1,98 +0,0 @@ |
| - | require 'ostruct' |
| - | require 'singleton' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module Generators |
| - | |
| - | module Site |
| - | |
| - | # Register a generator by adding it to the list of existing generators. |
| - | # |
| - | # @param [ String ] name The name of the generator |
| - | # @param [ Class ] klass The class of the generator |
| - | # @param [ String ] description The description of the generator (can be nil) |
| - | # |
| - | # @return [ Boolean ] True if the registration has been successful, false otherwise. |
| - | # |
| - | def self.register(name, klass, description = nil) |
| - | Locomotive::Builder::Generators::Site::List.instance.register(name, klass, description) |
| - | end |
| - | |
| - | # Return the information about a generator from its name. |
| - | # |
| - | # @param [ String ] name The name of the generator |
| - | # |
| - | # @return [ Object ] The information of the found generator or nil |
| - | # |
| - | def self.get(name) |
| - | Locomotive::Builder::Generators::Site::List.instance.get(name) |
| - | end |
| - | |
| - | # List all the generators |
| - | # |
| - | # @return [ Array ] The filtered (or not) list of generators |
| - | # |
| - | def self.list |
| - | Locomotive::Builder::Generators::Site::List.instance._list |
| - | end |
| - | |
| - | # Tell if the list of generators is empty or not . |
| - | # |
| - | # @return [ Boolean ] True if empty |
| - | # |
| - | def self.empty? |
| - | Locomotive::Builder::Generators::Site::List.instance._list.empty? |
| - | end |
| - | |
| - | class List |
| - | |
| - | include ::Singleton |
| - | |
| - | attr_accessor :_list |
| - | |
| - | def initialize |
| - | self._list = [] |
| - | end |
| - | |
| - | # Return the information about a generator from its name. |
| - | # |
| - | # @param [ String ] name The name of the generator |
| - | # |
| - | # @return [ Object ] The information of the found generator or nil |
| - | # |
| - | def get(name) |
| - | self._list.detect { |entry| entry.name == name.to_sym } |
| - | end |
| - | |
| - | # Register a generator by adding it to the list of existing generators. |
| - | # |
| - | # @param [ String ] name The name of the generator |
| - | # @param [ Class ] klass The class of the generator |
| - | # @param [ String ] description The description of the generator (can be nil) |
| - | # |
| - | # @return [ Boolean ] True if the registration has been successful, false otherwise. |
| - | # |
| - | def register(name, klass, description = nil) |
| - | return false unless self.get(name).nil? |
| - | |
| - | self._list << OpenStruct.new({ |
| - | name: name.to_sym, |
| - | klass: klass, |
| - | description: description ? description.strip.gsub("\n", '') : nil |
| - | }) |
| - | |
| - | self._list.last |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| - | |
| - | require 'locomotive/builder/generators/site/base' |
| - | require 'locomotive/builder/generators/site/blank' |
| - | require 'locomotive/builder/generators/site/bootstrap' |
| - | require 'locomotive/builder/generators/site/unzip' |
locomotive/builder/generators/site/base.rb b/lib/locomotive/builder/generators/site/base.rb
+0
-30
| @@ | @@ -1,30 +0,0 @@ |
| - | require 'thor/group' |
| - | require 'active_support' |
| - | require 'active_support/core_ext' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module Generators |
| - | module Site |
| - | |
| - | class Base < Thor::Group |
| - | |
| - | include Thor::Actions |
| - | |
| - | argument :name |
| - | argument :target_path |
| - | |
| - | def self.source_root |
| - | File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'generators', self.name.demodulize.underscore) |
| - | end |
| - | |
| - | def destination |
| - | File.join(target_path, name) |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/generators/site/blank.rb b/lib/locomotive/builder/generators/site/blank.rb
+0
-23
| @@ | @@ -1,23 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Generators |
| - | module Site |
| - | |
| - | class Blank < Base |
| - | |
| - | def copy_sources |
| - | directory('.', self.destination, { recursive: true }, { |
| - | name: self.name, |
| - | version: Locomotive::Builder::VERSION |
| - | }) |
| - | end |
| - | |
| - | end |
| - | |
| - | Locomotive::Builder::Generators::Site.register(:blank, Blank, %{ |
| - | A blank LocomotiveCMS site with the minimal files. |
| - | }) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/generators/site/bootstrap.rb b/lib/locomotive/builder/generators/site/bootstrap.rb
+0
-35
| @@ | @@ -1,35 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Generators |
| - | module Site |
| - | |
| - | class Bootstrap < Base |
| - | |
| - | def copy_sources |
| - | directory('.', self.destination, { recursive: true }, { |
| - | name: self.name, |
| - | version: Locomotive::Builder::VERSION |
| - | }) |
| - | end |
| - | |
| - | def choose_haml_over_html |
| - | if yes?('Do you prefer HAML templates ?') |
| - | remove_file File.join(self.destination, 'app/views/pages/index.liquid') |
| - | remove_file File.join(self.destination, 'app/views/pages/404.liquid') |
| - | remove_file File.join(self.destination, 'app/views/snippets/footer.liquid') |
| - | else |
| - | remove_file File.join(self.destination, 'app/views/pages/index.liquid.haml') |
| - | remove_file File.join(self.destination, 'app/views/pages/404.liquid.haml') |
| - | remove_file File.join(self.destination, 'app/views/snippets/footer.liquid.haml') |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | Locomotive::Builder::Generators::Site.register(:bootstrap, Bootstrap, %{ |
| - | A LocomotiveCMS site powered by Twitter bootstrap (v2.2.2). |
| - | }) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/generators/site/unzip.rb b/lib/locomotive/builder/generators/site/unzip.rb
+0
-81
| @@ | @@ -1,81 +0,0 @@ |
| - | require 'open-uri' |
| - | require 'zip/zipfilesystem' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module Generators |
| - | module Site |
| - | |
| - | class Unzip < Base |
| - | |
| - | def prepare |
| - | remove_file join('tmp') |
| - | empty_directory join('tmp') |
| - | end |
| - | |
| - | def ask_for_location |
| - | @location = ask('What is the location (on the filesystem or url) of the zip file ?') |
| - | raise GeneratorException.new('Please enter a location') if @location.blank? |
| - | end |
| - | |
| - | def download_or_copy |
| - | @template_path = join('tmp', File.basename(@location)) |
| - | |
| - | if @location =~ /^https?:\/\// |
| - | say "downloading...#{@location}" |
| - | create_file @template_path, open(@location).read |
| - | else |
| - | say "copying...#{@location}" |
| - | create_file @template_path, open(@location, 'rb') { |io| io.read } |
| - | end |
| - | end |
| - | |
| - | def unzip |
| - | say "unzipping...#{@template_path}" |
| - | |
| - | begin |
| - | Zip::ZipFile.open(@template_path) do |zipfile| |
| - | zipfile.each do |file| |
| - | next if file.name =~ /^__MACOSX/ |
| - | zipfile.extract(file, join('tmp', file.name)) |
| - | |
| - | @path = $1 if file.name =~ /(.*)\/config\/site.yml$/ |
| - | end |
| - | end |
| - | rescue Exception => e |
| - | raise GeneratorException.new("Unable to unzip the archive") |
| - | end |
| - | |
| - | raise GeneratorException.new('Not a valid LocomotiveCMS site') if @path.blank? |
| - | end |
| - | |
| - | def copy_sources |
| - | self.class.source_root = File.expand_path(join('tmp', @path, '/')) |
| - | say "copying files from #{self.class.source_root} / #{self.destination}" |
| - | directory('.', self.destination, { recursive: true }) |
| - | end |
| - | |
| - | def self.source_root |
| - | # only way to change the source root from the instance |
| - | @@source_root |
| - | end |
| - | |
| - | def self.source_root=(value) |
| - | @@source_root = value |
| - | end |
| - | |
| - | protected |
| - | |
| - | def join(*args) |
| - | File.join(self.destination, *args) |
| - | end |
| - | |
| - | end |
| - | |
| - | Locomotive::Builder::Generators::Site.register(:unzip, Unzip, %{ |
| - | Unzip a local or remote (http, https, ftp) zipped LocomotiveCMS site. |
| - | }) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/generators/snippet.rb b/lib/locomotive/builder/generators/snippet.rb
+0
-54
| @@ | @@ -1,54 +0,0 @@ |
| - | require 'thor/group' |
| - | require 'active_support' |
| - | require 'active_support/core_ext' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module Generators |
| - | class Snippet < Thor::Group |
| - | |
| - | include Thor::Actions |
| - | |
| - | argument :slug |
| - | argument :target_path # path to the site |
| - | |
| - | attr_accessor :haml, :locales |
| - | |
| - | def ask_for_haml_and_locales |
| - | self.locales = [] |
| - | self.haml = yes?('Do you prefer a HAML template ?') |
| - | |
| - | if yes?('Is your snippet localized ?') |
| - | self.locales = ask('What are the locales other than the default one (comma separated) ?').split(',').map(&:strip) |
| - | end |
| - | end |
| - | |
| - | def create_snippet |
| - | extension = self.haml ? 'liquid.haml' : 'liquid' |
| - | |
| - | options = { slug: slug, translated: false } |
| - | file_path = File.join(pages_path, slug) |
| - | |
| - | template "template.#{extension}.tt", "#{file_path}.#{extension}", options |
| - | |
| - | self.locales.each do |locale| |
| - | options[:translated] = true |
| - | template "template.#{extension}.tt", "#{file_path}.#{locale}.#{extension}", options |
| - | end |
| - | end |
| - | |
| - | def self.source_root |
| - | File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'snippet') |
| - | end |
| - | |
| - | protected |
| - | |
| - | def pages_path |
| - | File.join(target_path, 'app', 'views', 'snippets') |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid.rb b/lib/locomotive/builder/liquid.rb
+0
-19
| @@ | @@ -1,19 +0,0 @@ |
| - | require "locomotive/mounter" |
| - | require 'liquid' |
| - | require 'locomotive/builder/liquid/drops/base' |
| - | |
| - | %w{. drops tags filters}.each do |dir| |
| - | Dir[File.join(File.dirname(__FILE__), 'liquid', dir, '*.rb')].each { |lib| require lib } |
| - | end |
| - | |
| - | |
| - | # add to_liquid methods to main models from the mounter |
| - | %w{site page content_entry}.each do |name| |
| - | klass = "Locomotive::Mounter::Models::#{name.classify}".constantize |
| - | |
| - | klass.class_eval <<-EOV |
| - | def to_liquid |
| - | ::Locomotive::Builder::Liquid::Drops::#{name.classify}.new(self) |
| - | end |
| - | EOV |
| - | end |
locomotive/builder/liquid/drops/base.rb b/lib/locomotive/builder/liquid/drops/base.rb
+0
-44
| @@ | @@ -1,44 +0,0 @@ |
| - | # Code taken from Mephisto sources (http://mephistoblog.com/) |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Drops |
| - | class Base < ::Liquid::Drop |
| - | |
| - | @@forbidden_attributes = %w{_id _version _index} |
| - | |
| - | attr_reader :_source |
| - | |
| - | def initialize(source) |
| - | @_source = source |
| - | end |
| - | |
| - | def id |
| - | (@_source.respond_to?(:id) ? @_source.id : nil) || 'new' |
| - | end |
| - | |
| - | # converts an array of records to an array of liquid drops |
| - | def self.liquify(*records, &block) |
| - | i = -1 |
| - | records = |
| - | records.inject [] do |all, r| |
| - | i+=1 |
| - | attrs = (block && block.arity == 1) ? [r] : [r, i] |
| - | all << (block ? block.call(*attrs) : r.to_liquid) |
| - | all |
| - | end |
| - | records.compact! |
| - | records |
| - | end |
| - | |
| - | protected |
| - | |
| - | def liquify(*records, &block) |
| - | self.class.liquify(*records, &block) |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/drops/content_entry.rb b/lib/locomotive/builder/liquid/drops/content_entry.rb
+0
-48
| @@ | @@ -1,48 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Drops |
| - | class ContentEntry < Base |
| - | |
| - | delegate :seo_title, :meta_keywords, :meta_description, :to => '_source' |
| - | |
| - | def _label |
| - | @_label ||= self._source._label |
| - | end |
| - | |
| - | def _permalink |
| - | @_source._permalink.try(:parameterize) |
| - | end |
| - | |
| - | alias :_slug :_permalink |
| - | |
| - | def next |
| - | self |
| - | end |
| - | |
| - | def previous |
| - | self |
| - | end |
| - | |
| - | def errors |
| - | (@_source.errors || []).inject({}) do |memo, name| |
| - | memo[name] = ::I18n.t('errors.messages.blank') |
| - | memo |
| - | end |
| - | end |
| - | |
| - | def before_method(meth) |
| - | return '' if self._source.nil? |
| - | |
| - | if not @@forbidden_attributes.include?(meth.to_s) |
| - | self._source.send(meth) |
| - | else |
| - | nil |
| - | end |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/drops/content_types.rb b/lib/locomotive/builder/liquid/drops/content_types.rb
+0
-121
| @@ | @@ -1,121 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Drops |
| - | class ContentTypes < ::Liquid::Drop |
| - | |
| - | def before_method(meth) |
| - | type = self.mounting_point.content_types[meth.to_s] |
| - | ProxyCollection.new(type) |
| - | end |
| - | |
| - | end |
| - | |
| - | class ProxyCollection < ::Liquid::Drop |
| - | |
| - | def initialize(content_type) |
| - | @content_type = content_type |
| - | @collection = nil |
| - | end |
| - | |
| - | def first |
| - | self.collection.first |
| - | end |
| - | |
| - | def last |
| - | self.collection.last |
| - | end |
| - | |
| - | def size |
| - | self.collection.size |
| - | end |
| - | |
| - | alias :length :size |
| - | |
| - | def each(&block) |
| - | self.collection.each(&block) |
| - | end |
| - | |
| - | def public_submission_url |
| - | "/entry_submissions/#{@content_type.slug}" |
| - | end |
| - | |
| - | def api |
| - | { 'create' => "/entry_submissions/#{@content_type.slug}" } |
| - | end |
| - | |
| - | def before_method(meth) |
| - | if (meth.to_s =~ /^group_by_(.+)$/) == 0 |
| - | self.group_entries_by(@content_type, $1) |
| - | elsif (meth.to_s =~ /^(.+)_options$/) == 0 |
| - | self.select_options_for(@content_type, $1) |
| - | else |
| - | @content_type.send(meth) |
| - | end |
| - | end |
| - | |
| - | protected |
| - | |
| - | def group_entries_by(content_type, name) |
| - | field = @content_type.find_field(name) |
| - | |
| - | return {} if field.nil? || !%w(belongs_to select).include?(field.type.to_s) |
| - | |
| - | (@content_type.entries || []).group_by do |entry| |
| - | entry.send(name.to_sym) |
| - | end.to_a.collect do |group| |
| - | { name: group.first, entries: group.last }.with_indifferent_access |
| - | end |
| - | end |
| - | |
| - | def select_options_for(content_type, name) |
| - | field = @content_type.find_field(name) |
| - | |
| - | return {} if field.nil? || field.type.to_s != 'select' |
| - | |
| - | field.select_options.map(&:name) |
| - | end |
| - | |
| - | def paginate(options = {}) |
| - | @collection ||= self.collection.paginate(options) |
| - | { |
| - | collection: @collection, |
| - | current_page: @collection.current_page, |
| - | previous_page: @collection.previous_page, |
| - | next_page: @collection.next_page, |
| - | total_entries: @collection.total_entries, |
| - | total_pages: @collection.total_pages, |
| - | per_page: @collection.per_page |
| - | } |
| - | end |
| - | |
| - | def collection |
| - | return unless @collection.blank? |
| - | |
| - | if @context['with_scope'].blank? |
| - | @collection = @content_type.entries |
| - | else |
| - | @collection = [] |
| - | |
| - | conditions = @context['with_scope'].clone.delete_if { |k, _| %w(order_by per_page page).include?(k) } |
| - | |
| - | @content_type.entries.each do |content| |
| - | accepted = (conditions.map do |key, value| |
| - | case value |
| - | when TrueClass, FalseClass, String then content.send(key) == value |
| - | else |
| - | true |
| - | end |
| - | end).all? # all conditions works ? |
| - | |
| - | @collection << content if accepted |
| - | end |
| - | end |
| - | |
| - | @collection |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/drops/page.rb b/lib/locomotive/builder/liquid/drops/page.rb
+0
-36
| @@ | @@ -1,36 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Drops |
| - | class Page < Base |
| - | |
| - | delegate :title, :slug, :fullpath, :parent, :depth, :seo_title, :redirect_url, :meta_description, :meta_keywords, :to => '_source' |
| - | |
| - | def children |
| - | _children = @_source.children || [] |
| - | _children = _children.sort { |a, b| a.position.to_i <=> b.position.to_i } |
| - | @children ||= liquify(*_children) |
| - | end |
| - | |
| - | def published? |
| - | @_source.published? |
| - | end |
| - | |
| - | def redirect? |
| - | self._source.redirect? |
| - | end |
| - | |
| - | def breadcrumbs |
| - | # TODO |
| - | '' |
| - | end |
| - | |
| - | def listed? |
| - | @_source.listed? |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/drops/site.rb b/lib/locomotive/builder/liquid/drops/site.rb
+0
-21
| @@ | @@ -1,21 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Drops |
| - | class Site < Base |
| - | |
| - | delegate :name, :seo_title, :meta_description, :meta_keywords, :to => '_source' |
| - | |
| - | def index |
| - | @index ||= self.mounting_point.pages['index'] |
| - | end |
| - | |
| - | def pages |
| - | @pages ||= liquify(*self._source.pages) |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/errors.rb b/lib/locomotive/builder/liquid/errors.rb
+0
-7
| @@ | @@ -1,7 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | class PageNotFound < ::Liquid::Error; end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/filters/date.rb b/lib/locomotive/builder/liquid/filters/date.rb
+0
-98
| @@ | @@ -1,98 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Filters |
| - | module Date |
| - | |
| - | def localized_date(input, *args) |
| - | return '' if input.blank? |
| - | |
| - | format, locale = args |
| - | |
| - | locale ||= I18n.locale |
| - | format ||= I18n.t('date.formats.default', :locale => locale) |
| - | |
| - | if input.is_a?(String) |
| - | begin |
| - | fragments = ::Date._strptime(input, format) |
| - | input = ::Date.new(fragments[:year], fragments[:mon], fragments[:mday]) |
| - | rescue |
| - | input = Time.parse(input) |
| - | end |
| - | end |
| - | |
| - | return input.to_s unless input.respond_to?(:strftime) |
| - | |
| - | I18n.l input, :format => format, :locale => locale |
| - | end |
| - | |
| - | alias :format_date :localized_date |
| - | |
| - | def distance_of_time_in_words(input, *args) |
| - | return '' if input.blank? |
| - | |
| - | from_time = input |
| - | to_time = args[0] || Time.now |
| - | |
| - | from_time = from_time.to_time if from_time.respond_to?(:to_time) |
| - | to_time = to_time.to_time if to_time.respond_to?(:to_time) |
| - | distance_in_minutes = (((to_time - from_time).abs)/60).round |
| - | distance_in_seconds = ((to_time - from_time).abs).round |
| - | |
| - | ::I18n.with_options({ :scope => :'datetime.distance_in_words' }) do |locale| |
| - | |
| - | case distance_in_minutes |
| - | when 0..1 |
| - | return distance_in_minutes == 0 ? |
| - | locale.t(:less_than_x_minutes, :count => 1) : |
| - | locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds |
| - | |
| - | case distance_in_seconds |
| - | when 0..4 then locale.t :less_than_x_seconds, :count => 5 |
| - | when 5..9 then locale.t :less_than_x_seconds, :count => 10 |
| - | when 10..19 then locale.t :less_than_x_seconds, :count => 20 |
| - | when 20..39 then locale.t :half_a_minute |
| - | when 40..59 then locale.t :less_than_x_minutes, :count => 1 |
| - | else locale.t :x_minutes, :count => 1 |
| - | end |
| - | |
| - | when 2..44 then locale.t :x_minutes, :count => distance_in_minutes |
| - | when 45..89 then locale.t :about_x_hours, :count => 1 |
| - | when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round |
| - | when 1440..2519 then locale.t :x_days, :count => 1 |
| - | when 2520..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round |
| - | when 43200..86399 then locale.t :about_x_months, :count => 1 |
| - | when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round |
| - | else |
| - | fyear = from_time.year |
| - | fyear += 1 if from_time.month >= 3 |
| - | tyear = to_time.year |
| - | tyear -= 1 if to_time.month < 3 |
| - | leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| ::Date.leap?(x)} |
| - | minute_offset_for_leap_year = leap_years * 1440 |
| - | # Discount the leap year days when calculating year distance. |
| - | # e.g. if there are 20 leap year days between 2 dates having the same day |
| - | # and month then the based on 365 days calculation |
| - | # the distance in years will come out to over 80 years when in written |
| - | # english it would read better as about 80 years. |
| - | minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year |
| - | remainder = (minutes_with_offset % 525600) |
| - | distance_in_years = (minutes_with_offset / 525600) |
| - | if remainder < 131400 |
| - | locale.t(:about_x_years, :count => distance_in_years) |
| - | elsif remainder < 394200 |
| - | locale.t(:over_x_years, :count => distance_in_years) |
| - | else |
| - | locale.t(:almost_x_years, :count => distance_in_years + 1) |
| - | end |
| - | end |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_filter(Date) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/filters/html.rb b/lib/locomotive/builder/liquid/filters/html.rb
+0
-154
| @@ | @@ -1,154 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Filters |
| - | module Html |
| - | |
| - | # Write the link to a stylesheet resource |
| - | # input: url of the css file |
| - | def stylesheet_tag(input, media = 'screen') |
| - | return '' if input.nil? |
| - | |
| - | input = "/stylesheets/#{input}" unless input =~ /^(\/|http:)/ |
| - | |
| - | input = "#{input}.css" unless input.ends_with?('.css') |
| - | |
| - | %{<link href="#{input}" media="#{media}" rel="stylesheet" type="text/css" />} |
| - | end |
| - | |
| - | # Write the link to javascript resource |
| - | # input: url of the javascript file |
| - | def javascript_tag(input) |
| - | return '' if input.nil? |
| - | |
| - | input = "/javascripts/#{input}" unless input =~ /^(\/|http:)/ |
| - | |
| - | input = "#{input}.js" unless input.ends_with?('.js') |
| - | |
| - | %{<script src="#{input}" type="text/javascript"></script>} |
| - | end |
| - | |
| - | # Write an image tag |
| - | # input: url of the image OR asset drop |
| - | def image_tag(input, *args) |
| - | image_options = inline_options(args_to_options(args)) |
| - | |
| - | input = "/images/#{input}" unless input =~ /^(\/|http:)/ |
| - | |
| - | "<img src=\"#{File.join('/', get_url_from_asset(input))}\" #{image_options}/>" |
| - | end |
| - | |
| - | # Write a theme image tag |
| - | # input: name of file including folder |
| - | # example: 'about/myphoto.jpg' | theme_image # <img src="images/about/myphoto.jpg" /> |
| - | def theme_image_tag(input, *args) |
| - | image_options = inline_options(args_to_options(args)) |
| - | "<img src=\"#{theme_image_url(input)}\" #{image_options}/>" |
| - | end |
| - | |
| - | def theme_image_url(input) |
| - | return '' if input.nil? |
| - | |
| - | input = "images/#{input}" unless input.starts_with?('/') |
| - | |
| - | File.join('/', input) |
| - | end |
| - | |
| - | def image_format(input, *args) |
| - | format = args_to_options(args).first |
| - | "#{input}.#{format}" |
| - | end |
| - | |
| - | # Embed a flash movie into a page |
| - | # input: url of the flash movie OR asset drop |
| - | # width: width (in pixel or in %) of the embedded movie |
| - | # height: height (in pixel or in %) of the embedded movie |
| - | def flash_tag(input, *args) |
| - | path = get_url_from_asset(input) |
| - | embed_options = inline_options(args_to_options(args)) |
| - | %{ |
| - | <object #{embed_options}> |
| - | <param name="movie" value="#{path}" /> |
| - | <embed src="#{path}" #{embed_options}/> |
| - | </embed> |
| - | </object> |
| - | }.gsub(/ >/, '>').strip |
| - | end |
| - | |
| - | # Render the navigation for a paginated collection |
| - | def default_pagination(paginate, *args) |
| - | return '' if paginate['parts'].empty? |
| - | |
| - | options = args_to_options(args) |
| - | |
| - | previous_label = options[:previous_label] || I18n.t('pagination.previous') |
| - | next_label = options[:next_label] || I18n.t('pagination.next') |
| - | |
| - | previous_link = (if paginate['previous'].blank? |
| - | "<span class=\"disabled prev_page\">#{previous_label}</span>" |
| - | else |
| - | "<a href=\"#{absolute_url(paginate['previous']['url'])}\" class=\"prev_page\">#{previous_label}</a>" |
| - | end) |
| - | |
| - | links = "" |
| - | paginate['parts'].each do |part| |
| - | links << (if part['is_link'] |
| - | "<a href=\"#{absolute_url(part['url'])}\">#{part['title']}</a>" |
| - | elsif part['hellip_break'] |
| - | "<span class=\"gap\">#{part['title']}</span>" |
| - | else |
| - | "<span class=\"current\">#{part['title']}</span>" |
| - | end) |
| - | end |
| - | |
| - | next_link = (if paginate['next'].blank? |
| - | "<span class=\"disabled next_page\">#{next_label}</span>" |
| - | else |
| - | "<a href=\"#{absolute_url(paginate['next']['url'])}\" class=\"next_page\">#{next_label}</a>" |
| - | end) |
| - | |
| - | %{<div class="pagination #{options[:css]}"> |
| - | #{previous_link} |
| - | #{links} |
| - | #{next_link} |
| - | </div>} |
| - | end |
| - | |
| - | protected |
| - | |
| - | # Convert an array of properties ('key:value') into a hash |
| - | # Ex: ['width:50', 'height:100'] => { :width => '50', :height => '100' } |
| - | def args_to_options(*args) |
| - | options = {} |
| - | args.flatten.each do |a| |
| - | if (a =~ /^(.*):(.*)$/) |
| - | options[$1.to_sym] = $2 |
| - | end |
| - | end |
| - | options |
| - | end |
| - | |
| - | # Write options (Hash) into a string according to the following pattern: |
| - | # <key1>="<value1>", <key2>="<value2", ...etc |
| - | def inline_options(options = {}) |
| - | return '' if options.empty? |
| - | (options.stringify_keys.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ') << ' ' |
| - | end |
| - | |
| - | # Get the path to be used in html tags such as image_tag, flash_tag, ...etc |
| - | # input: url (String) OR asset drop |
| - | def get_url_from_asset(input) |
| - | input.respond_to?(:url) ? input.url : input |
| - | end |
| - | |
| - | def absolute_url(url) |
| - | url =~ /^\// ? url : "/#{url}" |
| - | end |
| - | end |
| - | |
| - | ::Liquid::Template.register_filter(Html) |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/filters/misc.rb b/lib/locomotive/builder/liquid/filters/misc.rb
+0
-28
| @@ | @@ -1,28 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Filters |
| - | module Misc |
| - | |
| - | # was called modulo at first |
| - | def str_modulo(word, index, modulo) |
| - | (index.to_i + 1) % modulo == 0 ? word : '' |
| - | end |
| - | |
| - | # Get the nth element of the passed in array |
| - | def index(array, position) |
| - | array.at(position) if array.respond_to?(:at) |
| - | end |
| - | |
| - | def default(input, value) |
| - | input.blank? ? value : input |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_filter(Misc) |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/filters/resize.rb b/lib/locomotive/builder/liquid/filters/resize.rb
+0
-18
| @@ | @@ -1,18 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Filters |
| - | module Resize |
| - | |
| - | def resize(input, resize_string) |
| - | Locomotive::Builder::Dragonfly.instance.resize_url(input, resize_string) |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_filter(Resize) |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/filters/text.rb b/lib/locomotive/builder/liquid/filters/text.rb
+0
-50
| @@ | @@ -1,50 +0,0 @@ |
| - | require 'RedCloth' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Filters |
| - | module Text |
| - | |
| - | # right justify and padd a string |
| - | def rjust(input, integer, padstr = '') |
| - | input.to_s.rjust(integer, padstr) |
| - | end |
| - | |
| - | # left justify and padd a string |
| - | def ljust(input, integer, padstr = '') |
| - | input.to_s.ljust(integer, padstr) |
| - | end |
| - | |
| - | def underscore(input) |
| - | input.to_s.gsub(' ', '_').gsub('/', '_').underscore |
| - | end |
| - | |
| - | def dasherize(input) |
| - | input.to_s.gsub(' ', '-').gsub('/', '-').dasherize |
| - | end |
| - | |
| - | # alias newline_to_br |
| - | def multi_line(input) |
| - | input.to_s.gsub("\n", '<br/>') |
| - | end |
| - | |
| - | def concat(input, *args) |
| - | result = input.to_s |
| - | args.flatten.each { |a| result << a.to_s } |
| - | result |
| - | end |
| - | |
| - | |
| - | def textile(input) |
| - | ::RedCloth.new(input).to_html |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_filter(Text) |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/filters/translate.rb b/lib/locomotive/builder/liquid/filters/translate.rb
+0
-24
| @@ | @@ -1,24 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Filters |
| - | module Translate |
| - | |
| - | def translate(key, locale = nil) |
| - | translation = @context.registers[:mounting_point].translations[key.to_s] |
| - | |
| - | if translation |
| - | translation.get(locale) || translation.get(Locomotive::Mounter.locale) |
| - | else |
| - | "[unknown translation key: #{key}]" |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_filter(Translate) |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/patches.rb b/lib/locomotive/builder/liquid/patches.rb
+0
-47
| @@ | @@ -1,47 +0,0 @@ |
| - | module Liquid |
| - | |
| - | class Drop |
| - | |
| - | def mounting_point |
| - | @context.registers[:mounting_point] |
| - | end |
| - | |
| - | def site |
| - | @context.registers[:site] |
| - | end |
| - | |
| - | end |
| - | |
| - | class Template |
| - | |
| - | # creates a new <tt>Template</tt> object from liquid source code |
| - | def parse_with_utf8(source, context = {}) |
| - | if RUBY_VERSION =~ /1\.9/ |
| - | source = source.force_encoding('UTF-8') if source.present? |
| - | end |
| - | self.parse_without_utf8(source, context) |
| - | end |
| - | |
| - | alias_method_chain :parse, :utf8 |
| - | |
| - | end |
| - | |
| - | module StandardFilters |
| - | |
| - | private |
| - | |
| - | def to_number(obj) |
| - | case obj |
| - | when Numeric |
| - | obj |
| - | when String |
| - | (obj.strip =~ /^\d+\.\d+$/) ? obj.to_f : obj.to_i |
| - | when DateTime, Date, Time |
| - | obj.to_time.to_i |
| - | else |
| - | 0 |
| - | end |
| - | end |
| - | end |
| - | |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/consume.rb b/lib/locomotive/builder/liquid/tags/consume.rb
+0
-58
| @@ | @@ -1,58 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | |
| - | # Consume web services as easy as pie directly in liquid ! |
| - | # |
| - | # Usage: |
| - | # |
| - | # {% consume blog from 'http://nocoffee.tumblr.com/api/read.json?num=3' username: 'john', password: 'easy', format: 'json', expires_in: 3000 %} |
| - | # {% for post in blog.posts %} |
| - | # {{ post.title }} |
| - | # {% endfor %} |
| - | # {% endconsume %} |
| - | # |
| - | class Consume < ::Liquid::Block |
| - | |
| - | Syntax = /(#{::Liquid::VariableSignature}+)\s*from\s*(#{::Liquid::QuotedString}+)/ |
| - | |
| - | def initialize(tag_name, markup, tokens, context) |
| - | if markup =~ Syntax |
| - | @target = $1 |
| - | @url = $2.gsub(/['"]/, '') |
| - | @options = {} |
| - | markup.scan(::Liquid::TagAttributes) do |key, value| |
| - | @options[key] = value if key != 'http' |
| - | end |
| - | @options.delete('expires_in') |
| - | else |
| - | raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]") |
| - | end |
| - | |
| - | super |
| - | end |
| - | |
| - | def render(context) |
| - | context.stack do |
| - | _response = nil |
| - | |
| - | begin |
| - | _response = Locomotive::Builder::Httparty::Webservice.consume(@url, @options.symbolize_keys) |
| - | rescue Exception => e |
| - | _response = { 'error' => e.message.to_s }.to_liquid |
| - | end |
| - | |
| - | context.scopes.last[@target.to_s] = _response |
| - | |
| - | render_all(@nodelist, context) |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('consume', Consume) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/csrf.rb b/lib/locomotive/builder/liquid/tags/csrf.rb
+0
-34
| @@ | @@ -1,34 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | module Csrf |
| - | |
| - | class Param < ::Liquid::Tag |
| - | |
| - | def render(context) |
| - | %{<input type="hidden" name="authenticity_token" value="helloworld" />} |
| - | end |
| - | |
| - | end |
| - | |
| - | class Meta < ::Liquid::Tag |
| - | |
| - | def render(context) |
| - | %{ |
| - | <meta name="csrf-param" content="authenticity_token" /> |
| - | <meta name="csrf-token" content="helloworld" /> |
| - | } |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('csrf_param', Csrf::Param) |
| - | ::Liquid::Template.register_tag('csrf_meta', Csrf::Meta) |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/editable.rb b/lib/locomotive/builder/liquid/tags/editable.rb
+0
-5
| @@ | @@ -1,5 +0,0 @@ |
| - | require 'locomotive/builder/liquid/tags/editable/base' |
| - | require 'locomotive/builder/liquid/tags/editable/short_text' |
| - | require 'locomotive/builder/liquid/tags/editable/long_text' |
| - | require 'locomotive/builder/liquid/tags/editable/file' |
| - | require 'locomotive/builder/liquid/tags/editable/control' |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/editable/base.rb b/lib/locomotive/builder/liquid/tags/editable/base.rb
+0
-46
| @@ | @@ -1,46 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | module Editable |
| - | class Base < ::Liquid::Block |
| - | |
| - | Syntax = /(#{::Liquid::QuotedFragment})(\s*,\s*#{::Liquid::Expression}+)?/ |
| - | |
| - | def initialize(tag_name, markup, tokens, context) |
| - | if markup =~ Syntax |
| - | @slug = $1.gsub(/[\"\']/, '') |
| - | @options = {} |
| - | markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/^'/, '').gsub(/'$/, '') } |
| - | else |
| - | raise ::Liquid::SyntaxError.new("Syntax Error in 'editable_xxx' - Valid syntax: editable_xxx <slug>(, <options>)") |
| - | end |
| - | |
| - | super |
| - | end |
| - | |
| - | def render(context) |
| - | current_page = context.registers[:page] |
| - | |
| - | element = current_page.find_editable_element(context['block'].try(:name), @slug) |
| - | |
| - | if element.present? |
| - | render_element(context, element) |
| - | else |
| - | super |
| - | end |
| - | end |
| - | |
| - | protected |
| - | |
| - | def render_element(context, element) |
| - | element.content |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/editable/control.rb b/lib/locomotive/builder/liquid/tags/editable/control.rb
+0
-19
| @@ | @@ -1,19 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | module Editable |
| - | class Control < Base |
| - | |
| - | def render(context) |
| - | super |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('editable_control', Control) |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/editable/file.rb b/lib/locomotive/builder/liquid/tags/editable/file.rb
+0
-15
| @@ | @@ -1,15 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | module Editable |
| - | class File < Base |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('editable_file', File) |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/editable/long_text.rb b/lib/locomotive/builder/liquid/tags/editable/long_text.rb
+0
-15
| @@ | @@ -1,15 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | module Editable |
| - | class LongText < ShortText |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('editable_long_text', LongText) |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/editable/short_text.rb b/lib/locomotive/builder/liquid/tags/editable/short_text.rb
+0
-15
| @@ | @@ -1,15 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | module Editable |
| - | class ShortText < Base |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('editable_short_text', ShortText) |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/extends.rb b/lib/locomotive/builder/liquid/tags/extends.rb
+0
-25
| @@ | @@ -1,25 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | class Extends < ::Liquid::Extends |
| - | |
| - | def parse_parent_template |
| - | mounting_point = @context[:mounting_point] |
| - | |
| - | page = if @template_name == 'parent' |
| - | @context[:page].parent |
| - | else |
| - | mounting_point.pages[@template_name] |
| - | end |
| - | |
| - | ::Liquid::Template.parse(page.source, { mounting_point: mounting_point, page: page }) |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('extends', Extends) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/google_analytics.rb b/lib/locomotive/builder/liquid/tags/google_analytics.rb
+0
-28
| @@ | @@ -1,28 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | class GoogleAnalytics < ::Liquid::Tag |
| - | |
| - | Syntax = /(#{::Liquid::Expression}+)?/ |
| - | |
| - | def initialize(tag_name, markup, tokens, context) |
| - | if markup =~ Syntax |
| - | @account_id = $1.gsub('\'', '') |
| - | else |
| - | raise ::Liquid::SyntaxError.new("Syntax Error in 'google_analytics' - Valid syntax: google_analytics <account_id>") |
| - | end |
| - | |
| - | super |
| - | end |
| - | |
| - | def render(context) |
| - | "<!-- google analytics for #{@account_id} -->" |
| - | end |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('google_analytics', GoogleAnalytics) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/inline_editor.rb b/lib/locomotive/builder/liquid/tags/inline_editor.rb
+0
-16
| @@ | @@ -1,16 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | class InlineEditor < ::Liquid::Tag |
| - | |
| - | def render(context) |
| - | '' |
| - | end |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('inline_editor', InlineEditor) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/locale_switcher.rb b/lib/locomotive/builder/liquid/tags/locale_switcher.rb
+0
-180
| @@ | @@ -1,180 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | # Display the links to change the locale of the current page |
| - | # |
| - | # Usage: |
| - | # |
| - | # {% locale_switcher %} => <div id="locale-switcher"><a href="/features" class="current en">Features</a><a href="/fr/fonctionnalites" class="fr">Fonctionnalités</a></div> |
| - | # |
| - | # {% locale_switcher label: locale, sep: ' - ' } |
| - | # |
| - | # options: |
| - | # - label: iso (de, fr, en, ...etc), locale (Deutsch, Français, English, ...etc), title (page title) |
| - | # - sep: piece of html code separating 2 locales |
| - | # |
| - | # notes: |
| - | # - "iso" is the default choice for label |
| - | # - " | " is the default separating code |
| - | # |
| - | class LocaleSwitcher < ::Liquid::Tag |
| - | |
| - | Syntax = /(#{::Liquid::Expression}+)?/ |
| - | |
| - | def initialize(tag_name, markup, tokens, context) |
| - | @options = { label: 'iso', sep: ' | ' } |
| - | |
| - | if markup =~ Syntax |
| - | markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') } |
| - | |
| - | @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude] |
| - | else |
| - | raise ::Liquid::SyntaxError.new("Syntax Error in 'locale_switcher' - Valid syntax: locale_switcher <options>") |
| - | end |
| - | |
| - | super |
| - | end |
| - | |
| - | def render(context) |
| - | @site, @page = context.registers[:site], context.registers[:page] |
| - | @default_locale = context.registers[:mounting_point].default_locale |
| - | |
| - | output = %(<div id="locale-switcher">) |
| - | |
| - | output += @site.locales.collect do |locale| |
| - | Locomotive::Mounter.with_locale(locale) do |
| - | fullpath = localized_fullpath(locale) |
| - | |
| - | if @page.templatized? |
| - | permalink = context['entry']._permalink |
| - | |
| - | if permalink |
| - | fullpath.gsub!('*', permalink) |
| - | else |
| - | fullpath = '404' |
| - | end |
| - | end |
| - | |
| - | css = link_class(locale, context['locale']) |
| - | |
| - | %(<a href="/#{fullpath}" class="#{css}">#{link_label(locale)}</a>) |
| - | end |
| - | end.join(@options[:sep]) |
| - | |
| - | output += %(</div>) |
| - | end |
| - | |
| - | private |
| - | |
| - | def link_class(locale, current_locale) |
| - | css = [locale] |
| - | css << 'current' if locale.to_s == current_locale.to_s |
| - | css.join(' ') |
| - | end |
| - | |
| - | def link_label(locale) |
| - | case @options[:label] |
| - | when 'iso' then locale |
| - | when 'locale' then I18n.t("locomotive.locales.#{locale}", locale: locale) |
| - | when 'title' then @page.title # FIXME: this returns nil if the page has not been translated in the locale |
| - | else |
| - | locale |
| - | end |
| - | end |
| - | |
| - | def localized_fullpath(locale) |
| - | # @site.localized_page_fullpath(@page, locale) |
| - | |
| - | return nil if @page.fullpath_translations.blank? |
| - | |
| - | fullpath = @page.safe_fullpath || @page.fullpath_or_default |
| - | |
| - | if locale.to_s == @default_locale.to_s # no need to specify the locale |
| - | @page.index? ? '' : fullpath |
| - | elsif @page.index? # avoid /en/index or /fr/index, prefer /en or /fr instead |
| - | locale |
| - | else |
| - | File.join(locale, fullpath) |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('locale_switcher', LocaleSwitcher) |
| - | end |
| - | end |
| - | end |
| - | end |
| - | # module Locomotive |
| - | # module Builder |
| - | # module Liquid |
| - | # module Tags |
| - | |
| - | # # Display the links to change the locale of the current page |
| - | # # |
| - | # # Usage: |
| - | # # |
| - | # # {% locale_switcher %} => <div id="locale-switcher"><a href="/features" class="current en">Features</a><a href="/fr/fonctionnalites" class="fr">Fonctionnalités</a></div> |
| - | # # |
| - | # # {% locale_switcher label: locale, sep: ' - ' } |
| - | # # |
| - | # # options: |
| - | # # - label: iso (de, fr, en, ...etc), locale (Deutsch, Français, English, ...etc), title (page title) |
| - | # # - sep: piece of html code separating 2 locales |
| - | # # |
| - | # # notes: |
| - | # # - "iso" is the default choice for label |
| - | # # - " | " is the default separating code |
| - | # # |
| - | # class LocaleSwitcher < ::Liquid::Tag |
| - | |
| - | # Syntax = /(#{::Liquid::Expression}+)?/ |
| - | |
| - | # def initialize(tag_name, markup, tokens, context) |
| - | # @options = { label: 'iso', sep: ' | ' } |
| - | |
| - | # if markup =~ Syntax |
| - | # markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') } |
| - | |
| - | # @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude] |
| - | # else |
| - | # raise ::Liquid::SyntaxError.new("Syntax Error in 'locale_switcher' - Valid syntax: locale_switcher <options>") |
| - | # end |
| - | |
| - | # super |
| - | # end |
| - | |
| - | # def render(context) |
| - | # @site, @page = context.registers[:site], context.registers[:page] |
| - | |
| - | # output = %(<div id="locale-switcher">) |
| - | |
| - | # output += @site.locales.collect do |locale| |
| - | # fullpath = locale.to_s == context['default_locale'].to_s ? '/' : locale |
| - | |
| - | # %(<a href="/#{fullpath}" class="#{locale} #{'current' if locale.to_s == context['default_locale'].to_s}">#{link_label(locale)}</a>) |
| - | # end.join(@options[:sep]) |
| - | |
| - | # output += %(</div>) |
| - | # end |
| - | |
| - | # private |
| - | |
| - | # def link_label(locale) |
| - | # case @options[:label] |
| - | # when :iso then locale |
| - | # when :locale then I18n.t("locomotive.locales.#{locale}", locale: locale) |
| - | # when :title then @page.title # FIXME: this returns nil if the page has not been translated in the locale |
| - | # else |
| - | # locale |
| - | # end |
| - | # end |
| - | |
| - | # end |
| - | |
| - | # ::Liquid::Template.register_tag('locale_switcher', LocaleSwitcher) |
| - | # end |
| - | # end |
| - | # end |
| - | # end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/nav.rb b/lib/locomotive/builder/liquid/tags/nav.rb
+0
-167
| @@ | @@ -1,167 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | # Display the children pages of the site, current page or the parent page. If not precised, nav is applied on the current page. |
| - | # The html output is based on the ul/li tags. |
| - | # |
| - | # Usage: |
| - | # |
| - | # {% nav site %} => <ul class="nav"><li class="on"><a href="/features">Features</a></li></ul> |
| - | # |
| - | # {% nav site, no_wrapper: true, exclude: 'contact|about', id: 'main-nav', class: 'nav', active_class: 'on' } |
| - | # |
| - | class Nav < ::Liquid::Tag |
| - | |
| - | Syntax = /(#{::Liquid::Expression}+)?/ |
| - | |
| - | attr_accessor :current_page, :mounting_point |
| - | |
| - | def initialize(tag_name, markup, tokens, context) |
| - | if markup =~ Syntax |
| - | @source = ($1 || 'page').gsub(/"|'/, '') |
| - | @options = { :id => 'nav', :class => '', :active_class => 'on', :bootstrap => false } |
| - | markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') } |
| - | |
| - | @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude] |
| - | |
| - | if @options[:snippet] |
| - | if template = self.parse_snippet_template(context, @options[:snippet]) |
| - | @options[:liquid_render] = template |
| - | end |
| - | end |
| - | else |
| - | raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <site|parent|page|<path to a page>> <options>") |
| - | end |
| - | |
| - | super |
| - | end |
| - | |
| - | def render(context) |
| - | self.set_accessors_from_context(context) |
| - | |
| - | children_output = [] |
| - | |
| - | entries = self.fetch_entries |
| - | |
| - | entries.each_with_index do |p, index| |
| - | css = [] |
| - | css << 'first' if index == 0 |
| - | css << 'last' if index == entries.size - 1 |
| - | |
| - | children_output << render_entry_link(p, css.join(' '), 1) |
| - | end |
| - | |
| - | output = children_output.join("\n") |
| - | |
| - | if @options[:no_wrapper] != 'true' |
| - | output = %{<ul id="#{@options[:id]}" class="#{@options[:class]}">\n#{output}</ul>} |
| - | end |
| - | |
| - | output |
| - | end |
| - | |
| - | protected |
| - | |
| - | def set_accessors_from_context(context) |
| - | self.current_page = context.registers[:page] |
| - | self.mounting_point = context.registers[:mounting_point] |
| - | end |
| - | |
| - | def parse_snippet_template(context, template_name) |
| - | source = if template_name.include?('{') |
| - | template_name |
| - | else |
| - | context[:mounting_point].snippets[template_name].try(:source) |
| - | end |
| - | |
| - | source ? ::Liquid::Template.parse(source) : nil |
| - | end |
| - | |
| - | def fetch_entries |
| - | children = (case @source |
| - | when 'site' then self.mounting_point.pages['index'] |
| - | when 'parent' then self.current_page.parent || self.current_page |
| - | when 'page' then self.current_page |
| - | else |
| - | self.mounting_point.pages[@source] |
| - | end).children.clone |
| - | |
| - | children.delete_if { |p| !include_page?(p) } |
| - | end |
| - | |
| - | # Determines whether or not a page should be a part of the menu |
| - | def include_page?(page) |
| - | if !page.listed? || page.templatized? || !page.published? |
| - | false |
| - | elsif @options[:exclude] |
| - | (page.fullpath =~ @options[:exclude]).nil? |
| - | else |
| - | true |
| - | end |
| - | end |
| - | |
| - | # Returns a list element, a link to the page and its children |
| - | def render_entry_link(page, css, depth) |
| - | selected = self.current_page.fullpath =~ /^#{page.fullpath}/ ? " #{@options[:active_class]}" : '' |
| - | |
| - | icon = @options[:icon] ? '<span></span>' : '' |
| - | |
| - | title = @options[:liquid_render] ? @options[:liquid_render].render('page' => page) : page.title |
| - | |
| - | label = %{#{icon if @options[:icon] != 'after' }#{title}#{icon if @options[:icon] == 'after' }} |
| - | |
| - | dropdow = "" |
| - | link_options = "" |
| - | href = ::I18n.locale.to_s == self.mounting_point.default_locale.to_s ? "/#{page.fullpath}" : "/#{::I18n.locale}/#{page.fullpath}" |
| - | caret = "" |
| - | |
| - | if render_children_for_page?(page, depth) && bootstrap? |
| - | dropdow = "dropdown" |
| - | link_options = %{class="dropdown-toggle" data-toggle="dropdown"} |
| - | href = "#" |
| - | caret = %{<b class="caret"></b>} |
| - | end |
| - | |
| - | output = %{<li id="#{page.slug.to_s.dasherize}-link" class="link#{selected} #{css} #{dropdow}">} |
| - | output << %{<a href="#{href}" #{link_options}>#{label} #{caret}</a>} |
| - | output << render_entry_children(page, depth.succ) if (depth.succ <= @options[:depth].to_i) |
| - | output << %{</li>} |
| - | |
| - | output.strip |
| - | end |
| - | |
| - | def render_children_for_page?(page, depth) |
| - | depth.succ <= @options[:depth].to_i && page.children.reject { |c| !include_page?(c) }.any? |
| - | end |
| - | |
| - | # Recursively creates a nested unordered list for the depth specified |
| - | def render_entry_children(page, depth) |
| - | output = %{} |
| - | |
| - | children = page.children.reject { |c| !include_page?(c) } |
| - | if children.present? |
| - | output = %{<ul id="#{@options[:id]}-#{page.slug.to_s.dasherize}" class="#{bootstrap? ? "dropdown-menu" : ""}">} |
| - | children.each do |c, page| |
| - | css = [] |
| - | css << 'first' if children.first == c |
| - | css << 'last' if children.last == c |
| - | |
| - | output << render_entry_link(c, css.join(' '),depth) |
| - | end |
| - | output << %{</ul>} |
| - | end |
| - | |
| - | output |
| - | end |
| - | |
| - | def bootstrap? |
| - | @options[:bootstrap] == 'true' || @options[:bootstrap] == true |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('nav', Nav) |
| - | end |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/paginate.rb b/lib/locomotive/builder/liquid/tags/paginate.rb
+0
-105
| @@ | @@ -1,105 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | |
| - | # Paginate a collection |
| - | # |
| - | # Usage: |
| - | # |
| - | # {% paginate contents.projects by 5 %} |
| - | # {% for project in paginate.collection %} |
| - | # {{ project.name }} |
| - | # {% endfor %} |
| - | # {% endpaginate %} |
| - | # |
| - | |
| - | class Paginate < ::Liquid::Block |
| - | |
| - | Syntax = /(#{::Liquid::Expression}+)\s+by\s+([0-9]+)/ |
| - | |
| - | def initialize(tag_name, markup, tokens, context) |
| - | if markup =~ Syntax |
| - | @collection_name = $1 |
| - | @per_page = $2.to_i |
| - | else |
| - | raise ::Liquid::SyntaxError.new("Syntax Error in 'paginate' - Valid syntax: paginate <collection> by <number>") |
| - | end |
| - | |
| - | super |
| - | end |
| - | |
| - | def render(context) |
| - | context.stack do |
| - | collection = context[@collection_name] |
| - | |
| - | raise ::Liquid::ArgumentError.new("Cannot paginate array '#{@collection_name}'. Not found.") if collection.nil? |
| - | |
| - | pagination = collection.send(:paginate, { |
| - | :page => context['current_page'], |
| - | :per_page => @per_page }).stringify_keys! |
| - | |
| - | page_count, current_page = pagination['total_pages'], pagination['current_page'] |
| - | |
| - | path = sanitize_path(context['fullpath']) |
| - | |
| - | pagination['previous'] = link(I18n.t('pagination.previous'), current_page - 1, path) if pagination['previous_page'] |
| - | pagination['next'] = link(I18n.t('pagination.next'), current_page + 1, path) if pagination['next_page'] |
| - | pagination['parts'] = [] |
| - | |
| - | hellip_break = false |
| - | |
| - | if page_count > 1 |
| - | 1.upto(page_count) do |page| |
| - | if current_page == page |
| - | pagination['parts'] << no_link(page) |
| - | elsif page == 1 |
| - | pagination['parts'] << link(page, page, path) |
| - | elsif page == page_count - 1 |
| - | pagination['parts'] << link(page, page, path) |
| - | elsif page <= current_page - window_size or page >= current_page + window_size |
| - | next if hellip_break |
| - | pagination['parts'] << no_link('…') |
| - | hellip_break = true |
| - | next |
| - | else |
| - | pagination['parts'] << link(page, page, path) |
| - | end |
| - | |
| - | hellip_break = false |
| - | end |
| - | end |
| - | |
| - | context['paginate'] = pagination |
| - | |
| - | render_all(@nodelist, context) |
| - | end |
| - | end |
| - | |
| - | private |
| - | |
| - | def sanitize_path(path) |
| - | _path = path.gsub(/page=[0-9]+&?/, '').gsub(/_pjax=true&?/, '') |
| - | _path = _path.slice(0..-2) if _path.last == '?' || _path.last == '&' |
| - | _path |
| - | end |
| - | |
| - | def window_size |
| - | 3 |
| - | end |
| - | |
| - | def no_link(title) |
| - | { 'title' => title, 'is_link' => false, 'hellip_break' => title == '…' } |
| - | end |
| - | |
| - | def link(title, page, path) |
| - | _path = %(#{path}#{path.include?('?') ? '&' : '?'}page=#{page}) |
| - | { 'title' => title, 'url' => _path, 'is_link' => true } |
| - | end |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('paginate', Paginate) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/seo.rb b/lib/locomotive/builder/liquid/tags/seo.rb
+0
-74
| @@ | @@ -1,74 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | module SEO |
| - | |
| - | class Base < ::Liquid::Tag |
| - | |
| - | def render(context) |
| - | %{ |
| - | #{self.render_title(context)} |
| - | #{self.render_metadata(context)} |
| - | } |
| - | end |
| - | |
| - | protected |
| - | |
| - | def render_title(context) |
| - | title = self.value_for(:seo_title, context) |
| - | title = context.registers[:site].name if title.blank? |
| - | |
| - | %{ |
| - | <title>#{title}</title> |
| - | } |
| - | end |
| - | |
| - | def render_metadata(context) |
| - | %{ |
| - | <meta name="description" content="#{self.value_for(:meta_description, context)}" /> |
| - | <meta name="keywords" content="#{self.value_for(:meta_keywords, context)}" /> |
| - | } |
| - | end |
| - | |
| - | # Removes whitespace and quote characters from the input |
| - | def sanitized_string(string) |
| - | string ? string.strip.gsub(/"/, '') : '' |
| - | end |
| - | |
| - | def value_for(attribute, context) |
| - | object = self.metadata_object(context) |
| - | value = object.try(attribute.to_sym).blank? ? context.registers[:site].send(attribute.to_sym) : object.send(attribute.to_sym) |
| - | self.sanitized_string(value) |
| - | end |
| - | |
| - | def metadata_object(context) |
| - | context['content_instance'] || context['page'] |
| - | end |
| - | end |
| - | |
| - | class Title < Base |
| - | |
| - | def render(context) |
| - | self.render_title(context) |
| - | end |
| - | |
| - | end |
| - | |
| - | class Metadata < Base |
| - | |
| - | def render(context) |
| - | self.render_metadata(context) |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('seo', SEO::Base) |
| - | ::Liquid::Template.register_tag('seo_title', SEO::Title) |
| - | ::Liquid::Template.register_tag('seo_metadata', SEO::Metadata) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/snippet.rb b/lib/locomotive/builder/liquid/tags/snippet.rb
+0
-44
| @@ | @@ -1,44 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | |
| - | class Snippet < ::Liquid::Include |
| - | |
| - | def render(context) |
| - | name = @template_name.gsub(/[\"\']/, '') |
| - | |
| - | source = context.registers[:mounting_point].snippets[name].try(:source) |
| - | |
| - | Locomotive::Builder::Logger.info " Rendered snippet #{name}" |
| - | |
| - | partial = ::Liquid::Template.parse(source) |
| - | |
| - | variable = context[@variable_name || @template_name[1..-2]] |
| - | |
| - | context.stack do |
| - | @attributes.each do |key, value| |
| - | context[key] = context[value] |
| - | end |
| - | |
| - | output = (if variable.is_a?(Array) |
| - | variable.collect do |variable| |
| - | context[@template_name[1..-2]] = variable |
| - | partial.render(context) |
| - | end |
| - | else |
| - | context[@template_name[1..-2]] = variable |
| - | partial.render(context) |
| - | end) |
| - | |
| - | output |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('include', Snippet) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/liquid/tags/with_scope.rb b/lib/locomotive/builder/liquid/tags/with_scope.rb
+0
-43
| @@ | @@ -1,43 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | module Liquid |
| - | module Tags |
| - | class WithScope < ::Liquid::Block |
| - | |
| - | def initialize(tag_name, markup, tokens, context) |
| - | @options = {} |
| - | |
| - | markup.scan(::Liquid::TagAttributes) do |key, value| |
| - | @options[key] = value |
| - | end |
| - | |
| - | super |
| - | end |
| - | |
| - | def render(context) |
| - | context.stack do |
| - | context['with_scope'] = decode(@options, context) |
| - | render_all(@nodelist, context) |
| - | end |
| - | end |
| - | |
| - | private |
| - | |
| - | def decode(attributes, context) |
| - | attributes.each_pair do |key, value| |
| - | attributes[key] = (case value |
| - | when /^true|false$/i then value == 'true' |
| - | when /^[0-9]+$/ then value.to_i |
| - | when /^["|'](.+)["|']$/ then $1.gsub(/^["|']/, '').gsub(/["|']$/, '') |
| - | else |
| - | context[value] || value |
| - | end) |
| - | end |
| - | end |
| - | end |
| - | |
| - | ::Liquid::Template.register_tag('with_scope', WithScope) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/listen.rb b/lib/locomotive/builder/listen.rb
+0
-57
| @@ | @@ -1,57 +0,0 @@ |
| - | require 'listen' |
| - | |
| - | module Locomotive::Builder |
| - | class Listen |
| - | |
| - | attr_accessor :reader |
| - | |
| - | def self.instance |
| - | @@instance = new |
| - | end |
| - | |
| - | def start(reader) |
| - | self.reader = reader |
| - | |
| - | self.definitions.each do |definition| |
| - | self.apply(definition) |
| - | end |
| - | end |
| - | |
| - | def definitions |
| - | [ |
| - | ['config', /\.yml/, [:site, :content_types, :pages, :snippets, :content_entries, :translations]], |
| - | ['app/views', /\.liquid/, [:pages, :snippets]], |
| - | ['app/content_types', /\.yml/, [:content_types, :content_entries]], |
| - | ['data', /\.yml/, :content_entries] |
| - | ] |
| - | end |
| - | |
| - | protected |
| - | |
| - | def apply(definition) |
| - | reloader = Proc.new do |modified, added, removed| |
| - | resources = [*definition.last] |
| - | names = resources.map { |n| "\"#{n}\"" }.join(', ') |
| - | |
| - | Locomotive::Builder::Logger.info "* Reloaded #{names} at #{Time.now}" |
| - | |
| - | begin |
| - | reader.reload(resources) |
| - | rescue Exception => e |
| - | Locomotive::Builder::MounterException.new('Unable to reload', e) |
| - | end |
| - | end |
| - | |
| - | filter = definition[1] |
| - | path = File.join(self.reader.mounting_point.path, definition.first) |
| - | path = File.expand_path(path) |
| - | |
| - | listener = ::Listen.to(path).filter(filter).change(&reloader) |
| - | |
| - | # non blocking listener |
| - | listener.start(false) |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| \ No newline at end of file | |
locomotive/builder/logger.rb b/lib/locomotive/builder/logger.rb
+0
-54
| @@ | @@ -1,54 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | |
| - | class Logger |
| - | |
| - | attr_accessor :logger, :logfile_path, :stdout |
| - | |
| - | def initialize |
| - | self.logger = nil |
| - | end |
| - | |
| - | # Setup the single instance of the ruby logger. |
| - | # |
| - | # @param [ String ] path The path to the log file (default: log/builder.log) |
| - | # @param [ Boolean ] stdout Instead of having a file, log to the standard output |
| - | # |
| - | def setup(path, stdout = false) |
| - | require 'logger' |
| - | |
| - | self.stdout = stdout |
| - | |
| - | self.logfile_path = File.expand_path(File.join(path, 'log', 'builder.log')) |
| - | FileUtils.mkdir_p(File.dirname(logfile_path)) |
| - | |
| - | out = self.stdout ? STDOUT : self.logfile_path |
| - | |
| - | self.logger = ::Logger.new(out).tap do |log| |
| - | log.level = ::Logger::DEBUG |
| - | log.formatter = proc do |severity, datetime, progname, msg| |
| - | "#{msg}\n" |
| - | end |
| - | end |
| - | end |
| - | |
| - | def self.instance |
| - | @@instance ||= self.new |
| - | end |
| - | |
| - | def self.setup(path, stdout = false) |
| - | self.instance.setup(path, stdout) |
| - | end |
| - | |
| - | class << self |
| - | %w(debug info warn error fatal unknown).each do |name| |
| - | define_method(name) do |message| |
| - | self.instance.logger.send(name.to_sym, message) |
| - | end |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/misc.rb b/lib/locomotive/builder/misc.rb
+0
-5
| @@ | @@ -1,5 +0,0 @@ |
| - | require 'locomotive/builder/misc/core_ext.rb' |
| - | require 'locomotive/builder/misc/will_paginate.rb' |
| - | require 'locomotive/builder/misc/httparty.rb' |
| - | require 'locomotive/builder/misc/dragonfly.rb' |
| - | require 'locomotive/builder/misc/i18n.rb' |
| \ No newline at end of file | |
locomotive/builder/misc/core_ext.rb b/lib/locomotive/builder/misc/core_ext.rb
+0
-29
| @@ | @@ -1,29 +0,0 @@ |
| - | unless Hash.instance_methods.include?(:underscore_keys) |
| - | class Hash |
| - | |
| - | def underscore_keys |
| - | new_hash = {} |
| - | |
| - | self.each_pair do |key, value| |
| - | if value.respond_to?(:collect!) # Array |
| - | value.collect do |item| |
| - | if item.respond_to?(:each_pair) # Hash item within |
| - | item.underscore_keys |
| - | else |
| - | item |
| - | end |
| - | end |
| - | elsif value.respond_to?(:each_pair) # Hash |
| - | value = value.underscore_keys |
| - | end |
| - | |
| - | new_key = key.is_a?(String) ? key.underscore : key # only String keys |
| - | |
| - | new_hash[new_key] = value |
| - | end |
| - | |
| - | self.replace(new_hash) |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/misc/dragonfly.rb b/lib/locomotive/builder/misc/dragonfly.rb
+0
-79
| @@ | @@ -1,79 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | class Dragonfly |
| - | |
| - | attr_accessor :path, :enabled |
| - | |
| - | def enabled? |
| - | !!self.enabled |
| - | end |
| - | |
| - | def resize_url(source, resize_string) |
| - | _source = (case source |
| - | when String then source |
| - | when Hash then source['url'] || source[:url] |
| - | else |
| - | source.try(:url) |
| - | end) |
| - | |
| - | if _source.blank? |
| - | LocomotiveEditor::Logger.error "Unable to resize on the fly: #{source.inspect}" |
| - | return |
| - | end |
| - | |
| - | return _source unless self.enabled? |
| - | |
| - | if _source =~ /^http/ |
| - | file = self.class.app.fetch_url(_source) |
| - | else |
| - | file = self.class.app.fetch_file(File.join(self.path, 'public', _source)) |
| - | end |
| - | |
| - | file.process(:thumb, resize_string).url |
| - | end |
| - | |
| - | def self.app |
| - | ::Dragonfly[:images] |
| - | end |
| - | |
| - | |
| - | def self.instance |
| - | @@instance ||= new |
| - | end |
| - | |
| - | def self.setup!(path) |
| - | self.instance.path = path |
| - | self.instance.enabled = false |
| - | |
| - | begin |
| - | require 'rack/cache' |
| - | require 'RMagick' |
| - | require 'dragonfly' |
| - | |
| - | ## initialize Dragonfly ## |
| - | app = ::Dragonfly[:images].configure_with(:imagemagick) |
| - | |
| - | ## configure it ## |
| - | ::Dragonfly[:images].configure do |c| |
| - | convert = `which convert`.strip.presence || '/usr/local/bin/convert' |
| - | c.convert_command = convert |
| - | c.identify_command = convert |
| - | |
| - | c.allow_fetch_url = true |
| - | c.allow_fetch_file = true |
| - | |
| - | c.url_format = '/images/dynamic/:job/:basename.:format' |
| - | end |
| - | |
| - | self.instance.enabled = true |
| - | rescue Exception => e |
| - | Locomotive::Builder::Logger.warn %{ |
| - | [Dragonfly] !disabled! |
| - | [Dragonfly] If you want to take full benefits of all the features in the LocomotiveBuilder, we recommend you to install ImageMagick and RMagick. Check out the documentation here: http://doc.locomotivecms.com/editor/installation. |
| - | } |
| - | end |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/misc/httparty.rb b/lib/locomotive/builder/misc/httparty.rb
+0
-46
| @@ | @@ -1,46 +0,0 @@ |
| - | require 'uri' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | module Httparty |
| - | class Webservice |
| - | |
| - | include ::HTTParty |
| - | |
| - | def self.consume(url, options = {}) |
| - | url = ::HTTParty.normalize_base_uri(url) |
| - | |
| - | uri = URI.parse(url) |
| - | options[:base_uri] = "#{uri.scheme}://#{uri.host}" |
| - | options[:base_uri] += ":#{uri.port}" if uri.port != 80 |
| - | path = uri.request_uri |
| - | |
| - | options.delete(:format) if options[:format] == 'default' |
| - | |
| - | username, password = options.delete(:username), options.delete(:password) |
| - | options[:basic_auth] = { username: username, password: password } if username |
| - | |
| - | path ||= '/' |
| - | |
| - | # Locomotive::Builder::Logger.debug "[WebService] consuming #{path}, #{options.inspect}" |
| - | |
| - | response = self.get(path, options) |
| - | |
| - | if response.code == 200 |
| - | _response = response.parsed_response |
| - | if _response.respond_to?(:underscore_keys) |
| - | _response.underscore_keys |
| - | else |
| - | _response.collect(&:underscore_keys) |
| - | end |
| - | else |
| - | Locomotive::Builder::Logger.error "[WebService] consumed #{path}, #{options.inspect}, response = #{response.inspect}" |
| - | nil |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/misc/i18n.rb b/lib/locomotive/builder/misc/i18n.rb
+0
-2
| @@ | @@ -1,2 +0,0 @@ |
| - | I18n.load_path = Dir[File.join(File.dirname(__FILE__), "/../../../../locales/*.yml")] |
| - | I18n.backend.reload! |
| \ No newline at end of file | |
locomotive/builder/misc/will_paginate.rb b/lib/locomotive/builder/misc/will_paginate.rb
+0
-16
| @@ | @@ -1,16 +0,0 @@ |
| - | require 'will_paginate' |
| - | require 'will_paginate/collection' |
| - | |
| - | Array.class_eval do |
| - | def paginate(options = {}) |
| - | raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options |
| - | |
| - | WillPaginate::Collection.create( |
| - | options[:page] || 1, |
| - | options[:per_page] || 30, |
| - | options[:total_entries] || self.length |
| - | ) { |pager| |
| - | pager.replace self[pager.offset, pager.per_page].to_a |
| - | } |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server.rb b/lib/locomotive/builder/server.rb
+0
-81
| @@ | @@ -1,81 +0,0 @@ |
| - | require 'rack/showexceptions' |
| - | require 'coffee_script' |
| - | |
| - | require 'locomotive/builder/listen' |
| - | require 'locomotive/builder/server/middleware' |
| - | require 'locomotive/builder/server/favicon' |
| - | require 'locomotive/builder/server/dynamic_assets' |
| - | require 'locomotive/builder/server/logging' |
| - | require 'locomotive/builder/server/entry_submission' |
| - | require 'locomotive/builder/server/path' |
| - | require 'locomotive/builder/server/locale' |
| - | require 'locomotive/builder/server/page' |
| - | require 'locomotive/builder/server/templatized_page' |
| - | require 'locomotive/builder/server/not_found' |
| - | require 'locomotive/builder/server/renderer' |
| - | |
| - | require 'locomotive/builder/liquid' |
| - | require 'locomotive/builder/misc' |
| - | |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | def initialize(reader, options = {}) |
| - | Locomotive::Builder::Dragonfly.setup!(reader.mounting_point.path) |
| - | |
| - | @reader = reader |
| - | @app = self.create_rack_app(@reader) |
| - | |
| - | unless options[:disable_listen] |
| - | Locomotive::Builder::Listen.instance.start(@reader) |
| - | end |
| - | end |
| - | |
| - | def call(env) |
| - | env['builder.mounting_point'] = @reader.mounting_point |
| - | @app.call(env) |
| - | end |
| - | |
| - | protected |
| - | |
| - | def create_rack_app(reader) |
| - | Rack::Builder.new do |
| - | use Rack::ShowExceptions |
| - | use Rack::Lint |
| - | |
| - | use Rack::Session::Cookie, { |
| - | key: 'rack.session', |
| - | domain: '0.0.0.0', |
| - | path: '/', |
| - | expire_after: 2592000, |
| - | secret: 'uselessinlocal' |
| - | } |
| - | |
| - | use ::Dragonfly::Middleware, :images |
| - | |
| - | use Rack::Static, { |
| - | urls: ['/images', '/fonts', '/samples'], |
| - | root: File.join(reader.mounting_point.path, 'public') |
| - | } |
| - | |
| - | use Favicon |
| - | use DynamicAssets |
| - | |
| - | use Logging |
| - | |
| - | use EntrySubmission |
| - | |
| - | use Path |
| - | use Locale |
| - | |
| - | use Page |
| - | use TemplatizedPage |
| - | use NotFound |
| - | use Renderer |
| - | |
| - | run Renderer.new |
| - | end |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/dynamic_assets.rb b/lib/locomotive/builder/server/dynamic_assets.rb
+0
-31
| @@ | @@ -1,31 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | class DynamicAssets < Middleware |
| - | |
| - | def call(env) |
| - | self.set_accessors(env) |
| - | |
| - | path = env['PATH_INFO'] |
| - | |
| - | if path =~ /^\/(stylesheets|javascripts)\// |
| - | |
| - | mime_type = MIME::Types.type_for(path).first.try(:to_s) || 'text/plain' |
| - | asset = self.mounting_point.theme_assets.detect do |_asset| |
| - | _asset.path == path |
| - | end |
| - | |
| - | if asset |
| - | [200, { 'Content-Type' => mime_type }, [asset.content!]] |
| - | else |
| - | [404, { 'Content-Type' => mime_type }, ['Asset not found']] |
| - | end |
| - | else |
| - | app.call(env) |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/entry_submission.rb b/lib/locomotive/builder/server/entry_submission.rb
+0
-116
| @@ | @@ -1,116 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | # Mimic the submission of a content entry |
| - | # |
| - | class EntrySubmission < Middleware |
| - | |
| - | def call(env) |
| - | self.set_accessors(env) |
| - | |
| - | if self.request.post? && env['PATH_INFO'] =~ /^\/entry_submissions\/(.*)/ |
| - | self.process_form($1) |
| - | |
| - | if @entry.valid? |
| - | if self.html? |
| - | self.record_submitted_entry |
| - | self.redirect_to self.callback_url |
| - | elsif self.json? |
| - | self.json_response |
| - | end |
| - | else |
| - | if self.html? |
| - | if self.callback_url =~ /^http:\/\// |
| - | self.redirect_to self.callback_url |
| - | else |
| - | env['PATH_INFO'] = self.callback_url |
| - | self.liquid_assigns[@content_type.slug.singularize] = @entry |
| - | app.call(env) |
| - | end |
| - | elsif self.json? |
| - | self.json_response(422) |
| - | end |
| - | end |
| - | else |
| - | self.fetch_submitted_entry |
| - | |
| - | app.call(env) |
| - | end |
| - | end |
| - | |
| - | protected |
| - | |
| - | def record_submitted_entry |
| - | self.request.session[:now] ||= {} |
| - | self.request.session[:now][:submitted_entry] = [@content_type.slug, @entry._slug] |
| - | end |
| - | |
| - | def fetch_submitted_entry |
| - | if data = self.request.session[:now].try(:delete, :submitted_entry) |
| - | content_type = self.mounting_point.content_types[data.first.to_s] |
| - | |
| - | entry = (content_type.entries || []).detect { |e| e._slug == data.last } |
| - | |
| - | # do not keep track of the entry |
| - | content_type.entries.delete(entry) if entry |
| - | |
| - | # add it to the additional liquid assigns for the next liquid rendering |
| - | if entry |
| - | self.liquid_assigns[content_type.slug.singularize] = entry |
| - | end |
| - | end |
| - | end |
| - | |
| - | # Mimic the creation of a content entry with a minimal validation. |
| - | # |
| - | # @param [ String ] permalink The permalink (or slug) of the content type |
| - | # |
| - | # |
| - | def process_form(permalink) |
| - | permalink = permalink.split('.').first |
| - | |
| - | @content_type = self.mounting_point.content_types[permalink] |
| - | |
| - | raise "Unknown content type '#{@content_type.inspect}'" if @content_type.nil? |
| - | |
| - | @entry = @content_type.build_entry(self.params[:entry] || self.params[:content]) |
| - | |
| - | # if not valid, we do not need to keep track of the entry |
| - | @content_type.entries.delete(@entry) if !@entry.valid? |
| - | end |
| - | |
| - | def callback_url |
| - | (@entry.valid? ? params[:success_callback] : params[:error_callback]) || '/' |
| - | end |
| - | |
| - | # Build the JSON response |
| - | # |
| - | # @param [ Integer ] status The HTTP return code |
| - | # |
| - | # @return [ Array ] The rack response depending on the validation status and the requested format |
| - | # |
| - | def json_response(status = 200) |
| - | locale = self.mounting_point.default_locale |
| - | |
| - | if self.request.path =~ /^\/(#{self.mounting_point.locales.join('|')})+(\/|$)/ |
| - | locale = $1 |
| - | end |
| - | |
| - | hash = @entry.to_hash(false).tap do |_hash| |
| - | if !@entry.valid? |
| - | _hash['errors'] = @entry.errors.inject({}) do |memo, name| |
| - | memo[name] = ::I18n.t('errors.messages.blank', locale: locale) |
| - | memo |
| - | end |
| - | end |
| - | end |
| - | |
| - | [status, { 'Content-Type' => 'application/json' }, [ |
| - | { @content_type.slug.singularize => hash }.to_json |
| - | ]] |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/favicon.rb b/lib/locomotive/builder/server/favicon.rb
+0
-17
| @@ | @@ -1,17 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | class Favicon < Middleware |
| - | |
| - | def call(env) |
| - | if env['PATH_INFO'] == '/favicon.ico' |
| - | [200, { 'Content-Type' => 'image/vnd.microsoft.icon' }, ['']] |
| - | else |
| - | app.call(env) |
| - | end |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/locale.rb b/lib/locomotive/builder/server/locale.rb
+0
-42
| @@ | @@ -1,42 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | # Set the locale from the path if possible or use the default one |
| - | # Examples: |
| - | # /fr/index => locale = :fr |
| - | # /fr/ => locale = :fr |
| - | # /index => locale = :en (default one) |
| - | # |
| - | class Locale < Middleware |
| - | |
| - | def call(env) |
| - | self.set_accessors(env) |
| - | |
| - | self.set_locale!(env) |
| - | |
| - | app.call(env) |
| - | end |
| - | |
| - | protected |
| - | |
| - | def set_locale!(env) |
| - | locale = self.mounting_point.default_locale |
| - | |
| - | if self.path =~ /^(#{self.mounting_point.locales.join('|')})+(\/|$)/ |
| - | locale = $1 |
| - | self.path = self.path.gsub($1 + $2, '') |
| - | self.path = 'index' if self.path.blank? |
| - | end |
| - | |
| - | Locomotive::Mounter.locale = locale |
| - | ::I18n.locale = locale |
| - | |
| - | self.log "Detecting locale #{locale.upcase}" |
| - | |
| - | env['builder.locale'] = locale |
| - | env['builder.path'] = self.path |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/logging.rb b/lib/locomotive/builder/server/logging.rb
+0
-32
| @@ | @@ -1,32 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | # Track the request into the current logger |
| - | # |
| - | class Logging < Middleware |
| - | |
| - | def call(env) |
| - | now = Time.now |
| - | |
| - | log "Started #{env['REQUEST_METHOD'].upcase} \"#{env['PATH_INFO']}\" at #{now}" |
| - | |
| - | app.call(env).tap do |response| |
| - | done_in_ms = (Time.now - now) * 1000 |
| - | log "Completed #{code_to_human(response.first)} in #{done_in_ms}ms\n\n" |
| - | end |
| - | end |
| - | |
| - | protected |
| - | |
| - | def code_to_human(code) |
| - | case code.to_i |
| - | when 200 then '200 OK' |
| - | when 301 then '301 Found' |
| - | when 302 then '302 Found' |
| - | when 404 then '404 Not Found' |
| - | end |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/middleware.rb b/lib/locomotive/builder/server/middleware.rb
+0
-59
| @@ | @@ -1,59 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | class Middleware |
| - | |
| - | attr_accessor :app, :request, :path, :liquid_assigns |
| - | |
| - | attr_accessor :mounting_point, :page, :content_entry |
| - | |
| - | def initialize(app = nil) |
| - | @app = app |
| - | end |
| - | |
| - | def call(env) |
| - | app.call(env) |
| - | end |
| - | |
| - | protected |
| - | |
| - | def set_accessors(env) |
| - | self.path = env['builder.path'] |
| - | self.request = Rack::Request.new(env) |
| - | self.mounting_point = env['builder.mounting_point'] |
| - | self.page = env['builder.page'] |
| - | self.content_entry = env['builder.content_entry'] |
| - | |
| - | env['builder.liquid_assigns'] ||= {} |
| - | self.liquid_assigns = env['builder.liquid_assigns'] |
| - | end |
| - | |
| - | def site |
| - | self.mounting_point.site |
| - | end |
| - | |
| - | def params |
| - | self.request.params.deep_symbolize_keys |
| - | end |
| - | |
| - | def html? |
| - | self.request.media_type == 'text/html' || !self.request.xhr? |
| - | end |
| - | |
| - | def json? |
| - | self.request.content_type == 'application/json' || File.extname(self.request.path) == '.json' |
| - | end |
| - | |
| - | def redirect_to(location, type = 301) |
| - | self.log "Redirected to #{location}" |
| - | [type, { 'Content-Type' => 'text/html', 'Location' => location }, []] |
| - | end |
| - | |
| - | def log(msg) |
| - | Locomotive::Builder::Logger.info msg |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/not_found.rb b/lib/locomotive/builder/server/not_found.rb
+0
-19
| @@ | @@ -1,19 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | class NotFound < Middleware |
| - | |
| - | def call(env) |
| - | self.set_accessors(env) |
| - | |
| - | if self.page.nil? |
| - | self.log "Page not found" |
| - | env['builder.page'] = self.mounting_point.pages['404'] |
| - | end |
| - | |
| - | app.call(env) |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/page.rb b/lib/locomotive/builder/server/page.rb
+0
-61
| @@ | @@ -1,61 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | # Sanitize the path from the previous middleware in order |
| - | # to make it work for the renderer. |
| - | # |
| - | class Page < Middleware |
| - | |
| - | def call(env) |
| - | self.set_accessors(env) |
| - | |
| - | self.set_page!(env) |
| - | |
| - | app.call(env) |
| - | end |
| - | |
| - | protected |
| - | |
| - | def set_page!(env) |
| - | page = self.fetch_page |
| - | |
| - | if page |
| - | self.log "Found page \"#{page.title}\" [/#{page.inspect}]" |
| - | end |
| - | |
| - | env['builder.page'] = page |
| - | end |
| - | |
| - | def fetch_page |
| - | matchers = self.path_combinations(self.path) |
| - | |
| - | self.mounting_point.pages.values.detect do |_page| |
| - | matchers.include?(_page.safe_fullpath) || |
| - | matchers.include?(_page.safe_fullpath.try(:underscore)) |
| - | end |
| - | end |
| - | |
| - | def path_combinations(path) |
| - | self._path_combinations(path.split('/')) |
| - | end |
| - | |
| - | def _path_combinations(segments, can_include_template = true) |
| - | return nil if segments.empty? |
| - | |
| - | segment = segments.shift |
| - | |
| - | (can_include_template ? [segment, '*'] : [segment]).map do |_segment| |
| - | if (_combinations = _path_combinations(segments.clone, can_include_template && _segment != '*')) |
| - | [*_combinations].map do |_combination| |
| - | File.join(_segment, _combination) |
| - | end |
| - | else |
| - | [_segment] |
| - | end |
| - | end.flatten |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/path.rb b/lib/locomotive/builder/server/path.rb
+0
-34
| @@ | @@ -1,34 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | # Sanitize the path from the previous middleware in order |
| - | # to make it work for the renderer. |
| - | # |
| - | class Path < Middleware |
| - | |
| - | def call(env) |
| - | self.set_accessors(env) |
| - | |
| - | self.set_path!(env) |
| - | |
| - | app.call(env) |
| - | end |
| - | |
| - | protected |
| - | |
| - | def set_path!(env) |
| - | path = env['PATH_INFO'].clone |
| - | |
| - | path.gsub!(/\.[a-zA-Z][a-zA-Z0-9]{2,}$/, '') |
| - | path.gsub!(/^\//, '') |
| - | path.gsub!(/^[A-Z]:\//, '') |
| - | |
| - | path = 'index' if path.blank? |
| - | |
| - | env['builder.path'] = path |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/renderer.rb b/lib/locomotive/builder/server/renderer.rb
+0
-105
| @@ | @@ -1,105 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | class Renderer < Middleware |
| - | |
| - | def call(env) |
| - | self.set_accessors(env) |
| - | |
| - | if self.page |
| - | if self.page.redirect? |
| - | self.redirect_to(self.page.redirect_url, self.page.redirect_type) |
| - | else |
| - | type = self.page.response_type || 'text/html' |
| - | html = self.render |
| - | |
| - | self.log " Rendered liquid page template" |
| - | |
| - | [200, { 'Content-Type' => type }, [html]] |
| - | end |
| - | else |
| - | # no page at all, even not the 404 page |
| - | [404, { 'Content-Type' => 'text/html' }, ['Page not found']] |
| - | end |
| - | end |
| - | |
| - | protected |
| - | |
| - | def render |
| - | context = self.locomotive_context |
| - | |
| - | template = ::Liquid::Template.parse(self.page.source, { |
| - | page: self.page, |
| - | mounting_point: self.mounting_point |
| - | }) |
| - | |
| - | template.render(context) |
| - | end |
| - | |
| - | # Build the Liquid context used to render the Locomotive page. It |
| - | # stores both assigns and registers. |
| - | # |
| - | # @param [ Hash ] other_assigns Assigns coming for instance from the controler (optional) |
| - | # |
| - | # @return [ Object ] A new instance of the Liquid::Context class. |
| - | # |
| - | def locomotive_context(other_assigns = {}) |
| - | assigns = self.locomotive_default_assigns |
| - | |
| - | # assigns from other middlewares |
| - | assigns.merge!(self.liquid_assigns) |
| - | |
| - | assigns.merge!(other_assigns) |
| - | |
| - | # templatized page |
| - | if self.page && self.content_entry |
| - | ['content_entry', 'entry', self.page.content_type.slug.singularize].each do |key| |
| - | assigns[key] = self.content_entry |
| - | end |
| - | end |
| - | |
| - | # Tip: switch from false to true to enable the re-thrown exception flag |
| - | ::Liquid::Context.new({}, assigns, self.locomotive_default_registers, true) |
| - | end |
| - | |
| - | # Return the default Liquid assigns used inside the Locomotive Liquid context |
| - | # |
| - | # @return [ Hash ] The default liquid assigns object |
| - | # |
| - | def locomotive_default_assigns |
| - | { |
| - | 'site' => self.site.to_liquid, |
| - | 'page' => self.page, |
| - | 'models' => Locomotive::Builder::Liquid::Drops::ContentTypes.new, |
| - | 'contents' => Locomotive::Builder::Liquid::Drops::ContentTypes.new, |
| - | 'current_page' => self.params[:page], |
| - | 'params' => self.params, |
| - | 'path' => self.request.path, |
| - | 'fullpath' => self.request.fullpath, |
| - | 'url' => self.request.url, |
| - | 'now' => Time.now.utc, |
| - | 'today' => Date.today, |
| - | 'locale' => I18n.locale.to_s, |
| - | 'default_locale' => self.mounting_point.default_locale.to_s, |
| - | 'locales' => self.mounting_point.locales.map(&:to_s), |
| - | 'current_user' => {} |
| - | } |
| - | end |
| - | |
| - | # Return the default Liquid registers used inside the Locomotive Liquid context |
| - | # |
| - | # @return [ Hash ] The default liquid registers object |
| - | # |
| - | def locomotive_default_registers |
| - | { |
| - | site: self.site, |
| - | page: self.page, |
| - | mounting_point: self.mounting_point, |
| - | inline_editor: false |
| - | } |
| - | end |
| - | |
| - | end |
| - | |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/server/templatized_page.rb b/lib/locomotive/builder/server/templatized_page.rb
+0
-32
| @@ | @@ -1,32 +0,0 @@ |
| - | module Locomotive::Builder |
| - | class Server |
| - | |
| - | class TemplatizedPage < Middleware |
| - | |
| - | def call(env) |
| - | self.set_accessors(env) |
| - | |
| - | if self.page && self.page.templatized? |
| - | self.set_content_entry!(env) |
| - | end |
| - | |
| - | app.call(env) |
| - | end |
| - | |
| - | protected |
| - | |
| - | def set_content_entry!(env) |
| - | %r(^#{self.page.safe_fullpath.gsub('*', '([^\/]+)')}$) =~ self.path |
| - | |
| - | permalink = $1 |
| - | |
| - | if content_entry = self.page.content_type.find_entry(permalink) |
| - | env['builder.content_entry'] = content_entry |
| - | else |
| - | env['builder.page'] = nil |
| - | end |
| - | end |
| - | |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/standalone_server.rb b/lib/locomotive/builder/standalone_server.rb
+0
-28
| @@ | @@ -1,28 +0,0 @@ |
| - | $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../..')) |
| - | |
| - | require 'locomotive/builder/logger' |
| - | require 'locomotive/builder/version' |
| - | require 'locomotive/builder/exceptions' |
| - | require 'locomotive/builder/server' |
| - | require 'locomotive/mounter' |
| - | |
| - | module Locomotive |
| - | module Builder |
| - | class StandaloneServer < Server |
| - | |
| - | def initialize(path) |
| - | Locomotive::Builder::Logger.setup(path, false) |
| - | |
| - | # get the reader |
| - | reader = Locomotive::Mounter::Reader::FileSystem.instance |
| - | reader.run!(path: path) |
| - | reader |
| - | |
| - | Bundler.require 'misc' |
| - | |
| - | # run the rack app |
| - | super(reader, disable_listen: true) |
| - | end |
| - | end |
| - | end |
| - | end |
| \ No newline at end of file | |
locomotive/builder/version.rb b/lib/locomotive/builder/version.rb
+0
-5
| @@ | @@ -1,5 +0,0 @@ |
| - | module Locomotive |
| - | module Builder |
| - | VERSION = '1.0.0.alpha8' |
| - | end |
| - | end |
locomotive/wagon.rb b/lib/locomotive/wagon.rb
+163
-0
| @@ | @@ -0,0 +1,163 @@ |
| + | require 'locomotive/wagon/version' |
| + | require 'locomotive/wagon/logger' |
| + | require 'locomotive/wagon/exceptions' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | |
| + | # Create a site from a site generator. |
| + | # |
| + | # @param [ String ] name The name of the site (underscored) |
| + | # @param [ String ] path The destination path of the site |
| + | # @param [ Object ] generator The wrapping class of the generator itself |
| + | # |
| + | def self.init(name, path, generator) |
| + | generator.klass.start [name, path] |
| + | end |
| + | |
| + | # Start the thin server which serves the LocomotiveCMS site from the system. |
| + | # |
| + | # @param [ String ] path The path of the site |
| + | # @param [ Hash ] options The options for the thin server (host, port) |
| + | # |
| + | def self.serve(path, options) |
| + | if reader = self.require_mounter(path, true) |
| + | Bundler.require 'misc' |
| + | |
| + | require 'thin' |
| + | require 'locomotive/wagon/server' |
| + | |
| + | server = Thin::Server.new(options[:host], options[:port], Locomotive::Wagon::Server.new(reader)) |
| + | server.threaded = true # TODO: make it an option ? |
| + | server.start |
| + | end |
| + | end |
| + | |
| + | # Generate components for the LocomotiveCMS site such as content types, snippets, pages. |
| + | # |
| + | # @param [ Symbol ] name The name of the generator |
| + | # @param [ Array ] *args The arguments for the generator |
| + | # |
| + | def self.generate(name, *args) |
| + | Bundler.require 'misc' |
| + | |
| + | lib = "locomotive/wagon/generators/#{name}" |
| + | require lib |
| + | |
| + | generator = lib.camelize.constantize.new(args, {}, {}) |
| + | generator.invoke_all |
| + | end |
| + | |
| + | # Push a site to a remote LocomotiveCMS engine described |
| + | # by the config/deploy.yml file of the site and for a specific environment. |
| + | # |
| + | # @param [ String ] path The path of the site |
| + | # @param [ Hash ] connection_info The information to get connected to the remote site |
| + | # @param [ Hash ] options The options passed to the push process |
| + | # |
| + | def self.push(path, connection_info, options = {}) |
| + | if reader = self.require_mounter(path, true) |
| + | Bundler.require 'misc' |
| + | |
| + | writer = Locomotive::Mounter::Writer::Api.instance |
| + | |
| + | connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api" |
| + | |
| + | _options = { mounting_point: reader.mounting_point, console: true }.merge(options).symbolize_keys |
| + | _options[:only] = _options.delete(:resources) |
| + | |
| + | writer.run!(_options.merge(connection_info)) |
| + | end |
| + | end |
| + | |
| + | # Pull a site from a remote LocomotiveCMS engine described |
| + | # by the config/deploy.yml file of the site and for a specific environment. |
| + | # |
| + | # @param [ String ] path The path of the site |
| + | # @param [ Hash ] connection_info The information to get connected to the remote site |
| + | # @param [ Hash ] options The options passed to the pull process |
| + | # |
| + | def self.pull(path, connection_info, options = {}) |
| + | self.require_mounter(path) |
| + | |
| + | Bundler.require 'misc' |
| + | |
| + | connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api" |
| + | |
| + | _options = { console: true }.merge(options) |
| + | _options[:only] = _options.delete(:resources) |
| + | |
| + | reader = Locomotive::Mounter::Reader::Api.instance |
| + | reader.run!(_options.merge(connection_info)) |
| + | |
| + | writer = Locomotive::Mounter::Writer::FileSystem.instance |
| + | writer.run!(mounting_point: reader.mounting_point, target_path: path) |
| + | rescue Exception => e |
| + | puts e.backtrace |
| + | end |
| + | |
| + | def self.clone(path, connection_info, options = {}) |
| + | if File.exists?(path) |
| + | puts "Path already exists. If it's an existing site, you might want to pull instead of clone." |
| + | return false |
| + | end |
| + | require 'locomotive/mounter' |
| + | |
| + | connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api" |
| + | |
| + | _options = options.dup |
| + | _options[:only] = _options.delete(:resources) |
| + | |
| + | reader = Locomotive::Mounter::Reader::Api.instance |
| + | reader.run!(_options.merge(connection_info)) |
| + | |
| + | writer = Locomotive::Mounter::Writer::FileSystem.instance |
| + | writer.run!(mounting_point: reader.mounting_point, target_path: path) |
| + | # rescue Exception => e |
| + | # puts e.backtrace |
| + | end |
| + | |
| + | # Destroy a remote site |
| + | # |
| + | # @param [ String ] path The path of the site |
| + | # @param [ Hash ] connection_info The information to get connected to the remote site |
| + | # @param [ Hash ] options The options passed to the push process |
| + | # |
| + | def self.destroy(path, connection_info, options = {}) |
| + | self.require_mounter(path) |
| + | |
| + | connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api" |
| + | |
| + | Locomotive::Mounter::EngineApi.set_token connection_info.symbolize_keys |
| + | Locomotive::Mounter::EngineApi.delete('/current_site.json') |
| + | end |
| + | |
| + | # Load the Locomotive::Mounter lib and set it up (logger, ...etc). |
| + | # If the second parameter is set to true, then the method builds |
| + | # an instance of the reader from the path passed in first parameter. |
| + | # |
| + | # @param [ String ] path The path to the local site |
| + | # @param [ Boolean ] get_reader Tell if it builds an instance of the reader. |
| + | # |
| + | # @param [ Object ] An instance of the reader is the get_reader parameter has been set. |
| + | # |
| + | def self.require_mounter(path, get_reader = false) |
| + | Locomotive::Wagon::Logger.setup(path, false) |
| + | |
| + | require 'locomotive/mounter' |
| + | |
| + | Locomotive::Mounter.logger = Locomotive::Wagon::Logger.instance.logger |
| + | |
| + | if get_reader |
| + | begin |
| + | reader = Locomotive::Mounter::Reader::FileSystem.instance |
| + | reader.run!(path: path) |
| + | reader |
| + | rescue Exception => e |
| + | raise Locomotive::Wagon::MounterException.new "Unable to read the local LocomotiveCMS site. Please check the logs.", e |
| + | end |
| + | end |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/cli.rb b/lib/locomotive/wagon/cli.rb
+229
-0
| @@ | @@ -0,0 +1,229 @@ |
| + | require 'thor' |
| + | require 'thor/runner' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module CLI |
| + | |
| + | module CheckPath |
| + | |
| + | protected |
| + | |
| + | # Check if the path given in option ('.' by default) points to a LocomotiveCMS |
| + | # site. It is also possible to pass a path other than the one from the options. |
| + | # |
| + | # @param [ String ] path The optional path instead of options['path'] |
| + | # |
| + | # @return [ String ] The fullpath to the LocomotiveCMS site or nil if it is not a valid site. |
| + | # |
| + | def check_path!(path = nil) |
| + | path ||= options['path'] |
| + | |
| + | path = path == '.' ? Dir.pwd : File.expand_path(path) |
| + | |
| + | (File.exists?(File.join(path, 'config', 'site.yml')) ? path : nil).tap do |_path| |
| + | if _path.nil? |
| + | say 'The path does not point to a LocomotiveCMS site', :red |
| + | end |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | class Generate < Thor |
| + | |
| + | include Locomotive::Wagon::CLI::CheckPath |
| + | |
| + | class_option :path, aliases: '-p', type: :string, default: '.', optional: true, desc: 'if your LocomotiveCMS site is not in the current path' |
| + | |
| + | desc 'content_type NAME FIELDS', 'Create a content type with NAME as the slug and FIELDS as the list of fields.' |
| + | long_desc <<-LONGDESC |
| + | Create a content type with NAME as the slug and FIELDS as the list of fields. |
| + | The fields follows that schema: |
| + | |
| + | field_1[:type][:required] field_2[:type][:required] |
| + | |
| + | Examples: |
| + | |
| + | * wagon generate content_type songs name:string duration:string |
| + | |
| + | * wagon generate content_type posts title body:text:true published_at:date |
| + | LONGDESC |
| + | def content_type(name, *fields) |
| + | say('The fields are missing', :red) and return false if fields.empty? |
| + | |
| + | if check_path! |
| + | Locomotive::Wagon.generate :content_type, name, self.options['path'], fields |
| + | end |
| + | end |
| + | |
| + | desc 'page FULLPATH', 'Create a page. No need to pass an extension to the FULLPATH arg' |
| + | long_desc <<-LONGDESC |
| + | Create a page. The generator will ask for the extension (liquid or haml) and also |
| + | if the page is localized or not. |
| + | |
| + | Examples: |
| + | |
| + | * wagon generate page contact |
| + | |
| + | * wagon generate page about_us/me |
| + | LONGDESC |
| + | def page(fullpath) |
| + | if check_path! |
| + | Locomotive::Wagon.generate :page, fullpath, self.options['path'] |
| + | end |
| + | end |
| + | |
| + | desc 'snippet SLUG', 'Create a snippet' |
| + | long_desc <<-LONGDESC |
| + | Create a snippet. The generator will ask for the extension (liquid or haml) and also |
| + | if the snippet is localized or not. |
| + | |
| + | Example: |
| + | |
| + | * wagon generate snippet footer |
| + | LONGDESC |
| + | def snippet(slug) |
| + | if check_path! |
| + | Locomotive::Wagon.generate :snippet, slug, self.options['path'] |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | class Main < Thor |
| + | |
| + | include Locomotive::Wagon::CLI::CheckPath |
| + | |
| + | desc 'version', 'Version of the LocomotiveCMS wagon' |
| + | def version |
| + | require 'locomotive/wagon/version' |
| + | say Locomotive::Wagon::VERSION |
| + | end |
| + | |
| + | desc 'init NAME [PATH]', 'Create a brand new LocomotiveCMS site' |
| + | method_option :template, aliases: '-t', type: 'string', default: 'blank', desc: 'instead of building from a blank site, you can have a pre-fetched site with form a template (see the templates command)' |
| + | def init(name, path = '.') |
| + | require 'locomotive/wagon/generators/site' |
| + | generator = Locomotive::Wagon::Generators::Site.get(options[:template]) |
| + | if generator.nil? |
| + | say "Unknown site template '#{options[:template]}'", :red |
| + | else |
| + | begin |
| + | Locomotive::Wagon.init(name, path, generator) |
| + | rescue GeneratorException => e |
| + | say e.message, :red |
| + | end |
| + | end |
| + | end |
| + | |
| + | desc 'generate TYPE ...ARGS', 'Generate resources (content_types, page, snippets) for a LocomotiveCMS site' |
| + | subcommand 'generate', Generate |
| + | |
| + | desc 'list_templates', 'List all the templates to create either a site or a content type' |
| + | def list_templates |
| + | require 'locomotive/wagon/generators/site' |
| + | if Locomotive::Wagon::Generators::Site.empty? |
| + | say 'No templates', :red |
| + | else |
| + | Locomotive::Wagon::Generators::Site.list.each do |info| |
| + | say info.name, :bold, false |
| + | say " - #{info.description}" unless info.description.blank? |
| + | end |
| + | end |
| + | end |
| + | |
| + | desc 'serve [PATH]', 'Serve a LocomotiveCMS site from the file system' |
| + | method_option :host, aliases: '-h', type: 'string', default: '0.0.0.0', desc: 'The host (address) of the Thin server' |
| + | method_option :port, aliases: '-p', type: 'string', default: '3333', desc: 'The port of the Thin server' |
| + | def serve(path = '.') |
| + | if check_path!(path) |
| + | begin |
| + | Locomotive::Wagon.serve(path, options) |
| + | rescue Exception => e |
| + | say e.message, :red |
| + | end |
| + | end |
| + | end |
| + | |
| + | desc 'push ENV [PATH]', 'Push a site to a remote LocomotiveCMS engine' |
| + | method_option :resources, aliases: '-r', type: 'array', default: nil, desc: 'Only push the resource(s) passed in argument' |
| + | method_option :force, aliases: '-f', type: 'boolean', default: false, desc: 'Force the push of a resource' |
| + | method_option :data, aliases: '-d', type: 'boolean', default: false, desc: 'Push the content entries and the editable elements (by default, they are not)' |
| + | def push(env, path = '.') |
| + | if check_path!(path) |
| + | if connection_info = self.retrieve_connection_info(env, path) |
| + | begin |
| + | Locomotive::Wagon.push(path, connection_info, options) |
| + | rescue Exception => e |
| + | say e.message, :red |
| + | end |
| + | end |
| + | end |
| + | end |
| + | |
| + | desc 'pull ENV [PATH]', 'Pull a site from a remote LocomotiveCMS engine' |
| + | method_option :resources, aliases: '-r', type: 'array', default: nil, desc: 'Only pull the resource(s) passed in argument' |
| + | # method_option :force, aliases: '-f', type: 'boolean', default: false, desc: 'Force the push of a resource' |
| + | # method_option :data, aliases: '-d', type: 'boolean', default: false, desc: 'Push the content entries and the editable elements (by default, they are not)' |
| + | def pull(env, path = '.') |
| + | if check_path!(path) |
| + | if connection_info = self.retrieve_connection_info(env, path) |
| + | begin |
| + | Locomotive::Wagon.pull(path, connection_info, options) |
| + | rescue Exception => e |
| + | say e.message, :red |
| + | end |
| + | end |
| + | end |
| + | end |
| + | |
| + | desc 'destroy ENV [PATH]', 'Destroy a remote LocomotiveCMS engine' |
| + | def destroy(env, path = '.') |
| + | if check_path!(path) |
| + | if connection_info = self.retrieve_connection_info(env, path) |
| + | if ask('Are you sure ?', limited_to: %w(yes no)) == 'yes' |
| + | Locomotive::Wagon.destroy(path, connection_info) |
| + | else |
| + | say 'The destroy operation has been cancelled', :red |
| + | end |
| + | end |
| + | end |
| + | end |
| + | |
| + | # desc "pull NAME SITE_URL EMAIL PASSWORD", "Pull an existing LocomotiveCMS site powered by the engine" |
| + | # def pull(name, site_url, email, password) |
| + | # say("ERROR: \"#{name}\" directory already exists", :red) and return if File.exists?(name) |
| + | # Locomotive::Wagon.pull(name, site_url, email, password) |
| + | # end |
| + | |
| + | protected |
| + | |
| + | # From a site specified by a path, retrieve the information of the connection |
| + | # for a environment located in the config/deploy.yml file of the site. |
| + | # |
| + | # @param [ String ] env The environment (development, staging, production, ...etc) |
| + | # @param [ String ] path The path of the local site |
| + | # |
| + | # @return [ Hash ] The information of the connection or nil if errors |
| + | # |
| + | def retrieve_connection_info(env, path) |
| + | connection_info = nil |
| + | begin |
| + | path_to_deploy_file = File.join(path, 'config', 'deploy.yml') |
| + | connection_info = YAML::load(File.open(path_to_deploy_file).read)[env.to_s] |
| + | |
| + | if connection_info.nil? |
| + | raise "No #{env.to_s} environment found in the config/deploy.yml file" |
| + | end |
| + | rescue Exception => e |
| + | say "Unable to read the information about the remote LocomotiveCMS site (#{e.message})", :red |
| + | end |
| + | connection_info |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/exceptions.rb b/lib/locomotive/wagon/exceptions.rb
+35
-0
| @@ | @@ -0,0 +1,35 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | |
| + | class DefaultException < ::Exception |
| + | |
| + | def initialize(message = nil, parent_exception = nil) |
| + | self.log_backtrace(parent_exception) if parent_exception |
| + | |
| + | super(message) |
| + | end |
| + | |
| + | protected |
| + | |
| + | def log_backtrace(parent_exception) |
| + | full_error_message = "#{parent_exception.message}\n\t" |
| + | full_error_message += parent_exception.backtrace.join("\n\t") |
| + | full_error_message += "\n\n" |
| + | Locomotive::Wagon::Logger.fatal full_error_message |
| + | end |
| + | |
| + | end |
| + | |
| + | class MounterException < DefaultException |
| + | end |
| + | |
| + | class GeneratorException < DefaultException |
| + | |
| + | def log_backtrace(parent_exception) |
| + | # Logger not initialized at this step |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
locomotive/wagon/generators/content_type.rb b/lib/locomotive/wagon/generators/content_type.rb
+47
-0
| @@ | @@ -0,0 +1,47 @@ |
| + | require 'thor/group' |
| + | require 'ostruct' |
| + | require 'active_support' |
| + | require 'active_support/core_ext' |
| + | require 'faker' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module Generators |
| + | class ContentType < Thor::Group |
| + | |
| + | include Thor::Actions |
| + | |
| + | argument :name |
| + | argument :target_path |
| + | argument :fields |
| + | |
| + | def copy_sources |
| + | directory('.', target_path, { recursive: true }, { |
| + | name: self.name, |
| + | fields: extract_fields(fields) |
| + | }) |
| + | end |
| + | |
| + | def self.source_root |
| + | File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'content_type') |
| + | end |
| + | |
| + | protected |
| + | |
| + | def extract_fields(fields) |
| + | fields.map do |raw_attributes| |
| + | name, type, required = raw_attributes.split(':') |
| + | |
| + | OpenStruct.new({ |
| + | name: name, |
| + | type: type || 'string', |
| + | required: %w(true required).include?(required) |
| + | }) |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/generators/page.rb b/lib/locomotive/wagon/generators/page.rb
+57
-0
| @@ | @@ -0,0 +1,57 @@ |
| + | require 'thor/group' |
| + | require 'active_support' |
| + | require 'active_support/core_ext' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module Generators |
| + | class Page < Thor::Group |
| + | |
| + | include Thor::Actions |
| + | |
| + | argument :slug |
| + | argument :target_path # path to the site |
| + | |
| + | attr_accessor :haml, :locales |
| + | |
| + | def ask_for_haml_and_locales |
| + | self.locales = [] |
| + | self.haml = yes?('Do you prefer a HAML template ?') |
| + | |
| + | if yes?('Is your page localized ?') |
| + | self.locales = ask('What are the locales other than the default one (comma separated) ?').split(',').map(&:strip) |
| + | end |
| + | end |
| + | |
| + | def create_page |
| + | extension = self.haml ? 'liquid.haml' : 'liquid' |
| + | |
| + | segments = self.slug.split('/') |
| + | while segment = segments.pop do |
| + | options = { slug: segment, translated: false } |
| + | file_path = File.join(pages_path, segments, segment) |
| + | |
| + | template "template.#{extension}.tt", "#{file_path}.#{extension}", options |
| + | |
| + | self.locales.each do |locale| |
| + | options[:translated] = true |
| + | template "template.#{extension}.tt", "#{file_path}.#{locale}.#{extension}", options |
| + | end |
| + | end |
| + | end |
| + | |
| + | def self.source_root |
| + | File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'page') |
| + | end |
| + | |
| + | protected |
| + | |
| + | def pages_path |
| + | File.join(target_path, 'app', 'views', 'pages') |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/generators/site.rb b/lib/locomotive/wagon/generators/site.rb
+98
-0
| @@ | @@ -0,0 +1,98 @@ |
| + | require 'ostruct' |
| + | require 'singleton' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module Generators |
| + | |
| + | module Site |
| + | |
| + | # Register a generator by adding it to the list of existing generators. |
| + | # |
| + | # @param [ String ] name The name of the generator |
| + | # @param [ Class ] klass The class of the generator |
| + | # @param [ String ] description The description of the generator (can be nil) |
| + | # |
| + | # @return [ Boolean ] True if the registration has been successful, false otherwise. |
| + | # |
| + | def self.register(name, klass, description = nil) |
| + | Locomotive::Wagon::Generators::Site::List.instance.register(name, klass, description) |
| + | end |
| + | |
| + | # Return the information about a generator from its name. |
| + | # |
| + | # @param [ String ] name The name of the generator |
| + | # |
| + | # @return [ Object ] The information of the found generator or nil |
| + | # |
| + | def self.get(name) |
| + | Locomotive::Wagon::Generators::Site::List.instance.get(name) |
| + | end |
| + | |
| + | # List all the generators |
| + | # |
| + | # @return [ Array ] The filtered (or not) list of generators |
| + | # |
| + | def self.list |
| + | Locomotive::Wagon::Generators::Site::List.instance._list |
| + | end |
| + | |
| + | # Tell if the list of generators is empty or not . |
| + | # |
| + | # @return [ Boolean ] True if empty |
| + | # |
| + | def self.empty? |
| + | Locomotive::Wagon::Generators::Site::List.instance._list.empty? |
| + | end |
| + | |
| + | class List |
| + | |
| + | include ::Singleton |
| + | |
| + | attr_accessor :_list |
| + | |
| + | def initialize |
| + | self._list = [] |
| + | end |
| + | |
| + | # Return the information about a generator from its name. |
| + | # |
| + | # @param [ String ] name The name of the generator |
| + | # |
| + | # @return [ Object ] The information of the found generator or nil |
| + | # |
| + | def get(name) |
| + | self._list.detect { |entry| entry.name == name.to_sym } |
| + | end |
| + | |
| + | # Register a generator by adding it to the list of existing generators. |
| + | # |
| + | # @param [ String ] name The name of the generator |
| + | # @param [ Class ] klass The class of the generator |
| + | # @param [ String ] description The description of the generator (can be nil) |
| + | # |
| + | # @return [ Boolean ] True if the registration has been successful, false otherwise. |
| + | # |
| + | def register(name, klass, description = nil) |
| + | return false unless self.get(name).nil? |
| + | |
| + | self._list << OpenStruct.new({ |
| + | name: name.to_sym, |
| + | klass: klass, |
| + | description: description ? description.strip.gsub("\n", '') : nil |
| + | }) |
| + | |
| + | self._list.last |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | |
| + | require 'locomotive/wagon/generators/site/base' |
| + | require 'locomotive/wagon/generators/site/blank' |
| + | require 'locomotive/wagon/generators/site/bootstrap' |
| + | require 'locomotive/wagon/generators/site/unzip' |
locomotive/wagon/generators/site/base.rb b/lib/locomotive/wagon/generators/site/base.rb
+30
-0
| @@ | @@ -0,0 +1,30 @@ |
| + | require 'thor/group' |
| + | require 'active_support' |
| + | require 'active_support/core_ext' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module Generators |
| + | module Site |
| + | |
| + | class Base < Thor::Group |
| + | |
| + | include Thor::Actions |
| + | |
| + | argument :name |
| + | argument :target_path |
| + | |
| + | def self.source_root |
| + | File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'generators', self.name.demodulize.underscore) |
| + | end |
| + | |
| + | def destination |
| + | File.join(target_path, name) |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/generators/site/blank.rb b/lib/locomotive/wagon/generators/site/blank.rb
+23
-0
| @@ | @@ -0,0 +1,23 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Generators |
| + | module Site |
| + | |
| + | class Blank < Base |
| + | |
| + | def copy_sources |
| + | directory('.', self.destination, { recursive: true }, { |
| + | name: self.name, |
| + | version: Locomotive::Wagon::VERSION |
| + | }) |
| + | end |
| + | |
| + | end |
| + | |
| + | Locomotive::Wagon::Generators::Site.register(:blank, Blank, %{ |
| + | A blank LocomotiveCMS site with the minimal files. |
| + | }) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/generators/site/bootstrap.rb b/lib/locomotive/wagon/generators/site/bootstrap.rb
+35
-0
| @@ | @@ -0,0 +1,35 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Generators |
| + | module Site |
| + | |
| + | class Bootstrap < Base |
| + | |
| + | def copy_sources |
| + | directory('.', self.destination, { recursive: true }, { |
| + | name: self.name, |
| + | version: Locomotive::Wagon::VERSION |
| + | }) |
| + | end |
| + | |
| + | def choose_haml_over_html |
| + | if yes?('Do you prefer HAML templates ?') |
| + | remove_file File.join(self.destination, 'app/views/pages/index.liquid') |
| + | remove_file File.join(self.destination, 'app/views/pages/404.liquid') |
| + | remove_file File.join(self.destination, 'app/views/snippets/footer.liquid') |
| + | else |
| + | remove_file File.join(self.destination, 'app/views/pages/index.liquid.haml') |
| + | remove_file File.join(self.destination, 'app/views/pages/404.liquid.haml') |
| + | remove_file File.join(self.destination, 'app/views/snippets/footer.liquid.haml') |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | Locomotive::Wagon::Generators::Site.register(:bootstrap, Bootstrap, %{ |
| + | A LocomotiveCMS site powered by Twitter bootstrap (v2.2.2). |
| + | }) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/generators/site/unzip.rb b/lib/locomotive/wagon/generators/site/unzip.rb
+81
-0
| @@ | @@ -0,0 +1,81 @@ |
| + | require 'open-uri' |
| + | require 'zip/zipfilesystem' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module Generators |
| + | module Site |
| + | |
| + | class Unzip < Base |
| + | |
| + | def prepare |
| + | remove_file join('tmp') |
| + | empty_directory join('tmp') |
| + | end |
| + | |
| + | def ask_for_location |
| + | @location = ask('What is the location (on the filesystem or url) of the zip file ?') |
| + | raise GeneratorException.new('Please enter a location') if @location.blank? |
| + | end |
| + | |
| + | def download_or_copy |
| + | @template_path = join('tmp', File.basename(@location)) |
| + | |
| + | if @location =~ /^https?:\/\// |
| + | say "downloading...#{@location}" |
| + | create_file @template_path, open(@location).read |
| + | else |
| + | say "copying...#{@location}" |
| + | create_file @template_path, open(@location, 'rb') { |io| io.read } |
| + | end |
| + | end |
| + | |
| + | def unzip |
| + | say "unzipping...#{@template_path}" |
| + | |
| + | begin |
| + | Zip::ZipFile.open(@template_path) do |zipfile| |
| + | zipfile.each do |file| |
| + | next if file.name =~ /^__MACOSX/ |
| + | zipfile.extract(file, join('tmp', file.name)) |
| + | |
| + | @path = $1 if file.name =~ /(.*)\/config\/site.yml$/ |
| + | end |
| + | end |
| + | rescue Exception => e |
| + | raise GeneratorException.new("Unable to unzip the archive") |
| + | end |
| + | |
| + | raise GeneratorException.new('Not a valid LocomotiveCMS site') if @path.blank? |
| + | end |
| + | |
| + | def copy_sources |
| + | self.class.source_root = File.expand_path(join('tmp', @path, '/')) |
| + | say "copying files from #{self.class.source_root} / #{self.destination}" |
| + | directory('.', self.destination, { recursive: true }) |
| + | end |
| + | |
| + | def self.source_root |
| + | # only way to change the source root from the instance |
| + | @@source_root |
| + | end |
| + | |
| + | def self.source_root=(value) |
| + | @@source_root = value |
| + | end |
| + | |
| + | protected |
| + | |
| + | def join(*args) |
| + | File.join(self.destination, *args) |
| + | end |
| + | |
| + | end |
| + | |
| + | Locomotive::Wagon::Generators::Site.register(:unzip, Unzip, %{ |
| + | Unzip a local or remote (http, https, ftp) zipped LocomotiveCMS site. |
| + | }) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/generators/snippet.rb b/lib/locomotive/wagon/generators/snippet.rb
+54
-0
| @@ | @@ -0,0 +1,54 @@ |
| + | require 'thor/group' |
| + | require 'active_support' |
| + | require 'active_support/core_ext' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module Generators |
| + | class Snippet < Thor::Group |
| + | |
| + | include Thor::Actions |
| + | |
| + | argument :slug |
| + | argument :target_path # path to the site |
| + | |
| + | attr_accessor :haml, :locales |
| + | |
| + | def ask_for_haml_and_locales |
| + | self.locales = [] |
| + | self.haml = yes?('Do you prefer a HAML template ?') |
| + | |
| + | if yes?('Is your snippet localized ?') |
| + | self.locales = ask('What are the locales other than the default one (comma separated) ?').split(',').map(&:strip) |
| + | end |
| + | end |
| + | |
| + | def create_snippet |
| + | extension = self.haml ? 'liquid.haml' : 'liquid' |
| + | |
| + | options = { slug: slug, translated: false } |
| + | file_path = File.join(pages_path, slug) |
| + | |
| + | template "template.#{extension}.tt", "#{file_path}.#{extension}", options |
| + | |
| + | self.locales.each do |locale| |
| + | options[:translated] = true |
| + | template "template.#{extension}.tt", "#{file_path}.#{locale}.#{extension}", options |
| + | end |
| + | end |
| + | |
| + | def self.source_root |
| + | File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'snippet') |
| + | end |
| + | |
| + | protected |
| + | |
| + | def pages_path |
| + | File.join(target_path, 'app', 'views', 'snippets') |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid.rb b/lib/locomotive/wagon/liquid.rb
+19
-0
| @@ | @@ -0,0 +1,19 @@ |
| + | require "locomotive/mounter" |
| + | require 'liquid' |
| + | require 'locomotive/wagon/liquid/drops/base' |
| + | |
| + | %w{. drops tags filters}.each do |dir| |
| + | Dir[File.join(File.dirname(__FILE__), 'liquid', dir, '*.rb')].each { |lib| require lib } |
| + | end |
| + | |
| + | |
| + | # add to_liquid methods to main models from the mounter |
| + | %w{site page content_entry}.each do |name| |
| + | klass = "Locomotive::Mounter::Models::#{name.classify}".constantize |
| + | |
| + | klass.class_eval <<-EOV |
| + | def to_liquid |
| + | ::Locomotive::Wagon::Liquid::Drops::#{name.classify}.new(self) |
| + | end |
| + | EOV |
| + | end |
locomotive/wagon/liquid/drops/base.rb b/lib/locomotive/wagon/liquid/drops/base.rb
+44
-0
| @@ | @@ -0,0 +1,44 @@ |
| + | # Code taken from Mephisto sources (http://mephistoblog.com/) |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Drops |
| + | class Base < ::Liquid::Drop |
| + | |
| + | @@forbidden_attributes = %w{_id _version _index} |
| + | |
| + | attr_reader :_source |
| + | |
| + | def initialize(source) |
| + | @_source = source |
| + | end |
| + | |
| + | def id |
| + | (@_source.respond_to?(:id) ? @_source.id : nil) || 'new' |
| + | end |
| + | |
| + | # converts an array of records to an array of liquid drops |
| + | def self.liquify(*records, &block) |
| + | i = -1 |
| + | records = |
| + | records.inject [] do |all, r| |
| + | i+=1 |
| + | attrs = (block && block.arity == 1) ? [r] : [r, i] |
| + | all << (block ? block.call(*attrs) : r.to_liquid) |
| + | all |
| + | end |
| + | records.compact! |
| + | records |
| + | end |
| + | |
| + | protected |
| + | |
| + | def liquify(*records, &block) |
| + | self.class.liquify(*records, &block) |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/drops/content_entry.rb b/lib/locomotive/wagon/liquid/drops/content_entry.rb
+48
-0
| @@ | @@ -0,0 +1,48 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Drops |
| + | class ContentEntry < Base |
| + | |
| + | delegate :seo_title, :meta_keywords, :meta_description, :to => '_source' |
| + | |
| + | def _label |
| + | @_label ||= self._source._label |
| + | end |
| + | |
| + | def _permalink |
| + | @_source._permalink.try(:parameterize) |
| + | end |
| + | |
| + | alias :_slug :_permalink |
| + | |
| + | def next |
| + | self |
| + | end |
| + | |
| + | def previous |
| + | self |
| + | end |
| + | |
| + | def errors |
| + | (@_source.errors || []).inject({}) do |memo, name| |
| + | memo[name] = ::I18n.t('errors.messages.blank') |
| + | memo |
| + | end |
| + | end |
| + | |
| + | def before_method(meth) |
| + | return '' if self._source.nil? |
| + | |
| + | if not @@forbidden_attributes.include?(meth.to_s) |
| + | self._source.send(meth) |
| + | else |
| + | nil |
| + | end |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/drops/content_types.rb b/lib/locomotive/wagon/liquid/drops/content_types.rb
+121
-0
| @@ | @@ -0,0 +1,121 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Drops |
| + | class ContentTypes < ::Liquid::Drop |
| + | |
| + | def before_method(meth) |
| + | type = self.mounting_point.content_types[meth.to_s] |
| + | ProxyCollection.new(type) |
| + | end |
| + | |
| + | end |
| + | |
| + | class ProxyCollection < ::Liquid::Drop |
| + | |
| + | def initialize(content_type) |
| + | @content_type = content_type |
| + | @collection = nil |
| + | end |
| + | |
| + | def first |
| + | self.collection.first |
| + | end |
| + | |
| + | def last |
| + | self.collection.last |
| + | end |
| + | |
| + | def size |
| + | self.collection.size |
| + | end |
| + | |
| + | alias :length :size |
| + | |
| + | def each(&block) |
| + | self.collection.each(&block) |
| + | end |
| + | |
| + | def public_submission_url |
| + | "/entry_submissions/#{@content_type.slug}" |
| + | end |
| + | |
| + | def api |
| + | { 'create' => "/entry_submissions/#{@content_type.slug}" } |
| + | end |
| + | |
| + | def before_method(meth) |
| + | if (meth.to_s =~ /^group_by_(.+)$/) == 0 |
| + | self.group_entries_by(@content_type, $1) |
| + | elsif (meth.to_s =~ /^(.+)_options$/) == 0 |
| + | self.select_options_for(@content_type, $1) |
| + | else |
| + | @content_type.send(meth) |
| + | end |
| + | end |
| + | |
| + | protected |
| + | |
| + | def group_entries_by(content_type, name) |
| + | field = @content_type.find_field(name) |
| + | |
| + | return {} if field.nil? || !%w(belongs_to select).include?(field.type.to_s) |
| + | |
| + | (@content_type.entries || []).group_by do |entry| |
| + | entry.send(name.to_sym) |
| + | end.to_a.collect do |group| |
| + | { name: group.first, entries: group.last }.with_indifferent_access |
| + | end |
| + | end |
| + | |
| + | def select_options_for(content_type, name) |
| + | field = @content_type.find_field(name) |
| + | |
| + | return {} if field.nil? || field.type.to_s != 'select' |
| + | |
| + | field.select_options.map(&:name) |
| + | end |
| + | |
| + | def paginate(options = {}) |
| + | @collection ||= self.collection.paginate(options) |
| + | { |
| + | collection: @collection, |
| + | current_page: @collection.current_page, |
| + | previous_page: @collection.previous_page, |
| + | next_page: @collection.next_page, |
| + | total_entries: @collection.total_entries, |
| + | total_pages: @collection.total_pages, |
| + | per_page: @collection.per_page |
| + | } |
| + | end |
| + | |
| + | def collection |
| + | return unless @collection.blank? |
| + | |
| + | if @context['with_scope'].blank? |
| + | @collection = @content_type.entries |
| + | else |
| + | @collection = [] |
| + | |
| + | conditions = @context['with_scope'].clone.delete_if { |k, _| %w(order_by per_page page).include?(k) } |
| + | |
| + | @content_type.entries.each do |content| |
| + | accepted = (conditions.map do |key, value| |
| + | case value |
| + | when TrueClass, FalseClass, String then content.send(key) == value |
| + | else |
| + | true |
| + | end |
| + | end).all? # all conditions works ? |
| + | |
| + | @collection << content if accepted |
| + | end |
| + | end |
| + | |
| + | @collection |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/drops/page.rb b/lib/locomotive/wagon/liquid/drops/page.rb
+36
-0
| @@ | @@ -0,0 +1,36 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Drops |
| + | class Page < Base |
| + | |
| + | delegate :title, :slug, :fullpath, :parent, :depth, :seo_title, :redirect_url, :meta_description, :meta_keywords, :to => '_source' |
| + | |
| + | def children |
| + | _children = @_source.children || [] |
| + | _children = _children.sort { |a, b| a.position.to_i <=> b.position.to_i } |
| + | @children ||= liquify(*_children) |
| + | end |
| + | |
| + | def published? |
| + | @_source.published? |
| + | end |
| + | |
| + | def redirect? |
| + | self._source.redirect? |
| + | end |
| + | |
| + | def breadcrumbs |
| + | # TODO |
| + | '' |
| + | end |
| + | |
| + | def listed? |
| + | @_source.listed? |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/drops/site.rb b/lib/locomotive/wagon/liquid/drops/site.rb
+21
-0
| @@ | @@ -0,0 +1,21 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Drops |
| + | class Site < Base |
| + | |
| + | delegate :name, :seo_title, :meta_description, :meta_keywords, :to => '_source' |
| + | |
| + | def index |
| + | @index ||= self.mounting_point.pages['index'] |
| + | end |
| + | |
| + | def pages |
| + | @pages ||= liquify(*self._source.pages) |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/errors.rb b/lib/locomotive/wagon/liquid/errors.rb
+7
-0
| @@ | @@ -0,0 +1,7 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | class PageNotFound < ::Liquid::Error; end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/filters/date.rb b/lib/locomotive/wagon/liquid/filters/date.rb
+98
-0
| @@ | @@ -0,0 +1,98 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Filters |
| + | module Date |
| + | |
| + | def localized_date(input, *args) |
| + | return '' if input.blank? |
| + | |
| + | format, locale = args |
| + | |
| + | locale ||= I18n.locale |
| + | format ||= I18n.t('date.formats.default', :locale => locale) |
| + | |
| + | if input.is_a?(String) |
| + | begin |
| + | fragments = ::Date._strptime(input, format) |
| + | input = ::Date.new(fragments[:year], fragments[:mon], fragments[:mday]) |
| + | rescue |
| + | input = Time.parse(input) |
| + | end |
| + | end |
| + | |
| + | return input.to_s unless input.respond_to?(:strftime) |
| + | |
| + | I18n.l input, :format => format, :locale => locale |
| + | end |
| + | |
| + | alias :format_date :localized_date |
| + | |
| + | def distance_of_time_in_words(input, *args) |
| + | return '' if input.blank? |
| + | |
| + | from_time = input |
| + | to_time = args[0] || Time.now |
| + | |
| + | from_time = from_time.to_time if from_time.respond_to?(:to_time) |
| + | to_time = to_time.to_time if to_time.respond_to?(:to_time) |
| + | distance_in_minutes = (((to_time - from_time).abs)/60).round |
| + | distance_in_seconds = ((to_time - from_time).abs).round |
| + | |
| + | ::I18n.with_options({ :scope => :'datetime.distance_in_words' }) do |locale| |
| + | |
| + | case distance_in_minutes |
| + | when 0..1 |
| + | return distance_in_minutes == 0 ? |
| + | locale.t(:less_than_x_minutes, :count => 1) : |
| + | locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds |
| + | |
| + | case distance_in_seconds |
| + | when 0..4 then locale.t :less_than_x_seconds, :count => 5 |
| + | when 5..9 then locale.t :less_than_x_seconds, :count => 10 |
| + | when 10..19 then locale.t :less_than_x_seconds, :count => 20 |
| + | when 20..39 then locale.t :half_a_minute |
| + | when 40..59 then locale.t :less_than_x_minutes, :count => 1 |
| + | else locale.t :x_minutes, :count => 1 |
| + | end |
| + | |
| + | when 2..44 then locale.t :x_minutes, :count => distance_in_minutes |
| + | when 45..89 then locale.t :about_x_hours, :count => 1 |
| + | when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round |
| + | when 1440..2519 then locale.t :x_days, :count => 1 |
| + | when 2520..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round |
| + | when 43200..86399 then locale.t :about_x_months, :count => 1 |
| + | when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round |
| + | else |
| + | fyear = from_time.year |
| + | fyear += 1 if from_time.month >= 3 |
| + | tyear = to_time.year |
| + | tyear -= 1 if to_time.month < 3 |
| + | leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| ::Date.leap?(x)} |
| + | minute_offset_for_leap_year = leap_years * 1440 |
| + | # Discount the leap year days when calculating year distance. |
| + | # e.g. if there are 20 leap year days between 2 dates having the same day |
| + | # and month then the based on 365 days calculation |
| + | # the distance in years will come out to over 80 years when in written |
| + | # english it would read better as about 80 years. |
| + | minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year |
| + | remainder = (minutes_with_offset % 525600) |
| + | distance_in_years = (minutes_with_offset / 525600) |
| + | if remainder < 131400 |
| + | locale.t(:about_x_years, :count => distance_in_years) |
| + | elsif remainder < 394200 |
| + | locale.t(:over_x_years, :count => distance_in_years) |
| + | else |
| + | locale.t(:almost_x_years, :count => distance_in_years + 1) |
| + | end |
| + | end |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_filter(Date) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/filters/html.rb b/lib/locomotive/wagon/liquid/filters/html.rb
+154
-0
| @@ | @@ -0,0 +1,154 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Filters |
| + | module Html |
| + | |
| + | # Write the link to a stylesheet resource |
| + | # input: url of the css file |
| + | def stylesheet_tag(input, media = 'screen') |
| + | return '' if input.nil? |
| + | |
| + | input = "/stylesheets/#{input}" unless input =~ /^(\/|http:)/ |
| + | |
| + | input = "#{input}.css" unless input.ends_with?('.css') |
| + | |
| + | %{<link href="#{input}" media="#{media}" rel="stylesheet" type="text/css" />} |
| + | end |
| + | |
| + | # Write the link to javascript resource |
| + | # input: url of the javascript file |
| + | def javascript_tag(input) |
| + | return '' if input.nil? |
| + | |
| + | input = "/javascripts/#{input}" unless input =~ /^(\/|http:)/ |
| + | |
| + | input = "#{input}.js" unless input.ends_with?('.js') |
| + | |
| + | %{<script src="#{input}" type="text/javascript"></script>} |
| + | end |
| + | |
| + | # Write an image tag |
| + | # input: url of the image OR asset drop |
| + | def image_tag(input, *args) |
| + | image_options = inline_options(args_to_options(args)) |
| + | |
| + | input = "/images/#{input}" unless input =~ /^(\/|http:)/ |
| + | |
| + | "<img src=\"#{File.join('/', get_url_from_asset(input))}\" #{image_options}/>" |
| + | end |
| + | |
| + | # Write a theme image tag |
| + | # input: name of file including folder |
| + | # example: 'about/myphoto.jpg' | theme_image # <img src="images/about/myphoto.jpg" /> |
| + | def theme_image_tag(input, *args) |
| + | image_options = inline_options(args_to_options(args)) |
| + | "<img src=\"#{theme_image_url(input)}\" #{image_options}/>" |
| + | end |
| + | |
| + | def theme_image_url(input) |
| + | return '' if input.nil? |
| + | |
| + | input = "images/#{input}" unless input.starts_with?('/') |
| + | |
| + | File.join('/', input) |
| + | end |
| + | |
| + | def image_format(input, *args) |
| + | format = args_to_options(args).first |
| + | "#{input}.#{format}" |
| + | end |
| + | |
| + | # Embed a flash movie into a page |
| + | # input: url of the flash movie OR asset drop |
| + | # width: width (in pixel or in %) of the embedded movie |
| + | # height: height (in pixel or in %) of the embedded movie |
| + | def flash_tag(input, *args) |
| + | path = get_url_from_asset(input) |
| + | embed_options = inline_options(args_to_options(args)) |
| + | %{ |
| + | <object #{embed_options}> |
| + | <param name="movie" value="#{path}" /> |
| + | <embed src="#{path}" #{embed_options}/> |
| + | </embed> |
| + | </object> |
| + | }.gsub(/ >/, '>').strip |
| + | end |
| + | |
| + | # Render the navigation for a paginated collection |
| + | def default_pagination(paginate, *args) |
| + | return '' if paginate['parts'].empty? |
| + | |
| + | options = args_to_options(args) |
| + | |
| + | previous_label = options[:previous_label] || I18n.t('pagination.previous') |
| + | next_label = options[:next_label] || I18n.t('pagination.next') |
| + | |
| + | previous_link = (if paginate['previous'].blank? |
| + | "<span class=\"disabled prev_page\">#{previous_label}</span>" |
| + | else |
| + | "<a href=\"#{absolute_url(paginate['previous']['url'])}\" class=\"prev_page\">#{previous_label}</a>" |
| + | end) |
| + | |
| + | links = "" |
| + | paginate['parts'].each do |part| |
| + | links << (if part['is_link'] |
| + | "<a href=\"#{absolute_url(part['url'])}\">#{part['title']}</a>" |
| + | elsif part['hellip_break'] |
| + | "<span class=\"gap\">#{part['title']}</span>" |
| + | else |
| + | "<span class=\"current\">#{part['title']}</span>" |
| + | end) |
| + | end |
| + | |
| + | next_link = (if paginate['next'].blank? |
| + | "<span class=\"disabled next_page\">#{next_label}</span>" |
| + | else |
| + | "<a href=\"#{absolute_url(paginate['next']['url'])}\" class=\"next_page\">#{next_label}</a>" |
| + | end) |
| + | |
| + | %{<div class="pagination #{options[:css]}"> |
| + | #{previous_link} |
| + | #{links} |
| + | #{next_link} |
| + | </div>} |
| + | end |
| + | |
| + | protected |
| + | |
| + | # Convert an array of properties ('key:value') into a hash |
| + | # Ex: ['width:50', 'height:100'] => { :width => '50', :height => '100' } |
| + | def args_to_options(*args) |
| + | options = {} |
| + | args.flatten.each do |a| |
| + | if (a =~ /^(.*):(.*)$/) |
| + | options[$1.to_sym] = $2 |
| + | end |
| + | end |
| + | options |
| + | end |
| + | |
| + | # Write options (Hash) into a string according to the following pattern: |
| + | # <key1>="<value1>", <key2>="<value2", ...etc |
| + | def inline_options(options = {}) |
| + | return '' if options.empty? |
| + | (options.stringify_keys.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ') << ' ' |
| + | end |
| + | |
| + | # Get the path to be used in html tags such as image_tag, flash_tag, ...etc |
| + | # input: url (String) OR asset drop |
| + | def get_url_from_asset(input) |
| + | input.respond_to?(:url) ? input.url : input |
| + | end |
| + | |
| + | def absolute_url(url) |
| + | url =~ /^\// ? url : "/#{url}" |
| + | end |
| + | end |
| + | |
| + | ::Liquid::Template.register_filter(Html) |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/filters/misc.rb b/lib/locomotive/wagon/liquid/filters/misc.rb
+28
-0
| @@ | @@ -0,0 +1,28 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Filters |
| + | module Misc |
| + | |
| + | # was called modulo at first |
| + | def str_modulo(word, index, modulo) |
| + | (index.to_i + 1) % modulo == 0 ? word : '' |
| + | end |
| + | |
| + | # Get the nth element of the passed in array |
| + | def index(array, position) |
| + | array.at(position) if array.respond_to?(:at) |
| + | end |
| + | |
| + | def default(input, value) |
| + | input.blank? ? value : input |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_filter(Misc) |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/filters/resize.rb b/lib/locomotive/wagon/liquid/filters/resize.rb
+18
-0
| @@ | @@ -0,0 +1,18 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Filters |
| + | module Resize |
| + | |
| + | def resize(input, resize_string) |
| + | Locomotive::Wagon::Dragonfly.instance.resize_url(input, resize_string) |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_filter(Resize) |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/filters/text.rb b/lib/locomotive/wagon/liquid/filters/text.rb
+50
-0
| @@ | @@ -0,0 +1,50 @@ |
| + | require 'RedCloth' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Filters |
| + | module Text |
| + | |
| + | # right justify and padd a string |
| + | def rjust(input, integer, padstr = '') |
| + | input.to_s.rjust(integer, padstr) |
| + | end |
| + | |
| + | # left justify and padd a string |
| + | def ljust(input, integer, padstr = '') |
| + | input.to_s.ljust(integer, padstr) |
| + | end |
| + | |
| + | def underscore(input) |
| + | input.to_s.gsub(' ', '_').gsub('/', '_').underscore |
| + | end |
| + | |
| + | def dasherize(input) |
| + | input.to_s.gsub(' ', '-').gsub('/', '-').dasherize |
| + | end |
| + | |
| + | # alias newline_to_br |
| + | def multi_line(input) |
| + | input.to_s.gsub("\n", '<br/>') |
| + | end |
| + | |
| + | def concat(input, *args) |
| + | result = input.to_s |
| + | args.flatten.each { |a| result << a.to_s } |
| + | result |
| + | end |
| + | |
| + | |
| + | def textile(input) |
| + | ::RedCloth.new(input).to_html |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_filter(Text) |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/filters/translate.rb b/lib/locomotive/wagon/liquid/filters/translate.rb
+24
-0
| @@ | @@ -0,0 +1,24 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Filters |
| + | module Translate |
| + | |
| + | def translate(key, locale = nil) |
| + | translation = @context.registers[:mounting_point].translations[key.to_s] |
| + | |
| + | if translation |
| + | translation.get(locale) || translation.get(Locomotive::Mounter.locale) |
| + | else |
| + | "[unknown translation key: #{key}]" |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_filter(Translate) |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/patches.rb b/lib/locomotive/wagon/liquid/patches.rb
+47
-0
| @@ | @@ -0,0 +1,47 @@ |
| + | module Liquid |
| + | |
| + | class Drop |
| + | |
| + | def mounting_point |
| + | @context.registers[:mounting_point] |
| + | end |
| + | |
| + | def site |
| + | @context.registers[:site] |
| + | end |
| + | |
| + | end |
| + | |
| + | class Template |
| + | |
| + | # creates a new <tt>Template</tt> object from liquid source code |
| + | def parse_with_utf8(source, context = {}) |
| + | if RUBY_VERSION =~ /1\.9/ |
| + | source = source.force_encoding('UTF-8') if source.present? |
| + | end |
| + | self.parse_without_utf8(source, context) |
| + | end |
| + | |
| + | alias_method_chain :parse, :utf8 |
| + | |
| + | end |
| + | |
| + | module StandardFilters |
| + | |
| + | private |
| + | |
| + | def to_number(obj) |
| + | case obj |
| + | when Numeric |
| + | obj |
| + | when String |
| + | (obj.strip =~ /^\d+\.\d+$/) ? obj.to_f : obj.to_i |
| + | when DateTime, Date, Time |
| + | obj.to_time.to_i |
| + | else |
| + | 0 |
| + | end |
| + | end |
| + | end |
| + | |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/consume.rb b/lib/locomotive/wagon/liquid/tags/consume.rb
+58
-0
| @@ | @@ -0,0 +1,58 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | |
| + | # Consume web services as easy as pie directly in liquid ! |
| + | # |
| + | # Usage: |
| + | # |
| + | # {% consume blog from 'http://nocoffee.tumblr.com/api/read.json?num=3' username: 'john', password: 'easy', format: 'json', expires_in: 3000 %} |
| + | # {% for post in blog.posts %} |
| + | # {{ post.title }} |
| + | # {% endfor %} |
| + | # {% endconsume %} |
| + | # |
| + | class Consume < ::Liquid::Block |
| + | |
| + | Syntax = /(#{::Liquid::VariableSignature}+)\s*from\s*(#{::Liquid::QuotedString}+)/ |
| + | |
| + | def initialize(tag_name, markup, tokens, context) |
| + | if markup =~ Syntax |
| + | @target = $1 |
| + | @url = $2.gsub(/['"]/, '') |
| + | @options = {} |
| + | markup.scan(::Liquid::TagAttributes) do |key, value| |
| + | @options[key] = value if key != 'http' |
| + | end |
| + | @options.delete('expires_in') |
| + | else |
| + | raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]") |
| + | end |
| + | |
| + | super |
| + | end |
| + | |
| + | def render(context) |
| + | context.stack do |
| + | _response = nil |
| + | |
| + | begin |
| + | _response = Locomotive::Wagon::Httparty::Webservice.consume(@url, @options.symbolize_keys) |
| + | rescue Exception => e |
| + | _response = { 'error' => e.message.to_s }.to_liquid |
| + | end |
| + | |
| + | context.scopes.last[@target.to_s] = _response |
| + | |
| + | render_all(@nodelist, context) |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('consume', Consume) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/csrf.rb b/lib/locomotive/wagon/liquid/tags/csrf.rb
+34
-0
| @@ | @@ -0,0 +1,34 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | module Csrf |
| + | |
| + | class Param < ::Liquid::Tag |
| + | |
| + | def render(context) |
| + | %{<input type="hidden" name="authenticity_token" value="helloworld" />} |
| + | end |
| + | |
| + | end |
| + | |
| + | class Meta < ::Liquid::Tag |
| + | |
| + | def render(context) |
| + | %{ |
| + | <meta name="csrf-param" content="authenticity_token" /> |
| + | <meta name="csrf-token" content="helloworld" /> |
| + | } |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('csrf_param', Csrf::Param) |
| + | ::Liquid::Template.register_tag('csrf_meta', Csrf::Meta) |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/editable.rb b/lib/locomotive/wagon/liquid/tags/editable.rb
+5
-0
| @@ | @@ -0,0 +1,5 @@ |
| + | require 'locomotive/wagon/liquid/tags/editable/base' |
| + | require 'locomotive/wagon/liquid/tags/editable/short_text' |
| + | require 'locomotive/wagon/liquid/tags/editable/long_text' |
| + | require 'locomotive/wagon/liquid/tags/editable/file' |
| + | require 'locomotive/wagon/liquid/tags/editable/control' |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/editable/base.rb b/lib/locomotive/wagon/liquid/tags/editable/base.rb
+46
-0
| @@ | @@ -0,0 +1,46 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | module Editable |
| + | class Base < ::Liquid::Block |
| + | |
| + | Syntax = /(#{::Liquid::QuotedFragment})(\s*,\s*#{::Liquid::Expression}+)?/ |
| + | |
| + | def initialize(tag_name, markup, tokens, context) |
| + | if markup =~ Syntax |
| + | @slug = $1.gsub(/[\"\']/, '') |
| + | @options = {} |
| + | markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/^'/, '').gsub(/'$/, '') } |
| + | else |
| + | raise ::Liquid::SyntaxError.new("Syntax Error in 'editable_xxx' - Valid syntax: editable_xxx <slug>(, <options>)") |
| + | end |
| + | |
| + | super |
| + | end |
| + | |
| + | def render(context) |
| + | current_page = context.registers[:page] |
| + | |
| + | element = current_page.find_editable_element(context['block'].try(:name), @slug) |
| + | |
| + | if element.present? |
| + | render_element(context, element) |
| + | else |
| + | super |
| + | end |
| + | end |
| + | |
| + | protected |
| + | |
| + | def render_element(context, element) |
| + | element.content |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/editable/control.rb b/lib/locomotive/wagon/liquid/tags/editable/control.rb
+19
-0
| @@ | @@ -0,0 +1,19 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | module Editable |
| + | class Control < Base |
| + | |
| + | def render(context) |
| + | super |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('editable_control', Control) |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/editable/file.rb b/lib/locomotive/wagon/liquid/tags/editable/file.rb
+15
-0
| @@ | @@ -0,0 +1,15 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | module Editable |
| + | class File < Base |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('editable_file', File) |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/editable/long_text.rb b/lib/locomotive/wagon/liquid/tags/editable/long_text.rb
+15
-0
| @@ | @@ -0,0 +1,15 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | module Editable |
| + | class LongText < ShortText |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('editable_long_text', LongText) |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/editable/short_text.rb b/lib/locomotive/wagon/liquid/tags/editable/short_text.rb
+15
-0
| @@ | @@ -0,0 +1,15 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | module Editable |
| + | class ShortText < Base |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('editable_short_text', ShortText) |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/extends.rb b/lib/locomotive/wagon/liquid/tags/extends.rb
+25
-0
| @@ | @@ -0,0 +1,25 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | class Extends < ::Liquid::Extends |
| + | |
| + | def parse_parent_template |
| + | mounting_point = @context[:mounting_point] |
| + | |
| + | page = if @template_name == 'parent' |
| + | @context[:page].parent |
| + | else |
| + | mounting_point.pages[@template_name] |
| + | end |
| + | |
| + | ::Liquid::Template.parse(page.source, { mounting_point: mounting_point, page: page }) |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('extends', Extends) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/google_analytics.rb b/lib/locomotive/wagon/liquid/tags/google_analytics.rb
+28
-0
| @@ | @@ -0,0 +1,28 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | class GoogleAnalytics < ::Liquid::Tag |
| + | |
| + | Syntax = /(#{::Liquid::Expression}+)?/ |
| + | |
| + | def initialize(tag_name, markup, tokens, context) |
| + | if markup =~ Syntax |
| + | @account_id = $1.gsub('\'', '') |
| + | else |
| + | raise ::Liquid::SyntaxError.new("Syntax Error in 'google_analytics' - Valid syntax: google_analytics <account_id>") |
| + | end |
| + | |
| + | super |
| + | end |
| + | |
| + | def render(context) |
| + | "<!-- google analytics for #{@account_id} -->" |
| + | end |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('google_analytics', GoogleAnalytics) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/inline_editor.rb b/lib/locomotive/wagon/liquid/tags/inline_editor.rb
+16
-0
| @@ | @@ -0,0 +1,16 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | class InlineEditor < ::Liquid::Tag |
| + | |
| + | def render(context) |
| + | '' |
| + | end |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('inline_editor', InlineEditor) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/locale_switcher.rb b/lib/locomotive/wagon/liquid/tags/locale_switcher.rb
+180
-0
| @@ | @@ -0,0 +1,180 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | # Display the links to change the locale of the current page |
| + | # |
| + | # Usage: |
| + | # |
| + | # {% locale_switcher %} => <div id="locale-switcher"><a href="/features" class="current en">Features</a><a href="/fr/fonctionnalites" class="fr">Fonctionnalités</a></div> |
| + | # |
| + | # {% locale_switcher label: locale, sep: ' - ' } |
| + | # |
| + | # options: |
| + | # - label: iso (de, fr, en, ...etc), locale (Deutsch, Français, English, ...etc), title (page title) |
| + | # - sep: piece of html code separating 2 locales |
| + | # |
| + | # notes: |
| + | # - "iso" is the default choice for label |
| + | # - " | " is the default separating code |
| + | # |
| + | class LocaleSwitcher < ::Liquid::Tag |
| + | |
| + | Syntax = /(#{::Liquid::Expression}+)?/ |
| + | |
| + | def initialize(tag_name, markup, tokens, context) |
| + | @options = { label: 'iso', sep: ' | ' } |
| + | |
| + | if markup =~ Syntax |
| + | markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') } |
| + | |
| + | @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude] |
| + | else |
| + | raise ::Liquid::SyntaxError.new("Syntax Error in 'locale_switcher' - Valid syntax: locale_switcher <options>") |
| + | end |
| + | |
| + | super |
| + | end |
| + | |
| + | def render(context) |
| + | @site, @page = context.registers[:site], context.registers[:page] |
| + | @default_locale = context.registers[:mounting_point].default_locale |
| + | |
| + | output = %(<div id="locale-switcher">) |
| + | |
| + | output += @site.locales.collect do |locale| |
| + | Locomotive::Mounter.with_locale(locale) do |
| + | fullpath = localized_fullpath(locale) |
| + | |
| + | if @page.templatized? |
| + | permalink = context['entry']._permalink |
| + | |
| + | if permalink |
| + | fullpath.gsub!('*', permalink) |
| + | else |
| + | fullpath = '404' |
| + | end |
| + | end |
| + | |
| + | css = link_class(locale, context['locale']) |
| + | |
| + | %(<a href="/#{fullpath}" class="#{css}">#{link_label(locale)}</a>) |
| + | end |
| + | end.join(@options[:sep]) |
| + | |
| + | output += %(</div>) |
| + | end |
| + | |
| + | private |
| + | |
| + | def link_class(locale, current_locale) |
| + | css = [locale] |
| + | css << 'current' if locale.to_s == current_locale.to_s |
| + | css.join(' ') |
| + | end |
| + | |
| + | def link_label(locale) |
| + | case @options[:label] |
| + | when 'iso' then locale |
| + | when 'locale' then I18n.t("locomotive.locales.#{locale}", locale: locale) |
| + | when 'title' then @page.title # FIXME: this returns nil if the page has not been translated in the locale |
| + | else |
| + | locale |
| + | end |
| + | end |
| + | |
| + | def localized_fullpath(locale) |
| + | # @site.localized_page_fullpath(@page, locale) |
| + | |
| + | return nil if @page.fullpath_translations.blank? |
| + | |
| + | fullpath = @page.safe_fullpath || @page.fullpath_or_default |
| + | |
| + | if locale.to_s == @default_locale.to_s # no need to specify the locale |
| + | @page.index? ? '' : fullpath |
| + | elsif @page.index? # avoid /en/index or /fr/index, prefer /en or /fr instead |
| + | locale |
| + | else |
| + | File.join(locale, fullpath) |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('locale_switcher', LocaleSwitcher) |
| + | end |
| + | end |
| + | end |
| + | end |
| + | # module Locomotive |
| + | # module Wagon |
| + | # module Liquid |
| + | # module Tags |
| + | |
| + | # # Display the links to change the locale of the current page |
| + | # # |
| + | # # Usage: |
| + | # # |
| + | # # {% locale_switcher %} => <div id="locale-switcher"><a href="/features" class="current en">Features</a><a href="/fr/fonctionnalites" class="fr">Fonctionnalités</a></div> |
| + | # # |
| + | # # {% locale_switcher label: locale, sep: ' - ' } |
| + | # # |
| + | # # options: |
| + | # # - label: iso (de, fr, en, ...etc), locale (Deutsch, Français, English, ...etc), title (page title) |
| + | # # - sep: piece of html code separating 2 locales |
| + | # # |
| + | # # notes: |
| + | # # - "iso" is the default choice for label |
| + | # # - " | " is the default separating code |
| + | # # |
| + | # class LocaleSwitcher < ::Liquid::Tag |
| + | |
| + | # Syntax = /(#{::Liquid::Expression}+)?/ |
| + | |
| + | # def initialize(tag_name, markup, tokens, context) |
| + | # @options = { label: 'iso', sep: ' | ' } |
| + | |
| + | # if markup =~ Syntax |
| + | # markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') } |
| + | |
| + | # @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude] |
| + | # else |
| + | # raise ::Liquid::SyntaxError.new("Syntax Error in 'locale_switcher' - Valid syntax: locale_switcher <options>") |
| + | # end |
| + | |
| + | # super |
| + | # end |
| + | |
| + | # def render(context) |
| + | # @site, @page = context.registers[:site], context.registers[:page] |
| + | |
| + | # output = %(<div id="locale-switcher">) |
| + | |
| + | # output += @site.locales.collect do |locale| |
| + | # fullpath = locale.to_s == context['default_locale'].to_s ? '/' : locale |
| + | |
| + | # %(<a href="/#{fullpath}" class="#{locale} #{'current' if locale.to_s == context['default_locale'].to_s}">#{link_label(locale)}</a>) |
| + | # end.join(@options[:sep]) |
| + | |
| + | # output += %(</div>) |
| + | # end |
| + | |
| + | # private |
| + | |
| + | # def link_label(locale) |
| + | # case @options[:label] |
| + | # when :iso then locale |
| + | # when :locale then I18n.t("locomotive.locales.#{locale}", locale: locale) |
| + | # when :title then @page.title # FIXME: this returns nil if the page has not been translated in the locale |
| + | # else |
| + | # locale |
| + | # end |
| + | # end |
| + | |
| + | # end |
| + | |
| + | # ::Liquid::Template.register_tag('locale_switcher', LocaleSwitcher) |
| + | # end |
| + | # end |
| + | # end |
| + | # end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/nav.rb b/lib/locomotive/wagon/liquid/tags/nav.rb
+167
-0
| @@ | @@ -0,0 +1,167 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | # Display the children pages of the site, current page or the parent page. If not precised, nav is applied on the current page. |
| + | # The html output is based on the ul/li tags. |
| + | # |
| + | # Usage: |
| + | # |
| + | # {% nav site %} => <ul class="nav"><li class="on"><a href="/features">Features</a></li></ul> |
| + | # |
| + | # {% nav site, no_wrapper: true, exclude: 'contact|about', id: 'main-nav', class: 'nav', active_class: 'on' } |
| + | # |
| + | class Nav < ::Liquid::Tag |
| + | |
| + | Syntax = /(#{::Liquid::Expression}+)?/ |
| + | |
| + | attr_accessor :current_page, :mounting_point |
| + | |
| + | def initialize(tag_name, markup, tokens, context) |
| + | if markup =~ Syntax |
| + | @source = ($1 || 'page').gsub(/"|'/, '') |
| + | @options = { :id => 'nav', :class => '', :active_class => 'on', :bootstrap => false } |
| + | markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') } |
| + | |
| + | @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude] |
| + | |
| + | if @options[:snippet] |
| + | if template = self.parse_snippet_template(context, @options[:snippet]) |
| + | @options[:liquid_render] = template |
| + | end |
| + | end |
| + | else |
| + | raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <site|parent|page|<path to a page>> <options>") |
| + | end |
| + | |
| + | super |
| + | end |
| + | |
| + | def render(context) |
| + | self.set_accessors_from_context(context) |
| + | |
| + | children_output = [] |
| + | |
| + | entries = self.fetch_entries |
| + | |
| + | entries.each_with_index do |p, index| |
| + | css = [] |
| + | css << 'first' if index == 0 |
| + | css << 'last' if index == entries.size - 1 |
| + | |
| + | children_output << render_entry_link(p, css.join(' '), 1) |
| + | end |
| + | |
| + | output = children_output.join("\n") |
| + | |
| + | if @options[:no_wrapper] != 'true' |
| + | output = %{<ul id="#{@options[:id]}" class="#{@options[:class]}">\n#{output}</ul>} |
| + | end |
| + | |
| + | output |
| + | end |
| + | |
| + | protected |
| + | |
| + | def set_accessors_from_context(context) |
| + | self.current_page = context.registers[:page] |
| + | self.mounting_point = context.registers[:mounting_point] |
| + | end |
| + | |
| + | def parse_snippet_template(context, template_name) |
| + | source = if template_name.include?('{') |
| + | template_name |
| + | else |
| + | context[:mounting_point].snippets[template_name].try(:source) |
| + | end |
| + | |
| + | source ? ::Liquid::Template.parse(source) : nil |
| + | end |
| + | |
| + | def fetch_entries |
| + | children = (case @source |
| + | when 'site' then self.mounting_point.pages['index'] |
| + | when 'parent' then self.current_page.parent || self.current_page |
| + | when 'page' then self.current_page |
| + | else |
| + | self.mounting_point.pages[@source] |
| + | end).children.clone |
| + | |
| + | children.delete_if { |p| !include_page?(p) } |
| + | end |
| + | |
| + | # Determines whether or not a page should be a part of the menu |
| + | def include_page?(page) |
| + | if !page.listed? || page.templatized? || !page.published? |
| + | false |
| + | elsif @options[:exclude] |
| + | (page.fullpath =~ @options[:exclude]).nil? |
| + | else |
| + | true |
| + | end |
| + | end |
| + | |
| + | # Returns a list element, a link to the page and its children |
| + | def render_entry_link(page, css, depth) |
| + | selected = self.current_page.fullpath =~ /^#{page.fullpath}/ ? " #{@options[:active_class]}" : '' |
| + | |
| + | icon = @options[:icon] ? '<span></span>' : '' |
| + | |
| + | title = @options[:liquid_render] ? @options[:liquid_render].render('page' => page) : page.title |
| + | |
| + | label = %{#{icon if @options[:icon] != 'after' }#{title}#{icon if @options[:icon] == 'after' }} |
| + | |
| + | dropdow = "" |
| + | link_options = "" |
| + | href = ::I18n.locale.to_s == self.mounting_point.default_locale.to_s ? "/#{page.fullpath}" : "/#{::I18n.locale}/#{page.fullpath}" |
| + | caret = "" |
| + | |
| + | if render_children_for_page?(page, depth) && bootstrap? |
| + | dropdow = "dropdown" |
| + | link_options = %{class="dropdown-toggle" data-toggle="dropdown"} |
| + | href = "#" |
| + | caret = %{<b class="caret"></b>} |
| + | end |
| + | |
| + | output = %{<li id="#{page.slug.to_s.dasherize}-link" class="link#{selected} #{css} #{dropdow}">} |
| + | output << %{<a href="#{href}" #{link_options}>#{label} #{caret}</a>} |
| + | output << render_entry_children(page, depth.succ) if (depth.succ <= @options[:depth].to_i) |
| + | output << %{</li>} |
| + | |
| + | output.strip |
| + | end |
| + | |
| + | def render_children_for_page?(page, depth) |
| + | depth.succ <= @options[:depth].to_i && page.children.reject { |c| !include_page?(c) }.any? |
| + | end |
| + | |
| + | # Recursively creates a nested unordered list for the depth specified |
| + | def render_entry_children(page, depth) |
| + | output = %{} |
| + | |
| + | children = page.children.reject { |c| !include_page?(c) } |
| + | if children.present? |
| + | output = %{<ul id="#{@options[:id]}-#{page.slug.to_s.dasherize}" class="#{bootstrap? ? "dropdown-menu" : ""}">} |
| + | children.each do |c, page| |
| + | css = [] |
| + | css << 'first' if children.first == c |
| + | css << 'last' if children.last == c |
| + | |
| + | output << render_entry_link(c, css.join(' '),depth) |
| + | end |
| + | output << %{</ul>} |
| + | end |
| + | |
| + | output |
| + | end |
| + | |
| + | def bootstrap? |
| + | @options[:bootstrap] == 'true' || @options[:bootstrap] == true |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('nav', Nav) |
| + | end |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/paginate.rb b/lib/locomotive/wagon/liquid/tags/paginate.rb
+105
-0
| @@ | @@ -0,0 +1,105 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | |
| + | # Paginate a collection |
| + | # |
| + | # Usage: |
| + | # |
| + | # {% paginate contents.projects by 5 %} |
| + | # {% for project in paginate.collection %} |
| + | # {{ project.name }} |
| + | # {% endfor %} |
| + | # {% endpaginate %} |
| + | # |
| + | |
| + | class Paginate < ::Liquid::Block |
| + | |
| + | Syntax = /(#{::Liquid::Expression}+)\s+by\s+([0-9]+)/ |
| + | |
| + | def initialize(tag_name, markup, tokens, context) |
| + | if markup =~ Syntax |
| + | @collection_name = $1 |
| + | @per_page = $2.to_i |
| + | else |
| + | raise ::Liquid::SyntaxError.new("Syntax Error in 'paginate' - Valid syntax: paginate <collection> by <number>") |
| + | end |
| + | |
| + | super |
| + | end |
| + | |
| + | def render(context) |
| + | context.stack do |
| + | collection = context[@collection_name] |
| + | |
| + | raise ::Liquid::ArgumentError.new("Cannot paginate array '#{@collection_name}'. Not found.") if collection.nil? |
| + | |
| + | pagination = collection.send(:paginate, { |
| + | :page => context['current_page'], |
| + | :per_page => @per_page }).stringify_keys! |
| + | |
| + | page_count, current_page = pagination['total_pages'], pagination['current_page'] |
| + | |
| + | path = sanitize_path(context['fullpath']) |
| + | |
| + | pagination['previous'] = link(I18n.t('pagination.previous'), current_page - 1, path) if pagination['previous_page'] |
| + | pagination['next'] = link(I18n.t('pagination.next'), current_page + 1, path) if pagination['next_page'] |
| + | pagination['parts'] = [] |
| + | |
| + | hellip_break = false |
| + | |
| + | if page_count > 1 |
| + | 1.upto(page_count) do |page| |
| + | if current_page == page |
| + | pagination['parts'] << no_link(page) |
| + | elsif page == 1 |
| + | pagination['parts'] << link(page, page, path) |
| + | elsif page == page_count - 1 |
| + | pagination['parts'] << link(page, page, path) |
| + | elsif page <= current_page - window_size or page >= current_page + window_size |
| + | next if hellip_break |
| + | pagination['parts'] << no_link('…') |
| + | hellip_break = true |
| + | next |
| + | else |
| + | pagination['parts'] << link(page, page, path) |
| + | end |
| + | |
| + | hellip_break = false |
| + | end |
| + | end |
| + | |
| + | context['paginate'] = pagination |
| + | |
| + | render_all(@nodelist, context) |
| + | end |
| + | end |
| + | |
| + | private |
| + | |
| + | def sanitize_path(path) |
| + | _path = path.gsub(/page=[0-9]+&?/, '').gsub(/_pjax=true&?/, '') |
| + | _path = _path.slice(0..-2) if _path.last == '?' || _path.last == '&' |
| + | _path |
| + | end |
| + | |
| + | def window_size |
| + | 3 |
| + | end |
| + | |
| + | def no_link(title) |
| + | { 'title' => title, 'is_link' => false, 'hellip_break' => title == '…' } |
| + | end |
| + | |
| + | def link(title, page, path) |
| + | _path = %(#{path}#{path.include?('?') ? '&' : '?'}page=#{page}) |
| + | { 'title' => title, 'url' => _path, 'is_link' => true } |
| + | end |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('paginate', Paginate) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/seo.rb b/lib/locomotive/wagon/liquid/tags/seo.rb
+74
-0
| @@ | @@ -0,0 +1,74 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | module SEO |
| + | |
| + | class Base < ::Liquid::Tag |
| + | |
| + | def render(context) |
| + | %{ |
| + | #{self.render_title(context)} |
| + | #{self.render_metadata(context)} |
| + | } |
| + | end |
| + | |
| + | protected |
| + | |
| + | def render_title(context) |
| + | title = self.value_for(:seo_title, context) |
| + | title = context.registers[:site].name if title.blank? |
| + | |
| + | %{ |
| + | <title>#{title}</title> |
| + | } |
| + | end |
| + | |
| + | def render_metadata(context) |
| + | %{ |
| + | <meta name="description" content="#{self.value_for(:meta_description, context)}" /> |
| + | <meta name="keywords" content="#{self.value_for(:meta_keywords, context)}" /> |
| + | } |
| + | end |
| + | |
| + | # Removes whitespace and quote characters from the input |
| + | def sanitized_string(string) |
| + | string ? string.strip.gsub(/"/, '') : '' |
| + | end |
| + | |
| + | def value_for(attribute, context) |
| + | object = self.metadata_object(context) |
| + | value = object.try(attribute.to_sym).blank? ? context.registers[:site].send(attribute.to_sym) : object.send(attribute.to_sym) |
| + | self.sanitized_string(value) |
| + | end |
| + | |
| + | def metadata_object(context) |
| + | context['content_instance'] || context['page'] |
| + | end |
| + | end |
| + | |
| + | class Title < Base |
| + | |
| + | def render(context) |
| + | self.render_title(context) |
| + | end |
| + | |
| + | end |
| + | |
| + | class Metadata < Base |
| + | |
| + | def render(context) |
| + | self.render_metadata(context) |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('seo', SEO::Base) |
| + | ::Liquid::Template.register_tag('seo_title', SEO::Title) |
| + | ::Liquid::Template.register_tag('seo_metadata', SEO::Metadata) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/snippet.rb b/lib/locomotive/wagon/liquid/tags/snippet.rb
+44
-0
| @@ | @@ -0,0 +1,44 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | |
| + | class Snippet < ::Liquid::Include |
| + | |
| + | def render(context) |
| + | name = @template_name.gsub(/[\"\']/, '') |
| + | |
| + | source = context.registers[:mounting_point].snippets[name].try(:source) |
| + | |
| + | Locomotive::Wagon::Logger.info " Rendered snippet #{name}" |
| + | |
| + | partial = ::Liquid::Template.parse(source) |
| + | |
| + | variable = context[@variable_name || @template_name[1..-2]] |
| + | |
| + | context.stack do |
| + | @attributes.each do |key, value| |
| + | context[key] = context[value] |
| + | end |
| + | |
| + | output = (if variable.is_a?(Array) |
| + | variable.collect do |variable| |
| + | context[@template_name[1..-2]] = variable |
| + | partial.render(context) |
| + | end |
| + | else |
| + | context[@template_name[1..-2]] = variable |
| + | partial.render(context) |
| + | end) |
| + | |
| + | output |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('include', Snippet) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/liquid/tags/with_scope.rb b/lib/locomotive/wagon/liquid/tags/with_scope.rb
+43
-0
| @@ | @@ -0,0 +1,43 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | module Liquid |
| + | module Tags |
| + | class WithScope < ::Liquid::Block |
| + | |
| + | def initialize(tag_name, markup, tokens, context) |
| + | @options = {} |
| + | |
| + | markup.scan(::Liquid::TagAttributes) do |key, value| |
| + | @options[key] = value |
| + | end |
| + | |
| + | super |
| + | end |
| + | |
| + | def render(context) |
| + | context.stack do |
| + | context['with_scope'] = decode(@options, context) |
| + | render_all(@nodelist, context) |
| + | end |
| + | end |
| + | |
| + | private |
| + | |
| + | def decode(attributes, context) |
| + | attributes.each_pair do |key, value| |
| + | attributes[key] = (case value |
| + | when /^true|false$/i then value == 'true' |
| + | when /^[0-9]+$/ then value.to_i |
| + | when /^["|'](.+)["|']$/ then $1.gsub(/^["|']/, '').gsub(/["|']$/, '') |
| + | else |
| + | context[value] || value |
| + | end) |
| + | end |
| + | end |
| + | end |
| + | |
| + | ::Liquid::Template.register_tag('with_scope', WithScope) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/listen.rb b/lib/locomotive/wagon/listen.rb
+57
-0
| @@ | @@ -0,0 +1,57 @@ |
| + | require 'listen' |
| + | |
| + | module Locomotive::Wagon |
| + | class Listen |
| + | |
| + | attr_accessor :reader |
| + | |
| + | def self.instance |
| + | @@instance = new |
| + | end |
| + | |
| + | def start(reader) |
| + | self.reader = reader |
| + | |
| + | self.definitions.each do |definition| |
| + | self.apply(definition) |
| + | end |
| + | end |
| + | |
| + | def definitions |
| + | [ |
| + | ['config', /\.yml/, [:site, :content_types, :pages, :snippets, :content_entries, :translations]], |
| + | ['app/views', /\.liquid/, [:pages, :snippets]], |
| + | ['app/content_types', /\.yml/, [:content_types, :content_entries]], |
| + | ['data', /\.yml/, :content_entries] |
| + | ] |
| + | end |
| + | |
| + | protected |
| + | |
| + | def apply(definition) |
| + | reloader = Proc.new do |modified, added, removed| |
| + | resources = [*definition.last] |
| + | names = resources.map { |n| "\"#{n}\"" }.join(', ') |
| + | |
| + | Locomotive::Wagon::Logger.info "* Reloaded #{names} at #{Time.now}" |
| + | |
| + | begin |
| + | reader.reload(resources) |
| + | rescue Exception => e |
| + | Locomotive::Wagon::MounterException.new('Unable to reload', e) |
| + | end |
| + | end |
| + | |
| + | filter = definition[1] |
| + | path = File.join(self.reader.mounting_point.path, definition.first) |
| + | path = File.expand_path(path) |
| + | |
| + | listener = ::Listen.to(path).filter(filter).change(&reloader) |
| + | |
| + | # non blocking listener |
| + | listener.start(false) |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/logger.rb b/lib/locomotive/wagon/logger.rb
+54
-0
| @@ | @@ -0,0 +1,54 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | |
| + | class Logger |
| + | |
| + | attr_accessor :logger, :logfile_path, :stdout |
| + | |
| + | def initialize |
| + | self.logger = nil |
| + | end |
| + | |
| + | # Setup the single instance of the ruby logger. |
| + | # |
| + | # @param [ String ] path The path to the log file (default: log/wagon.log) |
| + | # @param [ Boolean ] stdout Instead of having a file, log to the standard output |
| + | # |
| + | def setup(path, stdout = false) |
| + | require 'logger' |
| + | |
| + | self.stdout = stdout |
| + | |
| + | self.logfile_path = File.expand_path(File.join(path, 'log', 'wagon.log')) |
| + | FileUtils.mkdir_p(File.dirname(logfile_path)) |
| + | |
| + | out = self.stdout ? STDOUT : self.logfile_path |
| + | |
| + | self.logger = ::Logger.new(out).tap do |log| |
| + | log.level = ::Logger::DEBUG |
| + | log.formatter = proc do |severity, datetime, progname, msg| |
| + | "#{msg}\n" |
| + | end |
| + | end |
| + | end |
| + | |
| + | def self.instance |
| + | @@instance ||= self.new |
| + | end |
| + | |
| + | def self.setup(path, stdout = false) |
| + | self.instance.setup(path, stdout) |
| + | end |
| + | |
| + | class << self |
| + | %w(debug info warn error fatal unknown).each do |name| |
| + | define_method(name) do |message| |
| + | self.instance.logger.send(name.to_sym, message) |
| + | end |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/misc.rb b/lib/locomotive/wagon/misc.rb
+5
-0
| @@ | @@ -0,0 +1,5 @@ |
| + | require 'locomotive/wagon/misc/core_ext.rb' |
| + | require 'locomotive/wagon/misc/will_paginate.rb' |
| + | require 'locomotive/wagon/misc/httparty.rb' |
| + | require 'locomotive/wagon/misc/dragonfly.rb' |
| + | require 'locomotive/wagon/misc/i18n.rb' |
| \ No newline at end of file | |
locomotive/wagon/misc/core_ext.rb b/lib/locomotive/wagon/misc/core_ext.rb
+29
-0
| @@ | @@ -0,0 +1,29 @@ |
| + | unless Hash.instance_methods.include?(:underscore_keys) |
| + | class Hash |
| + | |
| + | def underscore_keys |
| + | new_hash = {} |
| + | |
| + | self.each_pair do |key, value| |
| + | if value.respond_to?(:collect!) # Array |
| + | value.collect do |item| |
| + | if item.respond_to?(:each_pair) # Hash item within |
| + | item.underscore_keys |
| + | else |
| + | item |
| + | end |
| + | end |
| + | elsif value.respond_to?(:each_pair) # Hash |
| + | value = value.underscore_keys |
| + | end |
| + | |
| + | new_key = key.is_a?(String) ? key.underscore : key # only String keys |
| + | |
| + | new_hash[new_key] = value |
| + | end |
| + | |
| + | self.replace(new_hash) |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/misc/dragonfly.rb b/lib/locomotive/wagon/misc/dragonfly.rb
+79
-0
| @@ | @@ -0,0 +1,79 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | class Dragonfly |
| + | |
| + | attr_accessor :path, :enabled |
| + | |
| + | def enabled? |
| + | !!self.enabled |
| + | end |
| + | |
| + | def resize_url(source, resize_string) |
| + | _source = (case source |
| + | when String then source |
| + | when Hash then source['url'] || source[:url] |
| + | else |
| + | source.try(:url) |
| + | end) |
| + | |
| + | if _source.blank? |
| + | LocomotiveEditor::Logger.error "Unable to resize on the fly: #{source.inspect}" |
| + | return |
| + | end |
| + | |
| + | return _source unless self.enabled? |
| + | |
| + | if _source =~ /^http/ |
| + | file = self.class.app.fetch_url(_source) |
| + | else |
| + | file = self.class.app.fetch_file(File.join(self.path, 'public', _source)) |
| + | end |
| + | |
| + | file.process(:thumb, resize_string).url |
| + | end |
| + | |
| + | def self.app |
| + | ::Dragonfly[:images] |
| + | end |
| + | |
| + | |
| + | def self.instance |
| + | @@instance ||= new |
| + | end |
| + | |
| + | def self.setup!(path) |
| + | self.instance.path = path |
| + | self.instance.enabled = false |
| + | |
| + | begin |
| + | require 'rack/cache' |
| + | require 'RMagick' |
| + | require 'dragonfly' |
| + | |
| + | ## initialize Dragonfly ## |
| + | app = ::Dragonfly[:images].configure_with(:imagemagick) |
| + | |
| + | ## configure it ## |
| + | ::Dragonfly[:images].configure do |c| |
| + | convert = `which convert`.strip.presence || '/usr/local/bin/convert' |
| + | c.convert_command = convert |
| + | c.identify_command = convert |
| + | |
| + | c.allow_fetch_url = true |
| + | c.allow_fetch_file = true |
| + | |
| + | c.url_format = '/images/dynamic/:job/:basename.:format' |
| + | end |
| + | |
| + | self.instance.enabled = true |
| + | rescue Exception => e |
| + | Locomotive::Wagon::Logger.warn %{ |
| + | [Dragonfly] !disabled! |
| + | [Dragonfly] If you want to take full benefits of all the features in the LocomotiveWagon, we recommend you to install ImageMagick and RMagick. Check out the documentation here: http://doc.locomotivecms.com/editor/installation. |
| + | } |
| + | end |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/misc/httparty.rb b/lib/locomotive/wagon/misc/httparty.rb
+46
-0
| @@ | @@ -0,0 +1,46 @@ |
| + | require 'uri' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | module Httparty |
| + | class Webservice |
| + | |
| + | include ::HTTParty |
| + | |
| + | def self.consume(url, options = {}) |
| + | url = ::HTTParty.normalize_base_uri(url) |
| + | |
| + | uri = URI.parse(url) |
| + | options[:base_uri] = "#{uri.scheme}://#{uri.host}" |
| + | options[:base_uri] += ":#{uri.port}" if uri.port != 80 |
| + | path = uri.request_uri |
| + | |
| + | options.delete(:format) if options[:format] == 'default' |
| + | |
| + | username, password = options.delete(:username), options.delete(:password) |
| + | options[:basic_auth] = { username: username, password: password } if username |
| + | |
| + | path ||= '/' |
| + | |
| + | # Locomotive::Wagon::Logger.debug "[WebService] consuming #{path}, #{options.inspect}" |
| + | |
| + | response = self.get(path, options) |
| + | |
| + | if response.code == 200 |
| + | _response = response.parsed_response |
| + | if _response.respond_to?(:underscore_keys) |
| + | _response.underscore_keys |
| + | else |
| + | _response.collect(&:underscore_keys) |
| + | end |
| + | else |
| + | Locomotive::Wagon::Logger.error "[WebService] consumed #{path}, #{options.inspect}, response = #{response.inspect}" |
| + | nil |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/misc/i18n.rb b/lib/locomotive/wagon/misc/i18n.rb
+2
-0
| @@ | @@ -0,0 +1,2 @@ |
| + | I18n.load_path = Dir[File.join(File.dirname(__FILE__), "/../../../../locales/*.yml")] |
| + | I18n.backend.reload! |
| \ No newline at end of file | |
locomotive/wagon/misc/will_paginate.rb b/lib/locomotive/wagon/misc/will_paginate.rb
+16
-0
| @@ | @@ -0,0 +1,16 @@ |
| + | require 'will_paginate' |
| + | require 'will_paginate/collection' |
| + | |
| + | Array.class_eval do |
| + | def paginate(options = {}) |
| + | raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options |
| + | |
| + | WillPaginate::Collection.create( |
| + | options[:page] || 1, |
| + | options[:per_page] || 30, |
| + | options[:total_entries] || self.length |
| + | ) { |pager| |
| + | pager.replace self[pager.offset, pager.per_page].to_a |
| + | } |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server.rb b/lib/locomotive/wagon/server.rb
+81
-0
| @@ | @@ -0,0 +1,81 @@ |
| + | require 'rack/showexceptions' |
| + | require 'coffee_script' |
| + | |
| + | require 'locomotive/wagon/listen' |
| + | require 'locomotive/wagon/server/middleware' |
| + | require 'locomotive/wagon/server/favicon' |
| + | require 'locomotive/wagon/server/dynamic_assets' |
| + | require 'locomotive/wagon/server/logging' |
| + | require 'locomotive/wagon/server/entry_submission' |
| + | require 'locomotive/wagon/server/path' |
| + | require 'locomotive/wagon/server/locale' |
| + | require 'locomotive/wagon/server/page' |
| + | require 'locomotive/wagon/server/templatized_page' |
| + | require 'locomotive/wagon/server/not_found' |
| + | require 'locomotive/wagon/server/renderer' |
| + | |
| + | require 'locomotive/wagon/liquid' |
| + | require 'locomotive/wagon/misc' |
| + | |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | def initialize(reader, options = {}) |
| + | Locomotive::Wagon::Dragonfly.setup!(reader.mounting_point.path) |
| + | |
| + | @reader = reader |
| + | @app = self.create_rack_app(@reader) |
| + | |
| + | unless options[:disable_listen] |
| + | Locomotive::Wagon::Listen.instance.start(@reader) |
| + | end |
| + | end |
| + | |
| + | def call(env) |
| + | env['wagon.mounting_point'] = @reader.mounting_point |
| + | @app.call(env) |
| + | end |
| + | |
| + | protected |
| + | |
| + | def create_rack_app(reader) |
| + | Rack::Builder.new do |
| + | use Rack::ShowExceptions |
| + | use Rack::Lint |
| + | |
| + | use Rack::Session::Cookie, { |
| + | key: 'rack.session', |
| + | domain: '0.0.0.0', |
| + | path: '/', |
| + | expire_after: 2592000, |
| + | secret: 'uselessinlocal' |
| + | } |
| + | |
| + | use ::Dragonfly::Middleware, :images |
| + | |
| + | use Rack::Static, { |
| + | urls: ['/images', '/fonts', '/samples'], |
| + | root: File.join(reader.mounting_point.path, 'public') |
| + | } |
| + | |
| + | use Favicon |
| + | use DynamicAssets |
| + | |
| + | use Logging |
| + | |
| + | use EntrySubmission |
| + | |
| + | use Path |
| + | use Locale |
| + | |
| + | use Page |
| + | use TemplatizedPage |
| + | use NotFound |
| + | use Renderer |
| + | |
| + | run Renderer.new |
| + | end |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/dynamic_assets.rb b/lib/locomotive/wagon/server/dynamic_assets.rb
+31
-0
| @@ | @@ -0,0 +1,31 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | class DynamicAssets < Middleware |
| + | |
| + | def call(env) |
| + | self.set_accessors(env) |
| + | |
| + | path = env['PATH_INFO'] |
| + | |
| + | if path =~ /^\/(stylesheets|javascripts)\// |
| + | |
| + | mime_type = MIME::Types.type_for(path).first.try(:to_s) || 'text/plain' |
| + | asset = self.mounting_point.theme_assets.detect do |_asset| |
| + | _asset.path == path |
| + | end |
| + | |
| + | if asset |
| + | [200, { 'Content-Type' => mime_type }, [asset.content!]] |
| + | else |
| + | [404, { 'Content-Type' => mime_type }, ['Asset not found']] |
| + | end |
| + | else |
| + | app.call(env) |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/entry_submission.rb b/lib/locomotive/wagon/server/entry_submission.rb
+116
-0
| @@ | @@ -0,0 +1,116 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | # Mimic the submission of a content entry |
| + | # |
| + | class EntrySubmission < Middleware |
| + | |
| + | def call(env) |
| + | self.set_accessors(env) |
| + | |
| + | if self.request.post? && env['PATH_INFO'] =~ /^\/entry_submissions\/(.*)/ |
| + | self.process_form($1) |
| + | |
| + | if @entry.valid? |
| + | if self.html? |
| + | self.record_submitted_entry |
| + | self.redirect_to self.callback_url |
| + | elsif self.json? |
| + | self.json_response |
| + | end |
| + | else |
| + | if self.html? |
| + | if self.callback_url =~ /^http:\/\// |
| + | self.redirect_to self.callback_url |
| + | else |
| + | env['PATH_INFO'] = self.callback_url |
| + | self.liquid_assigns[@content_type.slug.singularize] = @entry |
| + | app.call(env) |
| + | end |
| + | elsif self.json? |
| + | self.json_response(422) |
| + | end |
| + | end |
| + | else |
| + | self.fetch_submitted_entry |
| + | |
| + | app.call(env) |
| + | end |
| + | end |
| + | |
| + | protected |
| + | |
| + | def record_submitted_entry |
| + | self.request.session[:now] ||= {} |
| + | self.request.session[:now][:submitted_entry] = [@content_type.slug, @entry._slug] |
| + | end |
| + | |
| + | def fetch_submitted_entry |
| + | if data = self.request.session[:now].try(:delete, :submitted_entry) |
| + | content_type = self.mounting_point.content_types[data.first.to_s] |
| + | |
| + | entry = (content_type.entries || []).detect { |e| e._slug == data.last } |
| + | |
| + | # do not keep track of the entry |
| + | content_type.entries.delete(entry) if entry |
| + | |
| + | # add it to the additional liquid assigns for the next liquid rendering |
| + | if entry |
| + | self.liquid_assigns[content_type.slug.singularize] = entry |
| + | end |
| + | end |
| + | end |
| + | |
| + | # Mimic the creation of a content entry with a minimal validation. |
| + | # |
| + | # @param [ String ] permalink The permalink (or slug) of the content type |
| + | # |
| + | # |
| + | def process_form(permalink) |
| + | permalink = permalink.split('.').first |
| + | |
| + | @content_type = self.mounting_point.content_types[permalink] |
| + | |
| + | raise "Unknown content type '#{@content_type.inspect}'" if @content_type.nil? |
| + | |
| + | @entry = @content_type.build_entry(self.params[:entry] || self.params[:content]) |
| + | |
| + | # if not valid, we do not need to keep track of the entry |
| + | @content_type.entries.delete(@entry) if !@entry.valid? |
| + | end |
| + | |
| + | def callback_url |
| + | (@entry.valid? ? params[:success_callback] : params[:error_callback]) || '/' |
| + | end |
| + | |
| + | # Build the JSON response |
| + | # |
| + | # @param [ Integer ] status The HTTP return code |
| + | # |
| + | # @return [ Array ] The rack response depending on the validation status and the requested format |
| + | # |
| + | def json_response(status = 200) |
| + | locale = self.mounting_point.default_locale |
| + | |
| + | if self.request.path =~ /^\/(#{self.mounting_point.locales.join('|')})+(\/|$)/ |
| + | locale = $1 |
| + | end |
| + | |
| + | hash = @entry.to_hash(false).tap do |_hash| |
| + | if !@entry.valid? |
| + | _hash['errors'] = @entry.errors.inject({}) do |memo, name| |
| + | memo[name] = ::I18n.t('errors.messages.blank', locale: locale) |
| + | memo |
| + | end |
| + | end |
| + | end |
| + | |
| + | [status, { 'Content-Type' => 'application/json' }, [ |
| + | { @content_type.slug.singularize => hash }.to_json |
| + | ]] |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/favicon.rb b/lib/locomotive/wagon/server/favicon.rb
+17
-0
| @@ | @@ -0,0 +1,17 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | class Favicon < Middleware |
| + | |
| + | def call(env) |
| + | if env['PATH_INFO'] == '/favicon.ico' |
| + | [200, { 'Content-Type' => 'image/vnd.microsoft.icon' }, ['']] |
| + | else |
| + | app.call(env) |
| + | end |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/locale.rb b/lib/locomotive/wagon/server/locale.rb
+42
-0
| @@ | @@ -0,0 +1,42 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | # Set the locale from the path if possible or use the default one |
| + | # Examples: |
| + | # /fr/index => locale = :fr |
| + | # /fr/ => locale = :fr |
| + | # /index => locale = :en (default one) |
| + | # |
| + | class Locale < Middleware |
| + | |
| + | def call(env) |
| + | self.set_accessors(env) |
| + | |
| + | self.set_locale!(env) |
| + | |
| + | app.call(env) |
| + | end |
| + | |
| + | protected |
| + | |
| + | def set_locale!(env) |
| + | locale = self.mounting_point.default_locale |
| + | |
| + | if self.path =~ /^(#{self.mounting_point.locales.join('|')})+(\/|$)/ |
| + | locale = $1 |
| + | self.path = self.path.gsub($1 + $2, '') |
| + | self.path = 'index' if self.path.blank? |
| + | end |
| + | |
| + | Locomotive::Mounter.locale = locale |
| + | ::I18n.locale = locale |
| + | |
| + | self.log "Detecting locale #{locale.upcase}" |
| + | |
| + | env['wagon.locale'] = locale |
| + | env['wagon.path'] = self.path |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/logging.rb b/lib/locomotive/wagon/server/logging.rb
+32
-0
| @@ | @@ -0,0 +1,32 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | # Track the request into the current logger |
| + | # |
| + | class Logging < Middleware |
| + | |
| + | def call(env) |
| + | now = Time.now |
| + | |
| + | log "Started #{env['REQUEST_METHOD'].upcase} \"#{env['PATH_INFO']}\" at #{now}" |
| + | |
| + | app.call(env).tap do |response| |
| + | done_in_ms = (Time.now - now) * 1000 |
| + | log "Completed #{code_to_human(response.first)} in #{done_in_ms}ms\n\n" |
| + | end |
| + | end |
| + | |
| + | protected |
| + | |
| + | def code_to_human(code) |
| + | case code.to_i |
| + | when 200 then '200 OK' |
| + | when 301 then '301 Found' |
| + | when 302 then '302 Found' |
| + | when 404 then '404 Not Found' |
| + | end |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/middleware.rb b/lib/locomotive/wagon/server/middleware.rb
+59
-0
| @@ | @@ -0,0 +1,59 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | class Middleware |
| + | |
| + | attr_accessor :app, :request, :path, :liquid_assigns |
| + | |
| + | attr_accessor :mounting_point, :page, :content_entry |
| + | |
| + | def initialize(app = nil) |
| + | @app = app |
| + | end |
| + | |
| + | def call(env) |
| + | app.call(env) |
| + | end |
| + | |
| + | protected |
| + | |
| + | def set_accessors(env) |
| + | self.path = env['wagon.path'] |
| + | self.request = Rack::Request.new(env) |
| + | self.mounting_point = env['wagon.mounting_point'] |
| + | self.page = env['wagon.page'] |
| + | self.content_entry = env['wagon.content_entry'] |
| + | |
| + | env['wagon.liquid_assigns'] ||= {} |
| + | self.liquid_assigns = env['wagon.liquid_assigns'] |
| + | end |
| + | |
| + | def site |
| + | self.mounting_point.site |
| + | end |
| + | |
| + | def params |
| + | self.request.params.deep_symbolize_keys |
| + | end |
| + | |
| + | def html? |
| + | self.request.media_type == 'text/html' || !self.request.xhr? |
| + | end |
| + | |
| + | def json? |
| + | self.request.content_type == 'application/json' || File.extname(self.request.path) == '.json' |
| + | end |
| + | |
| + | def redirect_to(location, type = 301) |
| + | self.log "Redirected to #{location}" |
| + | [type, { 'Content-Type' => 'text/html', 'Location' => location }, []] |
| + | end |
| + | |
| + | def log(msg) |
| + | Locomotive::Wagon::Logger.info msg |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/not_found.rb b/lib/locomotive/wagon/server/not_found.rb
+19
-0
| @@ | @@ -0,0 +1,19 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | class NotFound < Middleware |
| + | |
| + | def call(env) |
| + | self.set_accessors(env) |
| + | |
| + | if self.page.nil? |
| + | self.log "Page not found" |
| + | env['wagon.page'] = self.mounting_point.pages['404'] |
| + | end |
| + | |
| + | app.call(env) |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/page.rb b/lib/locomotive/wagon/server/page.rb
+61
-0
| @@ | @@ -0,0 +1,61 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | # Sanitize the path from the previous middleware in order |
| + | # to make it work for the renderer. |
| + | # |
| + | class Page < Middleware |
| + | |
| + | def call(env) |
| + | self.set_accessors(env) |
| + | |
| + | self.set_page!(env) |
| + | |
| + | app.call(env) |
| + | end |
| + | |
| + | protected |
| + | |
| + | def set_page!(env) |
| + | page = self.fetch_page |
| + | |
| + | if page |
| + | self.log "Found page \"#{page.title}\" [/#{page.inspect}]" |
| + | end |
| + | |
| + | env['wagon.page'] = page |
| + | end |
| + | |
| + | def fetch_page |
| + | matchers = self.path_combinations(self.path) |
| + | |
| + | self.mounting_point.pages.values.detect do |_page| |
| + | matchers.include?(_page.safe_fullpath) || |
| + | matchers.include?(_page.safe_fullpath.try(:underscore)) |
| + | end |
| + | end |
| + | |
| + | def path_combinations(path) |
| + | self._path_combinations(path.split('/')) |
| + | end |
| + | |
| + | def _path_combinations(segments, can_include_template = true) |
| + | return nil if segments.empty? |
| + | |
| + | segment = segments.shift |
| + | |
| + | (can_include_template ? [segment, '*'] : [segment]).map do |_segment| |
| + | if (_combinations = _path_combinations(segments.clone, can_include_template && _segment != '*')) |
| + | [*_combinations].map do |_combination| |
| + | File.join(_segment, _combination) |
| + | end |
| + | else |
| + | [_segment] |
| + | end |
| + | end.flatten |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/path.rb b/lib/locomotive/wagon/server/path.rb
+34
-0
| @@ | @@ -0,0 +1,34 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | # Sanitize the path from the previous middleware in order |
| + | # to make it work for the renderer. |
| + | # |
| + | class Path < Middleware |
| + | |
| + | def call(env) |
| + | self.set_accessors(env) |
| + | |
| + | self.set_path!(env) |
| + | |
| + | app.call(env) |
| + | end |
| + | |
| + | protected |
| + | |
| + | def set_path!(env) |
| + | path = env['PATH_INFO'].clone |
| + | |
| + | path.gsub!(/\.[a-zA-Z][a-zA-Z0-9]{2,}$/, '') |
| + | path.gsub!(/^\//, '') |
| + | path.gsub!(/^[A-Z]:\//, '') |
| + | |
| + | path = 'index' if path.blank? |
| + | |
| + | env['wagon.path'] = path |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/renderer.rb b/lib/locomotive/wagon/server/renderer.rb
+105
-0
| @@ | @@ -0,0 +1,105 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | class Renderer < Middleware |
| + | |
| + | def call(env) |
| + | self.set_accessors(env) |
| + | |
| + | if self.page |
| + | if self.page.redirect? |
| + | self.redirect_to(self.page.redirect_url, self.page.redirect_type) |
| + | else |
| + | type = self.page.response_type || 'text/html' |
| + | html = self.render |
| + | |
| + | self.log " Rendered liquid page template" |
| + | |
| + | [200, { 'Content-Type' => type }, [html]] |
| + | end |
| + | else |
| + | # no page at all, even not the 404 page |
| + | [404, { 'Content-Type' => 'text/html' }, ['Page not found']] |
| + | end |
| + | end |
| + | |
| + | protected |
| + | |
| + | def render |
| + | context = self.locomotive_context |
| + | |
| + | template = ::Liquid::Template.parse(self.page.source, { |
| + | page: self.page, |
| + | mounting_point: self.mounting_point |
| + | }) |
| + | |
| + | template.render(context) |
| + | end |
| + | |
| + | # Build the Liquid context used to render the Locomotive page. It |
| + | # stores both assigns and registers. |
| + | # |
| + | # @param [ Hash ] other_assigns Assigns coming for instance from the controler (optional) |
| + | # |
| + | # @return [ Object ] A new instance of the Liquid::Context class. |
| + | # |
| + | def locomotive_context(other_assigns = {}) |
| + | assigns = self.locomotive_default_assigns |
| + | |
| + | # assigns from other middlewares |
| + | assigns.merge!(self.liquid_assigns) |
| + | |
| + | assigns.merge!(other_assigns) |
| + | |
| + | # templatized page |
| + | if self.page && self.content_entry |
| + | ['content_entry', 'entry', self.page.content_type.slug.singularize].each do |key| |
| + | assigns[key] = self.content_entry |
| + | end |
| + | end |
| + | |
| + | # Tip: switch from false to true to enable the re-thrown exception flag |
| + | ::Liquid::Context.new({}, assigns, self.locomotive_default_registers, true) |
| + | end |
| + | |
| + | # Return the default Liquid assigns used inside the Locomotive Liquid context |
| + | # |
| + | # @return [ Hash ] The default liquid assigns object |
| + | # |
| + | def locomotive_default_assigns |
| + | { |
| + | 'site' => self.site.to_liquid, |
| + | 'page' => self.page, |
| + | 'models' => Locomotive::Wagon::Liquid::Drops::ContentTypes.new, |
| + | 'contents' => Locomotive::Wagon::Liquid::Drops::ContentTypes.new, |
| + | 'current_page' => self.params[:page], |
| + | 'params' => self.params, |
| + | 'path' => self.request.path, |
| + | 'fullpath' => self.request.fullpath, |
| + | 'url' => self.request.url, |
| + | 'now' => Time.now.utc, |
| + | 'today' => Date.today, |
| + | 'locale' => I18n.locale.to_s, |
| + | 'default_locale' => self.mounting_point.default_locale.to_s, |
| + | 'locales' => self.mounting_point.locales.map(&:to_s), |
| + | 'current_user' => {} |
| + | } |
| + | end |
| + | |
| + | # Return the default Liquid registers used inside the Locomotive Liquid context |
| + | # |
| + | # @return [ Hash ] The default liquid registers object |
| + | # |
| + | def locomotive_default_registers |
| + | { |
| + | site: self.site, |
| + | page: self.page, |
| + | mounting_point: self.mounting_point, |
| + | inline_editor: false |
| + | } |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/server/templatized_page.rb b/lib/locomotive/wagon/server/templatized_page.rb
+32
-0
| @@ | @@ -0,0 +1,32 @@ |
| + | module Locomotive::Wagon |
| + | class Server |
| + | |
| + | class TemplatizedPage < Middleware |
| + | |
| + | def call(env) |
| + | self.set_accessors(env) |
| + | |
| + | if self.page && self.page.templatized? |
| + | self.set_content_entry!(env) |
| + | end |
| + | |
| + | app.call(env) |
| + | end |
| + | |
| + | protected |
| + | |
| + | def set_content_entry!(env) |
| + | %r(^#{self.page.safe_fullpath.gsub('*', '([^\/]+)')}$) =~ self.path |
| + | |
| + | permalink = $1 |
| + | |
| + | if content_entry = self.page.content_type.find_entry(permalink) |
| + | env['wagon.content_entry'] = content_entry |
| + | else |
| + | env['wagon.page'] = nil |
| + | end |
| + | end |
| + | |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/standalone_server.rb b/lib/locomotive/wagon/standalone_server.rb
+28
-0
| @@ | @@ -0,0 +1,28 @@ |
| + | $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../..')) |
| + | |
| + | require 'locomotive/wagon/logger' |
| + | require 'locomotive/wagon/version' |
| + | require 'locomotive/wagon/exceptions' |
| + | require 'locomotive/wagon/server' |
| + | require 'locomotive/mounter' |
| + | |
| + | module Locomotive |
| + | module Wagon |
| + | class StandaloneServer < Server |
| + | |
| + | def initialize(path) |
| + | Locomotive::Wagon::Logger.setup(path, false) |
| + | |
| + | # get the reader |
| + | reader = Locomotive::Mounter::Reader::FileSystem.instance |
| + | reader.run!(path: path) |
| + | reader |
| + | |
| + | Bundler.require 'misc' |
| + | |
| + | # run the rack app |
| + | super(reader, disable_listen: true) |
| + | end |
| + | end |
| + | end |
| + | end |
| \ No newline at end of file | |
locomotive/wagon/version.rb b/lib/locomotive/wagon/version.rb
+5
-0
| @@ | @@ -0,0 +1,5 @@ |
| + | module Locomotive |
| + | module Wagon |
| + | VERSION = '1.0.0.alpha8' |
| + | end |
| + | end |
locomotivecms_builder.gemspec
+0
-42
| @@ | @@ -1,42 +0,0 @@ |
| - | # -*- encoding: utf-8 -*- |
| - | lib = File.expand_path('../lib', __FILE__) |
| - | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) |
| - | require 'locomotive/builder/version' |
| - | |
| - | Gem::Specification.new do |gem| |
| - | gem.name = 'locomotivecms_builder' |
| - | gem.version = Locomotive::Builder::VERSION |
| - | gem.authors = ['Didier Lafforgue', 'Rodrigo Alvarez'] |
| - | gem.email = ['did@locomotivecms.com', 'papipo@gmail.com'] |
| - | gem.description = %q{The LocomotiveCMS builder is a site generator for the LocomotiveCMS engine} |
| - | gem.summary = %q{The LocomotiveCMS builder is a site generator for the LocomotiveCMS engine powered by all the efficient and modern HTML development tools (Haml, SASS, Compass, Less).} |
| - | gem.homepage = 'http://www.locomotivecms.com' |
| - | |
| - | gem.files = `git ls-files`.split($/) |
| - | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } |
| - | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) |
| - | gem.require_paths = ['lib'] |
| - | gem.executables = ['builder'] |
| - | |
| - | gem.add_dependency 'thor' |
| - | gem.add_dependency 'thin' |
| - | gem.add_dependency 'locomotive_liquid', '~> 2.4.1' |
| - | gem.add_dependency 'RedCloth', '~> 4.2.9' |
| - | gem.add_dependency 'dragonfly', '~> 0.9.12' |
| - | gem.add_dependency 'rack-cache', '~> 1.1' |
| - | gem.add_dependency 'rack-rescue', '~> 0.1.2' |
| - | |
| - | gem.add_dependency 'listen', '~> 0.7.0' |
| - | |
| - | gem.add_dependency 'rmagick', '2.12.2' |
| - | gem.add_dependency 'httmultiparty', '~> 0.3.8' |
| - | gem.add_dependency 'will_paginate', '~> 3.0.3' |
| - | gem.add_dependency 'locomotivecms_mounter', '1.0.0.alpha4' |
| - | |
| - | gem.add_dependency 'faker', '~> 0.9.5' |
| - | |
| - | gem.add_development_dependency 'rspec' |
| - | gem.add_development_dependency 'vcr' |
| - | gem.add_development_dependency 'webmock', '~> 1.8.0' |
| - | gem.add_development_dependency 'rack-test' |
| - | end |
locomotivecms_wagon.gemspec
+42
-0
| @@ | @@ -0,0 +1,42 @@ |
| + | # -*- encoding: utf-8 -*- |
| + | lib = File.expand_path('../lib', __FILE__) |
| + | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) |
| + | require 'locomotive/wagon/version' |
| + | |
| + | Gem::Specification.new do |gem| |
| + | gem.name = 'locomotivecms_wagon' |
| + | gem.version = Locomotive::Wagon::VERSION |
| + | gem.authors = ['Didier Lafforgue', 'Rodrigo Alvarez'] |
| + | gem.email = ['did@locomotivecms.com', 'papipo@gmail.com'] |
| + | gem.description = %q{The LocomotiveCMS wagon is a site generator for the LocomotiveCMS engine} |
| + | gem.summary = %q{The LocomotiveCMS wagon is a site generator for the LocomotiveCMS engine powered by all the efficient and modern HTML development tools (Haml, SASS, Compass, Less).} |
| + | gem.homepage = 'http://www.locomotivecms.com' |
| + | |
| + | gem.files = `git ls-files`.split($/) |
| + | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } |
| + | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) |
| + | gem.require_paths = ['lib'] |
| + | gem.executables = ['wagon'] |
| + | |
| + | gem.add_dependency 'thor' |
| + | gem.add_dependency 'thin' |
| + | gem.add_dependency 'locomotive_liquid', '~> 2.4.1' |
| + | gem.add_dependency 'RedCloth', '~> 4.2.9' |
| + | gem.add_dependency 'dragonfly', '~> 0.9.12' |
| + | gem.add_dependency 'rack-cache', '~> 1.1' |
| + | gem.add_dependency 'rack-rescue', '~> 0.1.2' |
| + | |
| + | gem.add_dependency 'listen', '~> 0.7.0' |
| + | |
| + | gem.add_dependency 'rmagick', '2.12.2' |
| + | gem.add_dependency 'httmultiparty', '~> 0.3.8' |
| + | gem.add_dependency 'will_paginate', '~> 3.0.3' |
| + | gem.add_dependency 'locomotivecms_mounter', '1.0.0.alpha4' |
| + | |
| + | gem.add_dependency 'faker', '~> 0.9.5' |
| + | |
| + | gem.add_development_dependency 'rspec' |
| + | gem.add_development_dependency 'vcr' |
| + | gem.add_development_dependency 'webmock', '~> 1.8.0' |
| + | gem.add_development_dependency 'rack-test' |
| + | end |
spec/integration/cassettes/push.yml
+35
-0
| @@ | @@ -772,4 +772,39 @@ http_interactions: |
| world!","es":"\u00a1Hola, Mundo!"}}' | |
| http_version: | |
| recorded_at: Thu, 24 Jan 2013 11:09:54 GMT | |
| + | - request: |
| + | method: get |
| + | uri: http://locomotive.engine.dev:3000/locomotive/api/translations.json?auth_token=jKkxPorzWo8uNYUxiiQC |
| + | body: |
| + | encoding: US-ASCII |
| + | string: '' |
| + | headers: {} |
| + | response: |
| + | status: |
| + | code: 401 |
| + | message: ! 'Unauthorized ' |
| + | headers: |
| + | Content-Type: |
| + | - application/json; charset=utf-8 |
| + | X-Ua-Compatible: |
| + | - IE=Edge |
| + | Cache-Control: |
| + | - no-cache |
| + | X-Request-Id: |
| + | - 8b99d01f9a7fc58002c7d92f8cbcec2e |
| + | X-Runtime: |
| + | - '1.137608' |
| + | Server: |
| + | - WEBrick/1.3.1 (Ruby/1.9.3/2012-11-10) |
| + | Date: |
| + | - Wed, 27 Feb 2013 11:39:27 GMT |
| + | Content-Length: |
| + | - '41' |
| + | Connection: |
| + | - Keep-Alive |
| + | body: |
| + | encoding: US-ASCII |
| + | string: ! '{"error":"Invalid authentication token."}' |
| + | http_version: |
| + | recorded_at: Wed, 27 Feb 2013 11:39:27 GMT |
| recorded_with: VCR 2.3.0 | |
spec/integration/server_spec.rb
+3
-3
| @@ | @@ -1,16 +1,16 @@ |
| # encoding: utf-8 | |
| require File.dirname(__FILE__) + "/integration_helper" | |
| - | require "locomotive/builder/server" |
| + | require "locomotive/wagon/server" |
| require "rack/test" | |
| - | describe Locomotive::Builder::Server do |
| + | describe Locomotive::Wagon::Server do |
| include Rack::Test::Methods | |
| def app | |
| clone_site | |
| reader = Locomotive::Mounter::Reader::FileSystem.instance | |
| reader.run!(path: "site") | |
| - | Locomotive::Builder::Server.new(reader) |
| + | Locomotive::Wagon::Server.new(reader) |
| end | |
| it "shows the index page" do | |
spec/integration/sites_spec.rb
+2
-2
| @@ | @@ -1,6 +1,6 @@ |
| require File.dirname(__FILE__) + "/integration_helper" | |
| - | describe Locomotive::Builder do |
| + | describe Locomotive::Wagon do |
| it "imports" do | |
| File.exists?("site/config/site.yml").should be_false | |
| clone_site | |
| @@ | @@ -19,7 +19,7 @@ describe Locomotive::Builder do |
| text.gsub!(/Content of the home page/, "New content of the home page") | |
| File.open(file_name, "w") { |file| file.puts text} | |
| VCR.use_cassette('push') do | |
| - | Locomotive::Builder.push("site", {"host" => "locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive") |
| + | Locomotive::Wagon.push("site", {"host" => "locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive") |
| end | |
| WebMock.should have_requested(:put, /pages\/.+.json\?auth_token=.+/).with(:body => /page\[raw_template\]=New%20content%20of%20the%20home%20page/).once | |
spec/spec_helper.rb
+1
-1
| @@ | @@ -1,4 +1,4 @@ |
| - | require "locomotive/builder" |
| + | require "locomotive/wagon" |
| require "rspec" | |
| Dir["#{File.expand_path('../support', __FILE__)}/*.rb"].each do |file| | |
spec/support/helpers.rb
+1
-1
| @@ | @@ -6,7 +6,7 @@ module Spec |
| def clone_site | |
| VCR.use_cassette('pull') do | |
| - | Locomotive::Builder.clone("site", {"host" => "locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive") |
| + | Locomotive::Wagon.clone("site", {"host" => "locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive") |
| end | |
| end | |
| end | |