html_mockup 0.8.4 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -2
- data/.travis.yml +12 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +5 -0
- data/README.md +95 -0
- data/Rakefile +9 -4
- data/bin/mockup +1 -1
- data/doc/cli.md +46 -0
- data/doc/mockupfile.md +3 -0
- data/doc/templating.md +88 -0
- data/html_mockup.gemspec +8 -4
- data/lib/html_mockup/cli.rb +57 -65
- data/lib/html_mockup/cli/command.rb +23 -0
- data/lib/html_mockup/cli/generate.rb +5 -0
- data/lib/html_mockup/cli/release.rb +10 -0
- data/lib/html_mockup/cli/serve.rb +29 -0
- data/lib/html_mockup/generators.rb +18 -1
- data/lib/html_mockup/generators/generator.rb +23 -0
- data/lib/html_mockup/generators/new.rb +4 -3
- data/lib/html_mockup/generators/templates/generator.tt +13 -0
- data/lib/html_mockup/project.rb +11 -11
- data/lib/html_mockup/release.rb +3 -3
- data/lib/html_mockup/resolver.rb +51 -28
- data/lib/html_mockup/template.rb +95 -11
- data/roger.gemspec +29 -0
- data/test/project/Gemfile +2 -1
- data/test/project/Gemfile.lock +17 -12
- data/test/project/Mockupfile +3 -0
- data/test/project/html/formats/index.html +1 -0
- data/test/project/html/formats/json.json.erb +0 -0
- data/test/project/html/layouts/content-for.html.erb +17 -0
- data/test/project/html/mockup/encoding.html +3 -0
- data/test/project/html/partials/load_path.html.erb +3 -0
- data/test/project/layouts/test.html.erb +8 -1
- data/test/project/layouts/yield.html.erb +1 -0
- data/test/project/lib/generators/test.rb +9 -0
- data/test/project/partials/formats/erb.html.erb +1 -0
- data/test/project/partials/partials-test.html.erb +1 -0
- data/test/project/partials/test/front_matter.html.erb +1 -0
- data/test/project/partials/test/json.json.erb +1 -0
- data/test/project/partials/test/simple.html.erb +1 -0
- data/test/project/partials2/partials2-test.html.erb +1 -0
- data/test/unit/cli_test.rb +12 -0
- data/test/unit/generators_test.rb +75 -0
- data/test/unit/release/cleaner_test.rb +13 -8
- data/test/unit/resolver_test.rb +92 -0
- data/test/unit/template_test.rb +127 -0
- metadata +100 -8
- data/README.rdoc +0 -89
- data/test/generator-subcommand.rb +0 -54
@@ -0,0 +1,23 @@
|
|
1
|
+
module HtmlMockup
|
2
|
+
|
3
|
+
class Cli::Command < Thor::Group
|
4
|
+
|
5
|
+
class_option :verbose,
|
6
|
+
:desc => "Set's verbose output",
|
7
|
+
:aliases => ["-v"],
|
8
|
+
:default => false,
|
9
|
+
:type => :boolean
|
10
|
+
|
11
|
+
def initialize_project
|
12
|
+
@project = Cli::Base.project
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def project_banner(project)
|
18
|
+
puts " Html: \"#{project.html_path}\""
|
19
|
+
puts " Partials: \"#{project.partial_path}\""
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module HtmlMockup
|
2
|
+
class Cli::Serve < Cli::Command
|
3
|
+
|
4
|
+
|
5
|
+
desc "Serve the current project"
|
6
|
+
|
7
|
+
class_options :port => :string, # Defaults to 9000
|
8
|
+
:handler => :string, # The handler to use (defaults to mongrel)
|
9
|
+
:validate => :boolean # Run validation?
|
10
|
+
|
11
|
+
def serve
|
12
|
+
|
13
|
+
server_options = {}
|
14
|
+
options.each{|k,v| server_options[k.to_sym] = v }
|
15
|
+
server_options[:server] = {}
|
16
|
+
[:port, :handler, :validate].each do |k|
|
17
|
+
server_options[:server][k] = server_options.delete(k) if server_options.has_key?(k)
|
18
|
+
end
|
19
|
+
|
20
|
+
server = @project.server
|
21
|
+
server.set_options(server_options[:server])
|
22
|
+
|
23
|
+
puts "Running HtmlMockup with #{server.handler.inspect} on port #{server.port}"
|
24
|
+
puts project_banner(@project)
|
25
|
+
|
26
|
+
server.run!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,6 +1,23 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/group'
|
3
|
+
|
1
4
|
module HtmlMockup
|
2
5
|
module Generators
|
6
|
+
|
7
|
+
class Base < Cli::Command
|
8
|
+
def self.register(sub)
|
9
|
+
name = sub.to_s.sub(/Generator$/, "").sub(/^.*Generators::/,"").downcase
|
10
|
+
usage = "#{name} #{sub.arguments.map{ |arg| arg.banner }.join(" ")}"
|
11
|
+
long_desc = sub.desc || "Run #{name} generator"
|
12
|
+
|
13
|
+
Cli::Generate.register sub, name, usage, long_desc
|
14
|
+
Cli::Generate.tasks[name].options = sub.class_options if sub.class_options
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
3
18
|
end
|
4
19
|
end
|
5
20
|
|
6
|
-
|
21
|
+
# Default generators
|
22
|
+
require File.dirname(__FILE__) + "/generators/new"
|
23
|
+
require File.dirname(__FILE__) + "/generators/generator"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class HtmlMockup::Generators::GeneratorGenerator < HtmlMockup::Generators::Base
|
2
|
+
|
3
|
+
include Thor::Actions
|
4
|
+
|
5
|
+
desc "Create your own generator for html_mockup"
|
6
|
+
argument :name, :type => :string, :required => true, :desc => "Name of the new generator"
|
7
|
+
argument :path, :type => :string, :required => true, :desc => "Path to generate the new generator"
|
8
|
+
# class_option :template, :type => :string, :aliases => ["-t"], :desc => "Template to use, can be a path or a git repository remote, uses built in minimal as default"
|
9
|
+
|
10
|
+
def self.source_root
|
11
|
+
File.dirname(__FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_lib_file
|
15
|
+
destination = "#{path}/#{name}_generator.rb"
|
16
|
+
template('templates/generator.tt', destination)
|
17
|
+
say "Add `require #{destination}` to your mockup file and run mockup generate #{name}."
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
HtmlMockup::Generators::Base.register HtmlMockup::Generators::GeneratorGenerator
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'shellwords'
|
2
2
|
|
3
|
-
class HtmlMockup::Generators::
|
3
|
+
class HtmlMockup::Generators::NewGenerator < Thor::Group
|
4
4
|
|
5
5
|
include Thor::Actions
|
6
6
|
|
@@ -62,5 +62,6 @@ class HtmlMockup::Generators::New < Thor::Group
|
|
62
62
|
directory(".", ".")
|
63
63
|
end
|
64
64
|
|
65
|
-
|
66
|
-
|
65
|
+
end
|
66
|
+
|
67
|
+
HtmlMockup::Generators::Base.register HtmlMockup::Generators::NewGenerator
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class <%= name.capitalize %>Generator < HtmlMockup::Generators::Base
|
2
|
+
|
3
|
+
include Thor::Actions
|
4
|
+
|
5
|
+
desc "<%= name.capitalize %> generator helps you doing ... and ..."
|
6
|
+
argument :path, :type => :string, :required => true, :desc => "Path to generate the new generator"
|
7
|
+
|
8
|
+
def create_stuff
|
9
|
+
# your stuff
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
HtmlMockup::Generators::Base.register <%= name.capitalize %>Generator
|
data/lib/html_mockup/project.rb
CHANGED
@@ -33,8 +33,6 @@ module HtmlMockup
|
|
33
33
|
self.layouts_path = @options[:layouts_path]
|
34
34
|
self.shell = @options[:shell]
|
35
35
|
|
36
|
-
|
37
|
-
load_dependencies!
|
38
36
|
load_mockup!
|
39
37
|
end
|
40
38
|
|
@@ -57,27 +55,29 @@ module HtmlMockup
|
|
57
55
|
end
|
58
56
|
|
59
57
|
def partial_path=(p)
|
60
|
-
@partial_path = self.
|
58
|
+
@partial_path = self.single_or_multiple_paths(p)
|
61
59
|
end
|
62
60
|
alias :partials_path :partial_path
|
63
61
|
alias :partials_path= :partial_path=
|
64
62
|
|
65
63
|
def layouts_path=(p)
|
66
|
-
@layouts_path = self.
|
64
|
+
@layouts_path = self.single_or_multiple_paths(p)
|
67
65
|
end
|
68
66
|
|
69
67
|
protected
|
70
|
-
|
71
|
-
def load_dependencies!
|
72
|
-
if Object.const_defined?(:Bundler)
|
73
|
-
Bundler.require(:default)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
68
|
+
|
77
69
|
def load_mockup!
|
78
70
|
@mockupfile = Mockupfile.new(self)
|
79
71
|
@mockupfile.load
|
80
72
|
end
|
73
|
+
|
74
|
+
def single_or_multiple_paths(p)
|
75
|
+
if p.kind_of?(Array)
|
76
|
+
p.map{|tp| self.realpath_or_path(tp) }
|
77
|
+
else
|
78
|
+
self.realpath_or_path(p)
|
79
|
+
end
|
80
|
+
end
|
81
81
|
|
82
82
|
def realpath_or_path(path)
|
83
83
|
path = Pathname.new(path)
|
data/lib/html_mockup/release.rb
CHANGED
@@ -339,8 +339,8 @@ module HtmlMockup
|
|
339
339
|
|
340
340
|
commenters = {
|
341
341
|
:html => Proc.new{|s| "<!-- #{s} -->" },
|
342
|
-
:css => Proc.new{|s| "
|
343
|
-
:js => Proc.new{|s| "
|
342
|
+
:css => Proc.new{|s| "/*! #{s} */" },
|
343
|
+
:js => Proc.new{|s| "/*! #{s} */" }
|
344
344
|
}
|
345
345
|
|
346
346
|
commenter = commenters[options[:style]] || commenters[:js]
|
@@ -360,4 +360,4 @@ require File.dirname(__FILE__) + "/release/scm"
|
|
360
360
|
require File.dirname(__FILE__) + "/release/injector"
|
361
361
|
require File.dirname(__FILE__) + "/release/cleaner"
|
362
362
|
require File.dirname(__FILE__) + "/release/finalizers"
|
363
|
-
require File.dirname(__FILE__) + "/release/processors"
|
363
|
+
require File.dirname(__FILE__) + "/release/processors"
|
data/lib/html_mockup/resolver.rb
CHANGED
@@ -1,36 +1,55 @@
|
|
1
1
|
module HtmlMockup
|
2
2
|
class Resolver
|
3
|
+
|
4
|
+
attr_reader :load_paths
|
3
5
|
|
4
|
-
def initialize(
|
5
|
-
raise ArgumentError, "Resolver base path can't be nil" if
|
6
|
-
|
6
|
+
def initialize(paths)
|
7
|
+
raise ArgumentError, "Resolver base path can't be nil" if paths.nil?
|
8
|
+
|
9
|
+
# Convert to paths
|
10
|
+
@load_paths = [paths].flatten.map{|p| Pathname.new(p) }
|
7
11
|
end
|
8
12
|
|
9
13
|
# @param [String] url The url to resolve to a path
|
10
|
-
# @param [
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
# @param [Hash] options Options
|
15
|
+
#
|
16
|
+
# @option options [true,false] :exact_match Wether or not to match exact paths, this is mainly used in the path_to_url method to match .js, .css, etc files.
|
17
|
+
# @option options [String] :preferred_extension The part to chop off and re-add to search for more complex double-extensions. (Makes it possible to have context aware partials)
|
18
|
+
def find_template(url, options = {})
|
19
|
+
orig_path, qs, anch = strip_query_string_and_anchor(url.to_s)
|
20
|
+
|
21
|
+
options = {
|
22
|
+
:exact_match => false,
|
23
|
+
:preferred_extension => "html"
|
24
|
+
}.update(options)
|
25
|
+
|
26
|
+
paths = self.load_paths.map{|base| File.join(base, orig_path) }
|
15
27
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
path
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
paths.find do |path|
|
29
|
+
if options[:exact_match] && File.exist?(path)
|
30
|
+
return Pathname.new(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
# It's a directory, add "/index"
|
34
|
+
if File.directory?(path)
|
35
|
+
path = File.join(path, "index")
|
36
|
+
end
|
37
|
+
|
38
|
+
# 2. If it's preferred_extension, we strip of the extension
|
39
|
+
if path =~ /\.#{options[:preferred_extension]}\Z/
|
40
|
+
path.sub!(/\.#{options[:preferred_extension]}\Z/, "")
|
41
|
+
end
|
42
|
+
|
43
|
+
extensions = Tilt.default_mapping.template_map.keys + Tilt.default_mapping.lazy_map.keys
|
31
44
|
|
32
|
-
|
33
|
-
|
45
|
+
# We have to re-add preferred_extension again as we have stripped it in in step 2!
|
46
|
+
extensions += extensions.map{|ext| "#{options[:preferred_extension]}.#{ext}"}
|
47
|
+
|
48
|
+
if found_extension = extensions.find { |ext| File.exist?(path + "." + ext) }
|
49
|
+
return Pathname.new(path + "." + found_extension)
|
50
|
+
end
|
51
|
+
|
52
|
+
false #Next iteration
|
34
53
|
end
|
35
54
|
end
|
36
55
|
alias :url_to_path :find_template
|
@@ -39,11 +58,15 @@ module HtmlMockup
|
|
39
58
|
# Convert a disk path on file to an url
|
40
59
|
def path_to_url(path, relative_to = nil)
|
41
60
|
|
42
|
-
path
|
61
|
+
# Find the parent path we're in
|
62
|
+
path = Pathname.new(path).realpath
|
63
|
+
base = self.load_paths.find{|lp| path.to_s =~ /\A#{Regexp.escape(lp.realpath.to_s)}/ }
|
64
|
+
|
65
|
+
path = path.relative_path_from(base).cleanpath
|
43
66
|
|
44
67
|
if relative_to
|
45
68
|
if relative_to.to_s =~ /\A\//
|
46
|
-
relative_to = Pathname.new(File.dirname(relative_to.to_s)).relative_path_from(
|
69
|
+
relative_to = Pathname.new(File.dirname(relative_to.to_s)).relative_path_from(base).cleanpath
|
47
70
|
else
|
48
71
|
relative_to = Pathname.new(File.dirname(relative_to.to_s))
|
49
72
|
end
|
@@ -62,7 +85,7 @@ module HtmlMockup
|
|
62
85
|
path, qs, anch = strip_query_string_and_anchor(url)
|
63
86
|
|
64
87
|
# Get disk path
|
65
|
-
if true_path = self.url_to_path(path, true)
|
88
|
+
if true_path = self.url_to_path(path, :exact_match => true)
|
66
89
|
path = self.path_to_url(true_path, relative_to_path)
|
67
90
|
path += qs if qs
|
68
91
|
path += anch if anch
|
data/lib/html_mockup/template.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'tilt'
|
2
|
+
require 'mime/types'
|
2
3
|
require 'yaml'
|
3
4
|
require 'ostruct'
|
4
5
|
|
5
6
|
require File.dirname(__FILE__) + "/mockup_template"
|
6
7
|
|
8
|
+
# We're enforcing Encoding to UTF-8
|
9
|
+
Encoding.default_external = "UTF-8"
|
10
|
+
|
7
11
|
module HtmlMockup
|
8
12
|
|
9
13
|
class Template
|
@@ -44,28 +48,79 @@ module HtmlMockup
|
|
44
48
|
|
45
49
|
def render(env = {})
|
46
50
|
context = TemplateContext.new(self, env)
|
47
|
-
locals = {:document => OpenStruct.new(self.data)}
|
48
51
|
|
49
52
|
if @layout_template
|
50
|
-
|
51
|
-
|
53
|
+
content_for_layout = self.template.render(context, {}) # yields
|
54
|
+
|
55
|
+
@layout_template.render(context, {}) do |content_for|
|
56
|
+
if content_for
|
57
|
+
context._content_for_blocks[content_for]
|
58
|
+
else
|
59
|
+
content_for_layout
|
60
|
+
end
|
52
61
|
end
|
53
62
|
else
|
54
|
-
self.template.render(context,
|
63
|
+
self.template.render(context, {})
|
55
64
|
end
|
56
65
|
end
|
57
66
|
|
58
67
|
def find_template(name, path_type)
|
59
68
|
raise(ArgumentError, "path_type must be one of :partials_path or :layouts_path") unless [:partials_path, :layouts_path].include?(path_type)
|
60
69
|
|
70
|
+
return nil unless @options[path_type]
|
71
|
+
|
61
72
|
@resolvers ||= {}
|
62
73
|
@resolvers[path_type] ||= Resolver.new(@options[path_type])
|
63
74
|
|
64
|
-
@resolvers[path_type].
|
65
|
-
end
|
75
|
+
@resolvers[path_type].find_template(name, :preferred_extension => self.target_extension)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Try to infer the final extension of the output file.
|
79
|
+
def target_extension
|
80
|
+
return @target_extension if @target_extension
|
81
|
+
|
82
|
+
if type = MIME::Types[self.target_mime_type].first
|
83
|
+
# Dirty little hack to enforce the use of .html instead of .htm
|
84
|
+
if type.sub_type == "html"
|
85
|
+
@target_extension = "html"
|
86
|
+
else
|
87
|
+
@target_extension = type.extensions.first
|
88
|
+
end
|
89
|
+
else
|
90
|
+
@target_extension = File.extname(self.source_path.to_s).sub(/^\./, "")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def source_extension
|
95
|
+
parts = File.basename(File.basename(self.source_path.to_s)).split(".")
|
96
|
+
if parts.size > 2
|
97
|
+
parts[-2..-1].join(".")
|
98
|
+
else
|
99
|
+
File.extname(self.source_path.to_s).sub(/^\./, "")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Try to figure out the mime type based on the Tilt class and if that doesn't
|
104
|
+
# work we try to infer the type by looking at extensions (needed for .erb)
|
105
|
+
def target_mime_type
|
106
|
+
mime = self.template.class.default_mime_type
|
107
|
+
return mime if mime
|
108
|
+
|
109
|
+
path = File.basename(self.source_path.to_s)
|
110
|
+
mime = MIME::Types.type_for(path).first
|
111
|
+
return mime.to_s if mime
|
112
|
+
|
113
|
+
parts = File.basename(path).split(".")
|
114
|
+
if parts.size > 2
|
115
|
+
mime = MIME::Types.type_for(parts[0..-2].join(".")).first
|
116
|
+
return mime.to_s if mime
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
end
|
66
121
|
|
67
122
|
protected
|
68
|
-
|
123
|
+
|
69
124
|
# Get the front matter portion of the file and extract it.
|
70
125
|
def extract_front_matter(source)
|
71
126
|
fm_regex = /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
@@ -91,26 +146,55 @@ module HtmlMockup
|
|
91
146
|
end
|
92
147
|
|
93
148
|
class TemplateContext
|
94
|
-
|
149
|
+
attr_accessor :_content_for_blocks
|
150
|
+
|
95
151
|
def initialize(template, env={})
|
96
|
-
@_template, @_env = template, env
|
152
|
+
@_template, @_env = template, @_content_for_blocks = {}, env
|
97
153
|
end
|
98
154
|
|
155
|
+
# The current HtmlMockup::Template in use
|
99
156
|
def template
|
100
157
|
@_template
|
101
158
|
end
|
159
|
+
|
160
|
+
# Access to the front-matter of the document (if any)
|
161
|
+
def document
|
162
|
+
@_data ||= OpenStruct.new(self.template.data)
|
163
|
+
end
|
102
164
|
|
165
|
+
# The current environment variables.
|
103
166
|
def env
|
104
167
|
@_env
|
105
168
|
end
|
169
|
+
|
170
|
+
# Capture content in blocks in the template for later use in the layout.
|
171
|
+
# Currently only works in ERB templates. Use like this in the template:
|
172
|
+
#
|
173
|
+
# ```
|
174
|
+
# <% content_for :name %> bla bla <% end %>
|
175
|
+
# ```
|
176
|
+
#
|
177
|
+
# Place it like this in the layout:
|
178
|
+
#
|
179
|
+
# ```
|
180
|
+
# <%= yield :name %>
|
181
|
+
# ```
|
182
|
+
def content_for(block_name, &capture)
|
183
|
+
raise ArgumentError, "content_for works only with ERB Templates" if !self.template.template.kind_of?(Tilt::ERBTemplate)
|
184
|
+
eval "@_erbout_tmp = _erbout", capture.binding
|
185
|
+
eval "_erbout = \"\"", capture.binding
|
186
|
+
t = Tilt::ERBTemplate.new(){ "<%= yield %>" }
|
187
|
+
@_content_for_blocks[block_name] = t.render(&capture)
|
188
|
+
return nil
|
189
|
+
ensure
|
190
|
+
eval "_erbout = @_erbout_tmp", capture.binding
|
191
|
+
end
|
106
192
|
|
107
193
|
def partial(name, options = {})
|
108
194
|
if template_path = self.template.find_template(name, :partials_path)
|
109
|
-
# puts "Rendering partial #{name}, with template #{template_path}"
|
110
195
|
partial_template = Tilt.new(template_path.to_s)
|
111
196
|
partial_template.render(self, options[:locals] || {})
|
112
197
|
elsif template_path = self.template.find_template(name + ".part", :partials_path)
|
113
|
-
# puts "Rendering old-style partial #{name}, with template #{template_path}"
|
114
198
|
template = Tilt::ERBTemplate.new(template_path.to_s)
|
115
199
|
context = MockupTemplate::TemplateContext.new(options[:locals] || {})
|
116
200
|
template.render(context, :env => self.env)
|