maglove 0.8.1 → 1.0.2
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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +157 -0
- data/Gemfile.lock +59 -49
- data/bin/maglove +13 -13
- data/data/maglove/export.haml +11 -0
- data/data/maglove/index.haml +17 -0
- data/data/maglove/maglove.css +62 -0
- data/data/maglove/maglove.haml +18 -0
- data/data/maglove/maglove.js +68 -0
- data/lib/block_resolver.rb +6 -0
- data/lib/ext/thor/option.rb +43 -0
- data/lib/magloft/api.rb +39 -0
- data/lib/magloft/api_caller.rb +67 -0
- data/lib/magloft/remote_collection.rb +50 -0
- data/lib/magloft/remote_resource.rb +124 -0
- data/lib/magloft/transformable.rb +11 -0
- data/lib/magloft/typeloft_block.rb +18 -0
- data/lib/magloft/typeloft_image.rb +18 -0
- data/lib/magloft/typeloft_template.rb +18 -0
- data/lib/magloft/typeloft_theme.rb +41 -0
- data/lib/magloft.rb +3 -0
- data/lib/maglove/application.rb +10 -12
- data/lib/maglove/asset/theme.rb +37 -32
- data/lib/maglove/commands/assets.rb +85 -0
- data/lib/maglove/commands/base.rb +55 -0
- data/lib/maglove/commands/fonts.rb +69 -0
- data/lib/maglove/commands/main.rb +24 -0
- data/lib/maglove/commands/theme.rb +197 -0
- data/lib/maglove/helper/log_helper.rb +3 -18
- data/lib/maglove/middleware/live_reload.rb +97 -0
- data/lib/maglove/phantom_script.rb +9 -10
- data/lib/maglove/server.rb +46 -78
- data/lib/maglove/tilt/coffee_template.rb +7 -6
- data/lib/maglove/tilt/haml_template.rb +4 -4
- data/lib/maglove/tilt/js_template.rb +8 -8
- data/lib/maglove/tilt/less_template.rb +5 -4
- data/lib/maglove/tilt/scss_template.rb +17 -11
- data/lib/maglove/tilt/yaml_template.rb +3 -2
- data/lib/maglove/version.rb +1 -1
- data/lib/maglove/workspace.rb +41 -0
- data/lib/maglove.rb +38 -49
- data/lib/powersnap.rb +24 -0
- data/lib/workspace/workspace_dir/archive.rb +18 -0
- data/lib/workspace/workspace_dir.rb +98 -0
- data/lib/workspace/workspace_file/archive.rb +45 -0
- data/lib/workspace/workspace_file/media.rb +19 -0
- data/lib/workspace/workspace_file/net.rb +18 -0
- data/lib/workspace/workspace_file/parse.rb +21 -0
- data/lib/workspace/workspace_file.rb +99 -0
- data/lib/workspace.rb +11 -0
- data/maglove.gemspec +12 -12
- metadata +100 -86
- data/data/maglove/dump.haml +0 -58
- data/data/maglove/sdk.haml +0 -27
- data/lib/ext/commander/command.rb +0 -32
- data/lib/ext/commander/methods.rb +0 -8
- data/lib/maglove/asset/base_theme.rb +0 -17
- data/lib/maglove/command/compile.rb +0 -44
- data/lib/maglove/command/compress.rb +0 -28
- data/lib/maglove/command/copy.rb +0 -35
- data/lib/maglove/command/core.rb +0 -23
- data/lib/maglove/command/font.rb +0 -80
- data/lib/maglove/command/server.rb +0 -16
- data/lib/maglove/command/sync.rb +0 -17
- data/lib/maglove/command/theme.rb +0 -175
- data/lib/maglove/command/util.rb +0 -45
- data/lib/maglove/helper/asset_helper.rb +0 -24
- data/lib/maglove/helper/command_helper.rb +0 -67
- data/lib/maglove/helper/theme_helper.rb +0 -105
- data/lib/maglove/server/hpub.rb +0 -185
- data/lib/maglove/template/tumblr.rb +0 -81
- data/lib/maglove/tilt/twig_template.rb +0 -49
@@ -0,0 +1,69 @@
|
|
1
|
+
module MagLove
|
2
|
+
module Commands
|
3
|
+
class Fonts < Base
|
4
|
+
desc "compile", "Compile fonts"
|
5
|
+
def compile
|
6
|
+
# clean up
|
7
|
+
FileUtils.rm_rf("dist/fonts")
|
8
|
+
FileUtils.mkdir_p("dist/fonts")
|
9
|
+
|
10
|
+
# 1: Build font map
|
11
|
+
debug("▸ building font map")
|
12
|
+
font_map = {}
|
13
|
+
font_files = Dir.glob("src/fonts/*/*.ttf")
|
14
|
+
font_files.each do |font_file|
|
15
|
+
(_root_dir, _font_dir, font_id, font_filename) = font_file.split("/")
|
16
|
+
font_variant = font_filename.gsub("#{font_id}-", '').gsub(".ttf", '')
|
17
|
+
font_map[font_id] = [] if font_map[font_id].nil?
|
18
|
+
font_map[font_id].push(font_variant)
|
19
|
+
end
|
20
|
+
|
21
|
+
# 2: Generate stylesheets
|
22
|
+
debug("▸ compiling fonts:")
|
23
|
+
FileUtils.touch("dist/fonts/fonts.css")
|
24
|
+
open("dist/fonts/fonts.css", 'wb') do |master_file|
|
25
|
+
font_map.each do |font_id, variants|
|
26
|
+
debug("~▸ compiling font '#{font_id}'")
|
27
|
+
FileUtils.mkdir_p("dist/fonts/#{font_id}")
|
28
|
+
variants.each do |variant|
|
29
|
+
FileUtils.copy("src/fonts/#{font_id}/#{font_id}-#{variant}.ttf", "dist/fonts/#{font_id}/#{font_id}-#{variant}.ttf")
|
30
|
+
end
|
31
|
+
font_style_file = "dist/fonts/#{font_id}/font.css"
|
32
|
+
FileUtils.touch(font_style_file)
|
33
|
+
open(font_style_file, 'wb') do |file|
|
34
|
+
master_file << "/* #{font_id} (#{variants.join(', ')}) */\n"
|
35
|
+
variants.each do |variant|
|
36
|
+
file << get_font_styles(font_id, variant)
|
37
|
+
master_file << get_font_styles(font_id, variant, "#{font_id}/")
|
38
|
+
end
|
39
|
+
master_file << "\n"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
debug("▸ all font styles created")
|
44
|
+
if options.sync
|
45
|
+
# @TODO: Submit to cloud server
|
46
|
+
debug("▸ all fonts synchronized")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def get_font_styles(font_id, variant, append_dir = '')
|
53
|
+
font_name = font_id.tr('-', ' ').titleize
|
54
|
+
case variant
|
55
|
+
when "regular"
|
56
|
+
"@font-face{font-family:'#{font_name}';src:url('#{append_dir}#{font_id}-#{variant}.ttf');}\n"
|
57
|
+
when "bold"
|
58
|
+
"@font-face{font-family:'#{font_name}';src:url('#{append_dir}#{font_id}-#{variant}.ttf');font-weight:bold;}\n"
|
59
|
+
when "italic"
|
60
|
+
"@font-face{font-family:'#{font_name}';src:url('#{append_dir}#{font_id}-#{variant}.ttf');font-style:italic;}\n"
|
61
|
+
when "bolditalic"
|
62
|
+
"@font-face{font-family:'#{font_name}';src:url('#{append_dir}#{font_id}-#{variant}.ttf');font-weight:bold;font-style:italic;}\n"
|
63
|
+
when "light"
|
64
|
+
"@font-face{font-family:'#{font_name}';src:url('#{append_dir}#{font_id}-#{variant}.ttf');font-weight:300;}\n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module MagLove
|
2
|
+
module Commands
|
3
|
+
class Main < Base
|
4
|
+
desc "fonts SUBCOMMAND ...ARGS", "manage fonts"
|
5
|
+
subcommand "fonts", Commands::Fonts
|
6
|
+
|
7
|
+
desc "theme SUBCOMMAND ...ARGS", "manage theme"
|
8
|
+
subcommand "theme", Commands::Theme
|
9
|
+
|
10
|
+
desc "assets SUBCOMMAND ...ARGS", "compile theme"
|
11
|
+
subcommand "assets", Commands::Assets
|
12
|
+
|
13
|
+
desc "compile", "Compile all themes"
|
14
|
+
def compile
|
15
|
+
workspace_dir("src/themes").each_dir do |dir|
|
16
|
+
theme = dir.name
|
17
|
+
info("COMPILING THEME #{theme}")
|
18
|
+
invoke(Assets, :compile, [], theme: theme)
|
19
|
+
reset_command_invocations(Assets)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module MagLove
|
2
|
+
module Commands
|
3
|
+
class Theme < Base
|
4
|
+
desc "server", "Launch Development Server"
|
5
|
+
option :port, type: :string, default: "4000"
|
6
|
+
option :theme, type: :string, required: true, validator: OptionValidator
|
7
|
+
def server
|
8
|
+
invoke(Assets, :compile, [], { theme: options.theme })
|
9
|
+
invoke(Fonts, :compile, [], {})
|
10
|
+
info("▸ starting server for theme '#{options.theme}' on '127.0.0.1:#{options.port}'")
|
11
|
+
require 'maglove/server'
|
12
|
+
MagLove::Server.start(options.port, options.theme, theme_config(:templates))
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "thumbnails", "Create page thumbnails"
|
16
|
+
option :theme, type: :string, required: true, validator: OptionValidator
|
17
|
+
def thumbnails
|
18
|
+
require "powersnap"
|
19
|
+
invoke(Fonts, :compile, [], {})
|
20
|
+
invoke(Assets, :compile, [], theme: options.theme)
|
21
|
+
|
22
|
+
info("▸ Generating Template Thumbnails")
|
23
|
+
output_dir = theme_dir(root: "dist").chdir("templates")
|
24
|
+
powersnap = Powersnap.new(output_dir.files("*.html").map(&:url))
|
25
|
+
powersnap.generate(dir: output_dir.to_s, width: 768, height: 1024, pattern: "{basename}.png", zoom: 1.0, page: false)
|
26
|
+
|
27
|
+
info("▸ Generating Block Thumbnails")
|
28
|
+
output_dir = theme_dir(root: "dist").chdir("blocks")
|
29
|
+
if output_dir.exists?
|
30
|
+
powersnap = Powersnap.new(output_dir.files("*.html").map(&:url))
|
31
|
+
powersnap.generate(dir: output_dir.to_s, width: 512, height: 200, pattern: "{basename}.png", zoom: 1.0, page: true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "push", "Push Theme to MagLoft"
|
36
|
+
option :token, type: :string, required: true
|
37
|
+
option :theme, type: :string, required: true, validator: OptionValidator
|
38
|
+
option :thumbnails, type: :boolean, default: false
|
39
|
+
def push
|
40
|
+
info("▸ Pushing theme '#{options.theme}' to MagLoft")
|
41
|
+
|
42
|
+
# validate theme
|
43
|
+
theme_identifier = theme_config(:identifier)
|
44
|
+
error!("Theme '#{theme_identifier}' not found") unless theme_identifier
|
45
|
+
begin
|
46
|
+
theme = magloft_api.typeloft_themes.find_by_identifier(theme_identifier)
|
47
|
+
rescue MagLoft::ApiCaller::UnauthorizedError
|
48
|
+
error!("▸ You are not allowed to access the MagLoft API.")
|
49
|
+
end
|
50
|
+
if theme.nil?
|
51
|
+
info("▸ To create a new theme, run: maglove theme:create --theme '#{theme_identifier}'")
|
52
|
+
error!("Theme '#{theme_identifier}' was not yet created.")
|
53
|
+
end
|
54
|
+
|
55
|
+
# invoke asset compilation
|
56
|
+
invoke(Fonts, :compile, [], {})
|
57
|
+
invoke(Assets, :compile, [], { theme: options.theme })
|
58
|
+
|
59
|
+
# update theme
|
60
|
+
info("▸ Synchronizing Metadata")
|
61
|
+
theme.base_version = theme_config(:base_version)
|
62
|
+
theme.name = theme_config(:name)
|
63
|
+
theme.description = theme_config(:description)
|
64
|
+
theme.widgets = theme_config(:widgets)
|
65
|
+
theme.fonts = theme_config(:fonts)
|
66
|
+
theme.save
|
67
|
+
|
68
|
+
# upload blocks
|
69
|
+
theme_blocks = theme.typeloft_blocks.all
|
70
|
+
theme_blocks_map = Hash[theme_blocks.map { |block| [block.identifier, block] }]
|
71
|
+
if theme_dir.chdir("blocks").exists?
|
72
|
+
block_files = theme_dir.chdir("blocks").files("**/*.haml")
|
73
|
+
block_files.each do |block_file|
|
74
|
+
block_identifier = block_file.slug
|
75
|
+
if (block = theme_blocks_map[block_identifier])
|
76
|
+
block.name = block_file.basename.titlecase
|
77
|
+
block.contents = block_file.read
|
78
|
+
if block.changed?
|
79
|
+
info "▸ Updating Block '#{block_identifier}'"
|
80
|
+
block.save
|
81
|
+
end
|
82
|
+
else
|
83
|
+
info "▸ Creating Block '#{block_identifier}'"
|
84
|
+
theme.typeloft_blocks.create(identifier: block_identifier, name: block_file.basename.titlecase, contents: block_file.read)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# upload images
|
90
|
+
info("▸ Synchronizing Images")
|
91
|
+
theme_images = theme.typeloft_images.all
|
92
|
+
theme_images_map = Hash[theme_images.map { |image| [image.remote_file, image] }]
|
93
|
+
theme_dir(root: "dist").files("images/**/*.{jpg,png,gif,svg}").each do |image_file|
|
94
|
+
remote_file = "themes/#{options.theme}/#{image_file.relative_path}"
|
95
|
+
if (existing_image = theme_images_map[remote_file])
|
96
|
+
if image_file.md5 != existing_image.md5
|
97
|
+
info("▸ Updating Image '#{remote_file}'")
|
98
|
+
existing_image.md5 = image_file.md5
|
99
|
+
existing_image.upload(image_file.to_s)
|
100
|
+
existing_image.save
|
101
|
+
end
|
102
|
+
else
|
103
|
+
info("▸ Creating Image '#{remote_file}'")
|
104
|
+
new_image = theme.typeloft_images.create(remote_file: remote_file, title: image_file.basename.titlecase, md5: image_file.md5)
|
105
|
+
new_image.upload(image_file.to_s)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# upload css/js
|
110
|
+
info("▸ Synchronizing JavaScript and Stylesheet")
|
111
|
+
theme.upload_stylesheet(theme_dir(root: "dist").file("theme.css").to_s)
|
112
|
+
theme.upload_javascript(theme_dir(root: "dist").file("theme.js").to_s)
|
113
|
+
|
114
|
+
# upload templates
|
115
|
+
info("▸ Synchronizing Templates")
|
116
|
+
theme_templates = theme.typeloft_templates.all
|
117
|
+
theme_templates_map = Hash[theme_templates.map { |template| [template.identifier, template] }]
|
118
|
+
templates = theme_config(:templates)
|
119
|
+
templates.each_with_index do |template_identifier, position|
|
120
|
+
template_file = theme_dir.file("templates/#{template_identifier}.haml")
|
121
|
+
next unless !template_file.nil? and template_file.exists?
|
122
|
+
if (template = theme_templates_map[template_identifier])
|
123
|
+
template.title = template_identifier.titlecase
|
124
|
+
template.contents = template_file.read
|
125
|
+
template.position = position
|
126
|
+
if template.changed?
|
127
|
+
info "▸ Updating Template '#{template_identifier}'"
|
128
|
+
template.save
|
129
|
+
end
|
130
|
+
else
|
131
|
+
info "▸ Creating Template '#{template_identifier}'"
|
132
|
+
theme.typeloft_templates.create(identifier: template_identifier, title: template_identifier.titlecase, contents: template_file.read, position: position)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# update thumbnails
|
137
|
+
if options.thumbnails
|
138
|
+
invoke(:thumbnails, [], { theme: options.theme })
|
139
|
+
|
140
|
+
info("▸ Synchronizing Template Thumbnails")
|
141
|
+
theme.typeloft_templates.all.each do |template|
|
142
|
+
thumbnail_file = theme_dir(root: "dist").dir("templates").file("#{template.identifier}.png")
|
143
|
+
if thumbnail_file.exists?
|
144
|
+
info("~> Uploading Thumbnail for '#{template.identifier}'")
|
145
|
+
template.upload_thumbnail(thumbnail_file.to_s)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
info("▸ Synchronizing Block Thumbnails")
|
150
|
+
theme.typeloft_blocks.all.each do |block|
|
151
|
+
thumbnail_file = theme_dir(root: "dist").dir("blocks").file("#{block.identifier}.png")
|
152
|
+
if thumbnail_file.exists?
|
153
|
+
info("~> Uploading Thumbnail for '#{block.identifier}'")
|
154
|
+
block.upload_thumbnail(thumbnail_file.to_s)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
info("▸ Successfully Pushed Theme")
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "create", "Create a new Theme on MagLoft"
|
163
|
+
option :token, type: :string, required: true
|
164
|
+
option :theme, type: :string, required: true, validator: OptionValidator
|
165
|
+
def create
|
166
|
+
info("▸ Creating new theme '#{options.theme}'")
|
167
|
+
|
168
|
+
error!("Missing yaml config 'name'") unless theme_config(:name)
|
169
|
+
error!("Missing yaml config 'base_version'") unless theme_config(:base_version)
|
170
|
+
theme = magloft_api.typeloft_themes.find_by_identifier(options.theme)
|
171
|
+
error!("This theme already exists") unless theme.nil?
|
172
|
+
magloft_api.typeloft_themes.create(identifier: options.theme, name: theme_config(:name), description: theme_config(:description), base_version: theme_config(:base_version))
|
173
|
+
info("▸ Theme successfully created!")
|
174
|
+
end
|
175
|
+
|
176
|
+
desc "delete", "Delete an existing Theme on MagLoft"
|
177
|
+
option :token, type: :string, required: true
|
178
|
+
option :theme, type: :string, required: true
|
179
|
+
def delete
|
180
|
+
info("▸ Deleting theme '#{options.theme}'")
|
181
|
+
theme = magloft_api.typeloft_themes.find_by_identifier(options.theme)
|
182
|
+
error!("Could not find a theme with identifier '#{options.theme}'") if theme.nil?
|
183
|
+
theme.destroy
|
184
|
+
info("▸ Theme successfully deleted!")
|
185
|
+
end
|
186
|
+
|
187
|
+
desc "list", "Delete an existing Theme on MagLoft"
|
188
|
+
option :token, type: :string, required: true
|
189
|
+
def list
|
190
|
+
info("▸ Listing themes")
|
191
|
+
magloft_api.typeloft_themes.all.each do |theme|
|
192
|
+
info("~> #{theme.identifier} (NAME: #{theme.name}, ACTIVE: #{theme.active ? 'YES' : 'NO'})")
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
|
+
require "logging"
|
1
2
|
module MagLove
|
2
3
|
module Helper
|
3
4
|
module LogHelper
|
4
|
-
@@logger = nil
|
5
|
-
|
6
5
|
def info(message)
|
7
6
|
logger.send(:info, message)
|
8
7
|
end
|
9
|
-
|
8
|
+
|
10
9
|
def debug(message)
|
11
10
|
logger.send(:debug, message)
|
12
11
|
end
|
@@ -21,21 +20,7 @@ module MagLove
|
|
21
20
|
end
|
22
21
|
|
23
22
|
def logger
|
24
|
-
|
25
|
-
if @@logger.nil?
|
26
|
-
Logging.color_scheme("bright",
|
27
|
-
levels: { debug: :blue, info: :green, warn: :yellow, error: :red, fatal: [:white, :on_red] },
|
28
|
-
date: :blue,
|
29
|
-
mdc: :cyan,
|
30
|
-
logger: :cyan,
|
31
|
-
message: :black
|
32
|
-
)
|
33
|
-
Logging.appenders.stdout("stdout", layout: Logging.layouts.pattern( pattern: '[%d] %-5l %-16X{command} %x %m\n', color_scheme: 'bright' ))
|
34
|
-
@@logger = Logging::Logger.new(self.class.name)
|
35
|
-
@@logger.level = :info
|
36
|
-
@@logger.add_appenders('stdout')
|
37
|
-
end
|
38
|
-
@@logger
|
23
|
+
Maglove.logger
|
39
24
|
end
|
40
25
|
end
|
41
26
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'filewatcher'
|
2
|
+
require "faye"
|
3
|
+
module MagLove
|
4
|
+
module Middleware
|
5
|
+
class LiveReload
|
6
|
+
include Workspace
|
7
|
+
attr_reader :app, :options
|
8
|
+
|
9
|
+
def initialize(app, options = {})
|
10
|
+
@app = app
|
11
|
+
@options = options
|
12
|
+
@theme = options[:theme]
|
13
|
+
@templates = options[:templates]
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
if env["PATH_INFO"] == @options[:mount]
|
18
|
+
request = Rack::Request.new(env)
|
19
|
+
@ws = Faye::WebSocket.new(request.env, [], {})
|
20
|
+
@ws.onmessage = lambda do |event|
|
21
|
+
message = JSON.parse(event.data)
|
22
|
+
command = message["command"]
|
23
|
+
handle_command(command, message)
|
24
|
+
end
|
25
|
+
@ws.onclose = ->(event) { clear_watcher! if @watcher }
|
26
|
+
@ws.rack_response
|
27
|
+
else
|
28
|
+
@app.call(env)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def clear_watcher!
|
33
|
+
@ws = nil
|
34
|
+
@watcher.stop
|
35
|
+
@thread.join
|
36
|
+
@watcher = nil
|
37
|
+
@thread = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def send_command(command, data = {})
|
41
|
+
data[:command] = command
|
42
|
+
@ws.send(JSON.dump(data)) if @ws
|
43
|
+
end
|
44
|
+
|
45
|
+
def handle_command(command, data = {})
|
46
|
+
if command == "init"
|
47
|
+
send_command("init", { templates: @templates })
|
48
|
+
elsif command == "watch"
|
49
|
+
watch
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def watch
|
54
|
+
patterns = [
|
55
|
+
theme_file("**/*.{haml,html,coffee,js,less,scss,css,yml}"),
|
56
|
+
theme_file("images/**/*.{jpg,jpeg,gif,png,svg}"),
|
57
|
+
theme_base_file("**/*.{coffee,js,less,scss,css}"),
|
58
|
+
theme_base_file("images/**/*.{jpg,jpeg,gif,png,svg}")
|
59
|
+
]
|
60
|
+
@watcher = FileWatcher.new(patterns.map(&:to_s))
|
61
|
+
@thread = Thread.new(@watcher) do |fw|
|
62
|
+
fw.watch do |filename, event|
|
63
|
+
if workspace_file(".", filename).exists? and event != :delete
|
64
|
+
if filename =~ %r{^src/base/#{theme_config(:base_version)}/.*\.coffee}
|
65
|
+
path = "theme.coffee"
|
66
|
+
elsif filename =~ %r{^src/base/#{theme_config(:base_version)}/.*\.less}
|
67
|
+
path = "theme.less"
|
68
|
+
elsif filename =~ %r{^src/base/#{theme_config(:base_version)}/.*\.scss}
|
69
|
+
path = "theme.scss"
|
70
|
+
elsif filename =~ %r{^src/themes/#{@theme}/.*\.less}
|
71
|
+
path = "theme.less"
|
72
|
+
elsif filename =~ %r{^src/themes/#{@theme}/.*\.scss}
|
73
|
+
path = "theme.scss"
|
74
|
+
elsif filename =~ %r{^src/themes/#{@theme}/.*\.coffee}
|
75
|
+
path = "theme.coffee"
|
76
|
+
else
|
77
|
+
path = filename.gsub("src/themes/#{@theme}/", '')
|
78
|
+
end
|
79
|
+
asset = theme_file(path).asset
|
80
|
+
if asset.write!
|
81
|
+
case asset.output_type
|
82
|
+
when "html"
|
83
|
+
template = path.match(%r{templates/(.*)\.haml})[1]
|
84
|
+
send_command("html", { template: template, contents: asset.contents })
|
85
|
+
when "css"
|
86
|
+
send_command("css", { contents: asset.contents })
|
87
|
+
when "js"
|
88
|
+
send_command("js", { contents: asset.contents })
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -4,42 +4,41 @@ module MagLove
|
|
4
4
|
class PhantomScript
|
5
5
|
attr_accessor :running, :script, :options, :path, :log_file
|
6
6
|
|
7
|
-
def initialize(script, options={})
|
7
|
+
def initialize(script, options = {})
|
8
8
|
@running = false
|
9
9
|
@script = script
|
10
10
|
@options = options.merge({
|
11
11
|
script_path: Gem.datadir("maglove")
|
12
12
|
})
|
13
13
|
@path = File.absolute_path(File.join(@options[:script_path], "#{@script}.js"))
|
14
|
-
throw "Script #{script} not found at #{@path}"
|
14
|
+
throw "Script #{script} not found at #{@path}" unless File.exist?(@path)
|
15
15
|
@log_file = Tempfile.new('pslog')
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def run(*args)
|
19
19
|
start = Time.now
|
20
20
|
@running = true
|
21
21
|
cmd = "phantomjs #{@path} #{@log_file.path} #{args.join(' ')}"
|
22
|
-
|
22
|
+
|
23
23
|
# log phantomjs info
|
24
|
-
@log_thread = Thread.new
|
24
|
+
@log_thread = Thread.new do
|
25
25
|
f = File.open(@log_file.path, "r")
|
26
26
|
f.seek(0, IO::SEEK_END)
|
27
27
|
puts "phantom@0ms ▸ (start)"
|
28
|
-
while @running
|
28
|
+
while @running
|
29
29
|
select([f])
|
30
30
|
line = f.gets
|
31
31
|
puts "phantom@#{((Time.now - start) * 1000.0).to_i}ms ▸ #{line}" if line
|
32
32
|
end
|
33
|
-
|
34
|
-
|
33
|
+
end
|
34
|
+
|
35
35
|
# run command and return result
|
36
36
|
result = `#{cmd}`
|
37
37
|
@running = false
|
38
|
-
|
38
|
+
result == "ERROR" ? false : result
|
39
39
|
ensure
|
40
40
|
@log_file.close
|
41
41
|
@log_file.unlink
|
42
42
|
end
|
43
|
-
|
44
43
|
end
|
45
44
|
end
|
data/lib/maglove/server.rb
CHANGED
@@ -1,90 +1,58 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'maglove/middleware/live_reload'
|
3
|
+
|
1
4
|
module MagLove
|
2
5
|
class Server
|
3
|
-
include
|
4
|
-
|
5
|
-
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
6
|
+
include Workspace
|
7
|
+
attr_reader :app, :options
|
8
|
+
|
9
|
+
def self.start(port, theme, templates)
|
10
|
+
app = Rack::Builder.new do
|
11
|
+
use MagLove::Middleware::LiveReload, mount: "/maglove", theme: theme, templates: templates
|
12
|
+
use Rack::Static, urls: ["/fonts", "/themes"], root: "dist"
|
13
|
+
run MagLove::Server.new(theme: theme, templates: templates, port: port)
|
14
|
+
end
|
15
|
+
Rack::Server.start(app: app, Port: port, server: :puma)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(options)
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
template = env["PATH_INFO"].sub("/", "")
|
24
|
+
if env["PATH_INFO"] == "/maglove.js"
|
25
|
+
[200, { 'Content-Type' => 'text/javascript' }, [gem_dir.file("maglove.js").read]]
|
26
|
+
elsif env["PATH_INFO"] == "/maglove.css"
|
27
|
+
[200, { 'Content-Type' => 'text/css' }, [gem_dir.file("maglove.css").read]]
|
28
|
+
elsif @options[:templates].include?(template)
|
29
|
+
[200, { 'Content-Type' => 'text/html' }, [process(template)]]
|
30
|
+
else
|
31
|
+
[200, { 'Content-Type' => 'text/html' }, [gem_dir.file("index.haml").read_hamloft(@options)]]
|
23
32
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
33
|
+
end
|
34
|
+
|
35
|
+
def process(template)
|
36
|
+
css_contents = theme_dir(root: "dist").file("theme.css").read
|
37
|
+
js_contents = theme_dir(root: "dist").file("theme.js").read
|
38
|
+
template_file = theme_dir.file("templates/#{template}.haml")
|
39
|
+
gem_dir.file("maglove.haml").read_hamloft(theme: @options[:theme], contents: template_file.asset.contents, css_contents: css_contents, js_contents: js_contents, template: template, asset_uri: ".", port: @options[:port] || 3000)
|
40
|
+
end
|
41
|
+
|
42
|
+
def setup(theme)
|
43
|
+
@widget_stamps = {}
|
44
|
+
Dir["widgets/*.rb"].each { |file| @widget_stamps[file] = File.mtime(file).to_i }
|
45
|
+
@options[:templates].each do |template|
|
28
46
|
mount("/#{template}") do |req, res|
|
29
|
-
|
30
47
|
Dir["widgets/*.rb"].each do |file|
|
31
48
|
stamp = File.mtime(file).to_i
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
49
|
+
next unless @widget_stamps[file] and @widget_stamps[file] < stamp
|
50
|
+
@widget_stamps[file] = stamp
|
51
|
+
debug("▸ reloading widget: #{file}")
|
52
|
+
load(file)
|
37
53
|
end
|
38
|
-
|
39
|
-
debug("▸ rendering template: #{template}")
|
40
|
-
|
41
|
-
# Prepare variables
|
42
|
-
variables = {}
|
43
|
-
variables_yaml = theme_contents("templates/#{template}.yml", self.theme)
|
44
|
-
variables = YAML.load(variables_yaml).with_indifferent_access if variables_yaml
|
45
|
-
variables[:theme] = self.theme
|
46
|
-
|
47
|
-
# Render template
|
48
|
-
template_file = theme_glob("templates/#{template}.{html,twig,haml}", self.theme).first
|
49
|
-
if !template_file.nil?
|
50
|
-
asset = MagLove::Asset::Theme.new(template_file, self.theme, variables)
|
51
|
-
contents = asset.contents
|
52
|
-
else
|
53
|
-
contents = "<p style='text-align: center; margin-top: 12px;'>ERROR: Template '#{template}' not found!</p>"
|
54
|
-
end
|
55
|
-
|
56
|
-
# render editor view
|
57
|
-
if File.exists?(theme_path("theme.haml", self.theme))
|
58
|
-
layout_path = theme_path("theme.haml", self.theme)
|
59
|
-
else
|
60
|
-
layout_path = File.join(Gem.datadir("maglove"), "sdk.haml")
|
61
|
-
end
|
62
|
-
haml_contents = File.read(layout_path)
|
63
|
-
res.body = Hamloft.render(haml_contents, theme: self.theme, contents: contents, templates: templates, template: template)
|
64
54
|
end
|
65
55
|
end
|
66
|
-
|
67
|
-
self.webrick.mount "/issue", Hpub::IssueServlet
|
68
|
-
self.webrick.mount "/manifest.json", Hpub::ManifestServlet
|
69
|
-
end
|
70
|
-
|
71
|
-
def mount_template(path, view, options={})
|
72
|
-
mount(path) do |req, res|
|
73
|
-
engine = Haml::Engine.new(parse_view(view))
|
74
|
-
res.body = engine.render(Object.new, options)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def mount(path, &block)
|
79
|
-
self.webrick.mount_proc(path, &block)
|
80
|
-
end
|
81
|
-
|
82
|
-
def run!
|
83
|
-
trap 'INT' do
|
84
|
-
self.webrick.shutdown
|
85
|
-
end
|
86
|
-
self.webrick.start
|
87
56
|
end
|
88
|
-
|
89
57
|
end
|
90
58
|
end
|
@@ -17,26 +17,25 @@ module MagLove
|
|
17
17
|
|
18
18
|
def evaluate(scope, locals, &block)
|
19
19
|
@output = CoffeeScript.compile(@data, options)
|
20
|
-
|
20
|
+
|
21
21
|
# handle includes
|
22
22
|
@output.gsub(/^include\("([^"]+)"\);$/) do |match|
|
23
23
|
path = Regexp.last_match[1]
|
24
24
|
path = "#{path}.coffee" if File.extname(path).empty?
|
25
25
|
include_path = File.absolute_path(path, File.dirname(file))
|
26
|
-
|
26
|
+
|
27
27
|
# check if base path exists
|
28
|
-
|
28
|
+
unless File.exist?(include_path)
|
29
29
|
include_path = File.absolute_path(path, locals[:base_path])
|
30
30
|
end
|
31
|
-
|
32
|
-
if File.
|
31
|
+
|
32
|
+
if File.exist?(include_path)
|
33
33
|
include_template = ::Tilt.new(include_path)
|
34
34
|
include_template.render(Object.new, locals)
|
35
35
|
else
|
36
36
|
raise "Path not found: #{include_path}"
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
40
39
|
end
|
41
40
|
|
42
41
|
def allows_script?
|
@@ -45,3 +44,5 @@ module MagLove
|
|
45
44
|
end
|
46
45
|
end
|
47
46
|
end
|
47
|
+
|
48
|
+
Tilt.mappings["coffee"] = [MagLove::Tilt::CoffeeTemplate]
|
@@ -2,19 +2,19 @@ module MagLove
|
|
2
2
|
module Tilt
|
3
3
|
class HamlTemplate < ::Tilt::Template
|
4
4
|
self.default_mime_type = 'theme/html'
|
5
|
-
|
5
|
+
|
6
6
|
def prepare
|
7
|
-
|
8
7
|
end
|
9
8
|
|
10
9
|
def evaluate(scope, locals, &block)
|
11
|
-
@output ||= Hamloft::Engine.new(data, {remove_whitespace: true}).render(scope, locals)
|
10
|
+
@output ||= Hamloft::Engine.new(data, { remove_whitespace: true }).render(scope, locals)
|
12
11
|
end
|
13
12
|
|
14
13
|
def allows_script?
|
15
14
|
false
|
16
15
|
end
|
17
16
|
end
|
18
|
-
|
19
17
|
end
|
20
18
|
end
|
19
|
+
|
20
|
+
Tilt.mappings["haml"] = [MagLove::Tilt::HamlTemplate]
|