maglove 0.8.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +157 -0
  4. data/Gemfile.lock +59 -49
  5. data/bin/maglove +13 -13
  6. data/data/maglove/export.haml +11 -0
  7. data/data/maglove/index.haml +17 -0
  8. data/data/maglove/maglove.css +62 -0
  9. data/data/maglove/maglove.haml +18 -0
  10. data/data/maglove/maglove.js +68 -0
  11. data/lib/block_resolver.rb +6 -0
  12. data/lib/ext/thor/option.rb +43 -0
  13. data/lib/magloft/api.rb +39 -0
  14. data/lib/magloft/api_caller.rb +67 -0
  15. data/lib/magloft/remote_collection.rb +50 -0
  16. data/lib/magloft/remote_resource.rb +124 -0
  17. data/lib/magloft/transformable.rb +11 -0
  18. data/lib/magloft/typeloft_block.rb +18 -0
  19. data/lib/magloft/typeloft_image.rb +18 -0
  20. data/lib/magloft/typeloft_template.rb +18 -0
  21. data/lib/magloft/typeloft_theme.rb +41 -0
  22. data/lib/magloft.rb +3 -0
  23. data/lib/maglove/application.rb +10 -12
  24. data/lib/maglove/asset/theme.rb +37 -32
  25. data/lib/maglove/commands/assets.rb +85 -0
  26. data/lib/maglove/commands/base.rb +55 -0
  27. data/lib/maglove/commands/fonts.rb +69 -0
  28. data/lib/maglove/commands/main.rb +24 -0
  29. data/lib/maglove/commands/theme.rb +197 -0
  30. data/lib/maglove/helper/log_helper.rb +3 -18
  31. data/lib/maglove/middleware/live_reload.rb +97 -0
  32. data/lib/maglove/phantom_script.rb +9 -10
  33. data/lib/maglove/server.rb +46 -78
  34. data/lib/maglove/tilt/coffee_template.rb +7 -6
  35. data/lib/maglove/tilt/haml_template.rb +4 -4
  36. data/lib/maglove/tilt/js_template.rb +8 -8
  37. data/lib/maglove/tilt/less_template.rb +5 -4
  38. data/lib/maglove/tilt/scss_template.rb +17 -11
  39. data/lib/maglove/tilt/yaml_template.rb +3 -2
  40. data/lib/maglove/version.rb +1 -1
  41. data/lib/maglove/workspace.rb +41 -0
  42. data/lib/maglove.rb +38 -49
  43. data/lib/powersnap.rb +24 -0
  44. data/lib/workspace/workspace_dir/archive.rb +18 -0
  45. data/lib/workspace/workspace_dir.rb +98 -0
  46. data/lib/workspace/workspace_file/archive.rb +45 -0
  47. data/lib/workspace/workspace_file/media.rb +19 -0
  48. data/lib/workspace/workspace_file/net.rb +18 -0
  49. data/lib/workspace/workspace_file/parse.rb +21 -0
  50. data/lib/workspace/workspace_file.rb +99 -0
  51. data/lib/workspace.rb +11 -0
  52. data/maglove.gemspec +12 -12
  53. metadata +100 -86
  54. data/data/maglove/dump.haml +0 -58
  55. data/data/maglove/sdk.haml +0 -27
  56. data/lib/ext/commander/command.rb +0 -32
  57. data/lib/ext/commander/methods.rb +0 -8
  58. data/lib/maglove/asset/base_theme.rb +0 -17
  59. data/lib/maglove/command/compile.rb +0 -44
  60. data/lib/maglove/command/compress.rb +0 -28
  61. data/lib/maglove/command/copy.rb +0 -35
  62. data/lib/maglove/command/core.rb +0 -23
  63. data/lib/maglove/command/font.rb +0 -80
  64. data/lib/maglove/command/server.rb +0 -16
  65. data/lib/maglove/command/sync.rb +0 -17
  66. data/lib/maglove/command/theme.rb +0 -175
  67. data/lib/maglove/command/util.rb +0 -45
  68. data/lib/maglove/helper/asset_helper.rb +0 -24
  69. data/lib/maglove/helper/command_helper.rb +0 -67
  70. data/lib/maglove/helper/theme_helper.rb +0 -105
  71. data/lib/maglove/server/hpub.rb +0 -185
  72. data/lib/maglove/template/tumblr.rb +0 -81
  73. 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
- # reset logger on task change
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}" if not File.exists?(@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 do
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
- (result == "ERROR") ? false : result
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
@@ -1,90 +1,58 @@
1
+ require 'rack'
2
+ require 'maglove/middleware/live_reload'
3
+
1
4
  module MagLove
2
5
  class Server
3
- include Commander::Methods
4
- attr_accessor :port, :root, :theme, :webrick
5
-
6
- def initialize(theme, port=3001)
7
- self.theme = theme
8
- self.port = port
9
- self.root = "dist/"
10
- @widget_stamps = {}
11
-
12
- # create server
13
- self.webrick = WEBrick::HTTPServer.new(
14
- Port: self.port,
15
- DocumentRoot: self.root,
16
- Logger: WEBrick::Log.new("/dev/null"),
17
- AccessLog: []
18
- )
19
-
20
- # create widget hash maps
21
- Dir["widgets/*.rb"].each do |file|
22
- @widget_stamps[file] = File.mtime(file).to_i
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
- # template view actions
26
- templates = theme_config(:templates, self.theme)
27
- templates.each do |template|
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
- if @widget_stamps[file] and @widget_stamps[file] < stamp
33
- @widget_stamps[file] = stamp
34
- debug("▸ reloading widget: #{file}")
35
- load(file)
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
- if not File.exists?(include_path)
28
+ unless File.exist?(include_path)
29
29
  include_path = File.absolute_path(path, locals[:base_path])
30
30
  end
31
-
32
- if File.exists?(include_path)
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]