locomotivecms_mounter 1.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/locomotive/mounter/config.rb +21 -0
- data/lib/locomotive/mounter/engine_api.rb +40 -0
- data/lib/locomotive/mounter/exceptions.rb +38 -0
- data/lib/locomotive/mounter/extensions/compass.rb +36 -0
- data/lib/locomotive/mounter/extensions/httmultiparty.rb +22 -0
- data/lib/locomotive/mounter/extensions/tilt/css.rb +31 -0
- data/lib/locomotive/mounter/extensions/tilt/haml.rb +27 -0
- data/lib/locomotive/mounter/extensions/tilt/liquid.rb +23 -0
- data/lib/locomotive/mounter/extensions/tilt/template.rb +11 -0
- data/lib/locomotive/mounter/fields.rb +250 -0
- data/lib/locomotive/mounter/models/base.rb +41 -0
- data/lib/locomotive/mounter/models/content_asset.rb +84 -0
- data/lib/locomotive/mounter/models/content_entry.rb +290 -0
- data/lib/locomotive/mounter/models/content_field.rb +128 -0
- data/lib/locomotive/mounter/models/content_select_option.rb +29 -0
- data/lib/locomotive/mounter/models/content_type.rb +217 -0
- data/lib/locomotive/mounter/models/editable_element.rb +27 -0
- data/lib/locomotive/mounter/models/page.rb +377 -0
- data/lib/locomotive/mounter/models/site.rb +27 -0
- data/lib/locomotive/mounter/models/snippet.rb +55 -0
- data/lib/locomotive/mounter/models/theme_asset.rb +135 -0
- data/lib/locomotive/mounter/models/translation.rb +28 -0
- data/lib/locomotive/mounter/mounting_point.rb +49 -0
- data/lib/locomotive/mounter/reader/api/base.rb +49 -0
- data/lib/locomotive/mounter/reader/api/content_assets_reader.rb +40 -0
- data/lib/locomotive/mounter/reader/api/content_entries_reader.rb +141 -0
- data/lib/locomotive/mounter/reader/api/content_types_reader.rb +74 -0
- data/lib/locomotive/mounter/reader/api/pages_reader.rb +174 -0
- data/lib/locomotive/mounter/reader/api/site_reader.rb +37 -0
- data/lib/locomotive/mounter/reader/api/snippets_reader.rb +59 -0
- data/lib/locomotive/mounter/reader/api/theme_assets_reader.rb +42 -0
- data/lib/locomotive/mounter/reader/api/translations_reader.rb +28 -0
- data/lib/locomotive/mounter/reader/api.rb +49 -0
- data/lib/locomotive/mounter/reader/file_system/base.rb +65 -0
- data/lib/locomotive/mounter/reader/file_system/content_assets_reader.rb +88 -0
- data/lib/locomotive/mounter/reader/file_system/content_entries_reader.rb +101 -0
- data/lib/locomotive/mounter/reader/file_system/content_types_reader.rb +88 -0
- data/lib/locomotive/mounter/reader/file_system/pages_reader.rb +206 -0
- data/lib/locomotive/mounter/reader/file_system/site_reader.rb +24 -0
- data/lib/locomotive/mounter/reader/file_system/snippets_reader.rb +78 -0
- data/lib/locomotive/mounter/reader/file_system/theme_assets_reader.rb +78 -0
- data/lib/locomotive/mounter/reader/file_system/translations_reader.rb +36 -0
- data/lib/locomotive/mounter/reader/file_system.rb +42 -0
- data/lib/locomotive/mounter/reader/runner.rb +89 -0
- data/lib/locomotive/mounter/utils/hash.rb +31 -0
- data/lib/locomotive/mounter/utils/string.rb +17 -0
- data/lib/locomotive/mounter/utils/yaml.rb +125 -0
- data/lib/locomotive/mounter/version.rb +8 -0
- data/lib/locomotive/mounter/writer/api/base.rb +323 -0
- data/lib/locomotive/mounter/writer/api/content_assets_writer.rb +74 -0
- data/lib/locomotive/mounter/writer/api/content_entries_writer.rb +223 -0
- data/lib/locomotive/mounter/writer/api/content_types_writer.rb +151 -0
- data/lib/locomotive/mounter/writer/api/pages_writer.rb +225 -0
- data/lib/locomotive/mounter/writer/api/site_writer.rb +164 -0
- data/lib/locomotive/mounter/writer/api/snippets_writer.rb +111 -0
- data/lib/locomotive/mounter/writer/api/theme_assets_writer.rb +152 -0
- data/lib/locomotive/mounter/writer/api/translations_writer.rb +83 -0
- data/lib/locomotive/mounter/writer/api.rb +62 -0
- data/lib/locomotive/mounter/writer/file_system/base.rb +61 -0
- data/lib/locomotive/mounter/writer/file_system/content_assets_writer.rb +33 -0
- data/lib/locomotive/mounter/writer/file_system/content_entries_writer.rb +29 -0
- data/lib/locomotive/mounter/writer/file_system/content_types_writer.rb +27 -0
- data/lib/locomotive/mounter/writer/file_system/pages_writer.rb +73 -0
- data/lib/locomotive/mounter/writer/file_system/site_writer.rb +25 -0
- data/lib/locomotive/mounter/writer/file_system/snippets_writer.rb +54 -0
- data/lib/locomotive/mounter/writer/file_system/theme_assets_writer.rb +35 -0
- data/lib/locomotive/mounter/writer/file_system/translations_writer.rb +22 -0
- data/lib/locomotive/mounter/writer/file_system.rb +69 -0
- data/lib/locomotive/mounter/writer/runner.rb +68 -0
- data/lib/locomotive/mounter.rb +97 -0
- metadata +487 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Mounter
|
|
3
|
+
|
|
4
|
+
class Config < Hash
|
|
5
|
+
|
|
6
|
+
def self.instance
|
|
7
|
+
@@instance ||= self.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.register(attributes)
|
|
11
|
+
self.instance.merge!(attributes)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.[](key)
|
|
15
|
+
self.instance[key]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Mounter
|
|
3
|
+
|
|
4
|
+
class EngineApi
|
|
5
|
+
|
|
6
|
+
include HTTMultiParty
|
|
7
|
+
|
|
8
|
+
format :json
|
|
9
|
+
|
|
10
|
+
# Get a new token from the Engine API and set it for
|
|
11
|
+
# this class. It raises an exception if the operation fails.
|
|
12
|
+
# Example of the base uri: locomotivecms.com, nocoffee.fr.
|
|
13
|
+
#
|
|
14
|
+
# @param [ Hash / Array ] *args A hash or parameters in that order: uri, email, password
|
|
15
|
+
#
|
|
16
|
+
# @return [ String ] The new token
|
|
17
|
+
#
|
|
18
|
+
def self.set_token(*args)
|
|
19
|
+
uri, email, password = (if args.first.is_a?(Hash)
|
|
20
|
+
[args.first[:uri], args.first[:email], args.first[:password]]
|
|
21
|
+
else
|
|
22
|
+
[*args]
|
|
23
|
+
end)
|
|
24
|
+
|
|
25
|
+
self.base_uri uri
|
|
26
|
+
|
|
27
|
+
response = post('/tokens.json', body: { email: email, password: password })
|
|
28
|
+
|
|
29
|
+
if response.code < 400
|
|
30
|
+
self.default_params auth_token: response['token']
|
|
31
|
+
response['token']
|
|
32
|
+
else
|
|
33
|
+
raise WrongCredentials.new("#{response['message']} (#{response.code})")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Mounter
|
|
3
|
+
|
|
4
|
+
class DefaultException < ::Exception
|
|
5
|
+
|
|
6
|
+
def initialize(message = nil)
|
|
7
|
+
Locomotive::Mounter.logger.error message
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ReaderException < DefaultException
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class WriterException < DefaultException
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class ImplementationIsMissingException < DefaultException
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class FieldDoesNotExistException < DefaultException
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class UnknownContentTypeException < DefaultException
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class DuplicateContentEntryException < DefaultException
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class UnknownTemplateException < DefaultException
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class WrongCredentials < DefaultException
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Mounter
|
|
3
|
+
module Extensions
|
|
4
|
+
|
|
5
|
+
module Compass
|
|
6
|
+
|
|
7
|
+
# Configure Compass for the current site
|
|
8
|
+
#
|
|
9
|
+
# @param [ String ] site_path The root directory of the site
|
|
10
|
+
#
|
|
11
|
+
def self.configure(site_path)
|
|
12
|
+
::Compass.configuration do |config|
|
|
13
|
+
config.project_path = File.join(site_path, 'public')
|
|
14
|
+
config.http_path = '/'
|
|
15
|
+
config.css_dir = '../tmp/stylesheets'
|
|
16
|
+
config.sass_dir = 'stylesheets'
|
|
17
|
+
config.images_dir = 'images'
|
|
18
|
+
config.javascripts_dir = 'javascripts'
|
|
19
|
+
config.project_type = :stand_alone
|
|
20
|
+
config.output_style = :nested
|
|
21
|
+
config.line_comments = false
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# # Return the Compass options
|
|
26
|
+
# #
|
|
27
|
+
# # @return [ Hash ] The Compass options
|
|
28
|
+
# def self.options
|
|
29
|
+
# ::Compass.configuration.to_sass_engine_options
|
|
30
|
+
# end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Patch for https://github.com/jwagener/httmultiparty/issues/11
|
|
2
|
+
|
|
3
|
+
module HTTMultiParty
|
|
4
|
+
module ClassMethods
|
|
5
|
+
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def perform_request(http_method, path, options, &block) #:nodoc:
|
|
9
|
+
options = default_options.dup.merge(options)
|
|
10
|
+
|
|
11
|
+
# FIXME: default_params are not handled for some unknown reasons, so move them to the body instead
|
|
12
|
+
if http_method == MultipartPost || http_method == MultipartPut
|
|
13
|
+
default_params = options.delete(:default_params)
|
|
14
|
+
options[:body].merge!(default_params) if options[:body] && default_params
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
process_cookies(options)
|
|
18
|
+
HTTParty::Request.new(http_method, path, options).perform(&block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Tilt
|
|
2
|
+
|
|
3
|
+
# Sass with Compass features
|
|
4
|
+
class SassWithCompassTemplate < SassTemplate
|
|
5
|
+
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def sass_options
|
|
9
|
+
super.merge(::Compass.configuration.to_sass_engine_options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Tilt.register 'sass', SassWithCompassTemplate
|
|
15
|
+
Tilt.prefer SassWithCompassTemplate
|
|
16
|
+
|
|
17
|
+
# Scss with Compass features
|
|
18
|
+
class ScssWithCompassTemplate < ScssTemplate
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def sass_options
|
|
23
|
+
super.merge(::Compass.configuration.to_sass_engine_options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Tilt.register 'scss', ScssWithCompassTemplate
|
|
29
|
+
Tilt.prefer ScssWithCompassTemplate
|
|
30
|
+
|
|
31
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Tilt
|
|
2
|
+
|
|
3
|
+
# YAML Front-matters for HAML templates
|
|
4
|
+
class YamlFrontMattersHamlTemplate < HamlTemplate
|
|
5
|
+
|
|
6
|
+
# Attributes from YAML Front-matters header
|
|
7
|
+
attr_reader :attributes
|
|
8
|
+
|
|
9
|
+
def need_for_prerendering?
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def prepare
|
|
14
|
+
if data =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)(.*)/m
|
|
15
|
+
@attributes = YAML.load($1)
|
|
16
|
+
@data = $3
|
|
17
|
+
end
|
|
18
|
+
@data = @data.force_encoding('utf-8')
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Tilt.register 'haml', YamlFrontMattersHamlTemplate
|
|
25
|
+
Tilt.prefer YamlFrontMattersHamlTemplate
|
|
26
|
+
|
|
27
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Tilt
|
|
2
|
+
|
|
3
|
+
# YAML Front-matters for Liquid templates
|
|
4
|
+
class YamlFrontMattersLiquidTemplate < Template
|
|
5
|
+
|
|
6
|
+
# Attributes from YAML Front-matters header
|
|
7
|
+
attr_reader :attributes
|
|
8
|
+
|
|
9
|
+
def prepare
|
|
10
|
+
if data =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)(.*)/m
|
|
11
|
+
@attributes = YAML.load($1)
|
|
12
|
+
@data = $3
|
|
13
|
+
end
|
|
14
|
+
@data = @data.force_encoding('utf-8')
|
|
15
|
+
# Note: do not call 'super' because we are going to use a different parse mechanism
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Tilt.register 'liquid', YamlFrontMattersLiquidTemplate
|
|
21
|
+
Tilt.prefer YamlFrontMattersLiquidTemplate
|
|
22
|
+
|
|
23
|
+
end
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Mounter
|
|
3
|
+
|
|
4
|
+
module Fields
|
|
5
|
+
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
include ActiveSupport::Callbacks
|
|
10
|
+
|
|
11
|
+
define_callbacks :initialize
|
|
12
|
+
|
|
13
|
+
class << self; attr_accessor :_fields end
|
|
14
|
+
|
|
15
|
+
attr_accessor :_locales
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Default constructor
|
|
19
|
+
#
|
|
20
|
+
# @param [ Hash ] attributes The new attributes
|
|
21
|
+
#
|
|
22
|
+
def initialize(attributes = {})
|
|
23
|
+
run_callbacks :initialize do
|
|
24
|
+
_attributes = attributes.symbolize_keys
|
|
25
|
+
|
|
26
|
+
# set default values
|
|
27
|
+
self.class._fields.each do |name, options|
|
|
28
|
+
next if !options.key?(:default) || _attributes.key?(name)
|
|
29
|
+
|
|
30
|
+
_attributes[name] = options[:default]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# set default translation
|
|
34
|
+
self.add_locale(Locomotive::Mounter.locale)
|
|
35
|
+
|
|
36
|
+
self.write_attributes(_attributes)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Set or replace the attributes of the current instance by the ones
|
|
41
|
+
# passed as parameter.
|
|
42
|
+
# It raises an exception if one of the keys is not included in the list of fields.
|
|
43
|
+
#
|
|
44
|
+
# @param [ Hash ] attributes The new attributes
|
|
45
|
+
#
|
|
46
|
+
def write_attributes(attributes)
|
|
47
|
+
return if attributes.blank?
|
|
48
|
+
|
|
49
|
+
attributes.each do |name, value|
|
|
50
|
+
unless self.class._fields.key?(name.to_sym) || self.respond_to?(:"#{name}=")
|
|
51
|
+
raise FieldDoesNotExistException.new "[#{self.class.inspect}] setting an unknown attribute '#{name}' with the value '#{value.inspect}'"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if self.localized?(name) && value.is_a?(Hash)
|
|
55
|
+
self.send(:"#{name}_translations=", value)
|
|
56
|
+
else
|
|
57
|
+
self.send(:"#{name}=", value)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
alias :attributes= :write_attributes
|
|
63
|
+
|
|
64
|
+
# Return the fields with their values
|
|
65
|
+
#
|
|
66
|
+
# @return [ Hash ] The attributes
|
|
67
|
+
#
|
|
68
|
+
def attributes
|
|
69
|
+
{}.tap do |_attributes|
|
|
70
|
+
self.class._fields.each do |name, options|
|
|
71
|
+
_attributes[name] = self.send(name.to_sym)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Return the fields with their values and their translations
|
|
77
|
+
#
|
|
78
|
+
# @return [ Hash ] The attributes
|
|
79
|
+
#
|
|
80
|
+
def attributes_with_translations
|
|
81
|
+
{}.tap do |_attributes|
|
|
82
|
+
self.class._fields.each do |name, options|
|
|
83
|
+
next if options[:association]
|
|
84
|
+
|
|
85
|
+
if options[:localized]
|
|
86
|
+
value = self.send(:"#{name}_translations")
|
|
87
|
+
|
|
88
|
+
value = value.values.first if value.size == 1
|
|
89
|
+
|
|
90
|
+
value = nil if value.empty?
|
|
91
|
+
|
|
92
|
+
_attributes[name] = value
|
|
93
|
+
else
|
|
94
|
+
_attributes[name] = self.send(name.to_sym)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Check if the field specified by the argument is localized
|
|
101
|
+
#
|
|
102
|
+
# @param [ String ] name Name of the field
|
|
103
|
+
#
|
|
104
|
+
# @return [ Boolean ] True if the field is localized
|
|
105
|
+
#
|
|
106
|
+
def localized?(name)
|
|
107
|
+
method_name = :"#{name}_localized?"
|
|
108
|
+
self.respond_to?(method_name) && self.send(method_name)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# List all the translations done on that model
|
|
112
|
+
#
|
|
113
|
+
# @return [ List ] List of locales
|
|
114
|
+
#
|
|
115
|
+
def translated_in
|
|
116
|
+
self._locales.map(&:to_sym)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Tell if the object has been translated in the locale
|
|
120
|
+
# passed in parameter.
|
|
121
|
+
#
|
|
122
|
+
# @param [ String/Symbol ] locale
|
|
123
|
+
#
|
|
124
|
+
# @return [ Boolean ] True if one of the fields has been translated.
|
|
125
|
+
#
|
|
126
|
+
def translated_in?(locale)
|
|
127
|
+
self.translated_in.include?(locale.to_sym)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Return a Hash of all the non blank attributes of the object.
|
|
131
|
+
# It also performs a couple of modifications: stringify keys and
|
|
132
|
+
# convert Symbol to String.
|
|
133
|
+
#
|
|
134
|
+
# @param [ Boolean ] translation Flag (by default true) to get the translations too.
|
|
135
|
+
#
|
|
136
|
+
# @return [ Hash ] The non blank attributes
|
|
137
|
+
#
|
|
138
|
+
def to_hash(translations = true)
|
|
139
|
+
hash = translations ? self.attributes_with_translations : self.attributes
|
|
140
|
+
|
|
141
|
+
hash.delete_if { |k, v| v.blank? }
|
|
142
|
+
|
|
143
|
+
hash.each { |k, v| hash[k] = v.to_s if v.is_a?(Symbol) }
|
|
144
|
+
|
|
145
|
+
hash.deep_stringify_keys
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Provide a better output of the default to_yaml method
|
|
149
|
+
#
|
|
150
|
+
# @return [ String ] The YAML version of the object
|
|
151
|
+
#
|
|
152
|
+
def to_yaml
|
|
153
|
+
# get the attributes with their translations and get rid of all the symbols
|
|
154
|
+
object = self.to_hash
|
|
155
|
+
|
|
156
|
+
object.each do |key, value|
|
|
157
|
+
if value.is_a?(Array)
|
|
158
|
+
object[key] = if value.first.is_a?(String)
|
|
159
|
+
StyledYAML.inline(value) # inline array
|
|
160
|
+
else
|
|
161
|
+
value.map(&:to_hash)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
StyledYAML.dump object
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
protected
|
|
170
|
+
|
|
171
|
+
def getter(name, options = {})
|
|
172
|
+
value = self.instance_variable_get(:"@#{name}")
|
|
173
|
+
if options[:localized]
|
|
174
|
+
(value || {})[Locomotive::Mounter.locale]
|
|
175
|
+
else
|
|
176
|
+
value
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def setter(name, value, options = {})
|
|
181
|
+
if options[:localized]
|
|
182
|
+
# keep track of the current locale
|
|
183
|
+
self.add_locale(Locomotive::Mounter.locale)
|
|
184
|
+
|
|
185
|
+
translations = self.instance_variable_get(:"@#{name}") || {}
|
|
186
|
+
translations[Locomotive::Mounter.locale] = value
|
|
187
|
+
value = translations
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
if options[:type] == :array
|
|
191
|
+
klass = options[:class_name].constantize
|
|
192
|
+
value = value.map { |object| object.is_a?(Hash) ? klass.new(object) : object }
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
self.instance_variable_set(:"@#{name}", value)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def add_locale(locale)
|
|
199
|
+
self._locales ||= []
|
|
200
|
+
self._locales << locale.to_sym unless self._locales.include?(locale.to_sym)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
module ClassMethods
|
|
204
|
+
|
|
205
|
+
# Add a field to the current instance. It creates getter/setter methods related to that field.
|
|
206
|
+
# A field can have translations if the option named localized is set to true.
|
|
207
|
+
#
|
|
208
|
+
# @param [ String ] name The name of the field
|
|
209
|
+
# @param [ Hash ] options The options related to the field.
|
|
210
|
+
#
|
|
211
|
+
def field(name, options = {})
|
|
212
|
+
options = { localized: false }.merge(options)
|
|
213
|
+
|
|
214
|
+
@_fields ||= {} # initialize the list of fields if nil
|
|
215
|
+
|
|
216
|
+
self._fields[name.to_sym] = options
|
|
217
|
+
|
|
218
|
+
class_eval <<-EOV
|
|
219
|
+
def #{name}
|
|
220
|
+
self.getter '#{name}', self.class._fields[:#{name}]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def #{name}=(value)
|
|
224
|
+
self.setter '#{name}', value, self.class._fields[:#{name}] #, #{options[:localized]}
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def #{name}_localized?
|
|
228
|
+
#{options[:localized]}
|
|
229
|
+
end
|
|
230
|
+
EOV
|
|
231
|
+
|
|
232
|
+
if options[:localized]
|
|
233
|
+
class_eval <<-EOV
|
|
234
|
+
def #{name}_translations
|
|
235
|
+
@#{name} || {}
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def #{name}_translations=(translations)
|
|
239
|
+
translations.each { |locale, value| self.add_locale(locale) }
|
|
240
|
+
@#{name} = translations.symbolize_keys
|
|
241
|
+
end
|
|
242
|
+
EOV
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Mounter
|
|
3
|
+
module Models
|
|
4
|
+
|
|
5
|
+
class Base
|
|
6
|
+
|
|
7
|
+
include Locomotive::Mounter::Fields
|
|
8
|
+
|
|
9
|
+
attr_accessor :_id, :mounting_point
|
|
10
|
+
|
|
11
|
+
## methods ##
|
|
12
|
+
|
|
13
|
+
def initialize(attributes = {})
|
|
14
|
+
self.mounting_point = attributes.delete(:mounting_point)
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def persisted?
|
|
19
|
+
!self._id.blank?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
protected
|
|
23
|
+
|
|
24
|
+
# Take a list of field names and return a hash with
|
|
25
|
+
# their values only if they are not nil.
|
|
26
|
+
#
|
|
27
|
+
# @param [ Array ] fields List of field names (string)
|
|
28
|
+
#
|
|
29
|
+
# @return [ Hash ] A hash with symbolize keys
|
|
30
|
+
#
|
|
31
|
+
def filter_attributes(fields)
|
|
32
|
+
self.attributes.clone.delete_if do |k, v|
|
|
33
|
+
!fields.include?(k.to_s) || (!v.is_a?(FalseClass) && v.blank?)
|
|
34
|
+
end.deep_symbolize_keys
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module Locomotive
|
|
2
|
+
module Mounter
|
|
3
|
+
module Models
|
|
4
|
+
|
|
5
|
+
class ContentAsset < Base
|
|
6
|
+
|
|
7
|
+
## fields ##
|
|
8
|
+
field :source
|
|
9
|
+
|
|
10
|
+
## other accessors ##
|
|
11
|
+
attr_accessor :folder, :filepath, :uri
|
|
12
|
+
|
|
13
|
+
## methods ##
|
|
14
|
+
|
|
15
|
+
# Name of the file
|
|
16
|
+
#
|
|
17
|
+
# @return [ String ] Name of the file
|
|
18
|
+
#
|
|
19
|
+
def filename
|
|
20
|
+
return @filename if @filename
|
|
21
|
+
|
|
22
|
+
if self.uri
|
|
23
|
+
@filename = File.basename(self.uri.path)
|
|
24
|
+
else
|
|
25
|
+
@filename = File.basename(self.filepath)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Content of the asset.
|
|
30
|
+
#
|
|
31
|
+
# @return [ String ] The content of the asset
|
|
32
|
+
#
|
|
33
|
+
def content
|
|
34
|
+
return @raw if @raw
|
|
35
|
+
|
|
36
|
+
if self.uri
|
|
37
|
+
@raw = Net::HTTP.get(self.uri)
|
|
38
|
+
else
|
|
39
|
+
@raw = File.read(self.filepath)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def size
|
|
44
|
+
if self.uri
|
|
45
|
+
Net::HTTP.get(self.uri).body.size
|
|
46
|
+
else
|
|
47
|
+
File.size(self.filepath)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Return true if the uri or the file exists.
|
|
52
|
+
#
|
|
53
|
+
# @return [ Boolean ] True if it exists
|
|
54
|
+
#
|
|
55
|
+
def exists?
|
|
56
|
+
self.size
|
|
57
|
+
true
|
|
58
|
+
rescue
|
|
59
|
+
false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def local_filepath
|
|
63
|
+
File.join('/', self.folder, self.filename)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Return the params used for the API.
|
|
67
|
+
#
|
|
68
|
+
# @return [ Hash ] The params
|
|
69
|
+
#
|
|
70
|
+
def to_params
|
|
71
|
+
return {} if self.uri
|
|
72
|
+
|
|
73
|
+
{ source: File.new(self.filepath) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_s
|
|
77
|
+
self.uri ? self.uri.path : self.filename
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|