html_mockup 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.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)
|