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.
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]