html_mockup 0.4.0 → 0.5.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.
@@ -0,0 +1,53 @@
1
+ # Use the ruby-yui-compressor gem.
2
+ require 'yui/compressor'
3
+
4
+ module HtmlMockup::Release::Processors
5
+ class Yuicompressor < Base
6
+
7
+ # Compresses all JS and CSS files, it will keep all lines before
8
+ #
9
+ # /* -------------------------------------------------------------------------------- */
10
+ #
11
+ # (80 dashes)
12
+ #
13
+ # @options options [Array] match Files to match, default to ["**/*.{css,js}"]
14
+ # @options options [Regexp] :delimiter An array of header delimiters. Defaults to the one above. The delimiter will be removed from the output.
15
+ # @options options [Array[Regexp]] :skip An array of file regular expressions to specifiy which files to skip. Defaults to [/javascripts\/vendor\/.\*.js\Z/, /_doc\/.*/]
16
+ def call(release, options={})
17
+ options = {
18
+ :match => ["**/*.{css,js}"],
19
+ :skip => [/javascripts\/vendor\/.*\.js\Z/, /_doc\/.*/],
20
+ :delimiter => Regexp.escape("/* -------------------------------------------------------------------------------- */")
21
+ }.update(options)
22
+
23
+ compressor_options = {:line_break => 80}
24
+ css_compressor = YUI::CssCompressor.new(compressor_options)
25
+ js_compressor = YUI::JavaScriptCompressor.new(compressor_options)
26
+
27
+ # Add version numbers and minify the files
28
+ release.get_files(options[:match], options[:skip]).each do |f|
29
+ type = f[/\.(.+)$/,1]
30
+
31
+ data = File.read(f);
32
+ File.open(f,"w") do |fh|
33
+
34
+ # Extract header and store for later use
35
+ header = data[/\A(.+?)\n#{options[:delimiter]}\s*\n/m,1]
36
+ minified = [header]
37
+
38
+ # Actual minification
39
+ release.log self, "Minifying #{f}"
40
+ case type
41
+ when "css"
42
+ minified << css_compressor.compress(data)
43
+ when "js"
44
+ minified << js_compressor.compress(data)
45
+ end
46
+
47
+ fh.write minified.join("\n")
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,11 @@
1
+ module HtmlMockup::Release::Processors
2
+ class Base
3
+ def call(release, options = {})
4
+ raise ArgumentError, "Implement in subclass"
5
+ end
6
+ end
7
+ end
8
+
9
+ require File.dirname(__FILE__) + "/finalizers/zip"
10
+ require File.dirname(__FILE__) + "/finalizers/dir"
11
+
@@ -0,0 +1,101 @@
1
+ require 'pathname'
2
+
3
+ module HtmlMockup::Release::Scm
4
+ class Git < Base
5
+
6
+ # @option config [String] :ref Ref to use for current tag
7
+ # @option config [String, Pathname] :path Path to working dir
8
+ def initialize(config={})
9
+ super(config)
10
+ @config[:ref] ||= "HEAD"
11
+ end
12
+
13
+ # Version is either:
14
+ # - the tagged version number (first "v" will be stripped) or
15
+ # - the return value of "git describe --tags HEAD"
16
+ # - the short SHA1 if there hasn't been a previous tag
17
+ def version
18
+ get_scm_data if @_version.nil?
19
+ @_version
20
+ end
21
+
22
+ # Date will be Time.now if it can't be determined from GIT repository
23
+ def date
24
+ get_scm_data if @_date.nil?
25
+ @_date
26
+ end
27
+
28
+ def previous
29
+ self.class.new(@config.dup.update(:ref => get_previous_tag_name))
30
+ end
31
+
32
+ protected
33
+
34
+ def get_previous_tag_name
35
+ # Get list of SHA1 that have a ref
36
+ begin
37
+ sha1s = `git --git-dir=#{git_dir} log --pretty='%H' --simplify-by-decoration`.split("\n")
38
+ tags = []
39
+ while tags.size < 2 && sha1s.any?
40
+ sha1 = sha1s.shift
41
+ tag = `git --git-dir=#{git_dir} describe --tags --exact-match #{sha1} 2>/dev/null`.strip
42
+ tags << tag if !tag.empty?
43
+ end
44
+ tags.last
45
+ rescue
46
+ raise "Could not get previous tag"
47
+ end
48
+ end
49
+
50
+ def git_dir
51
+ @git_dir ||= find_git_dir(@config[:path])
52
+ end
53
+
54
+ # Some hackery to determine if we're on a tagged version or not
55
+ def get_scm_data(ref = @config[:ref])
56
+ @_version = ""
57
+ @_date = Time.now
58
+ begin
59
+ if File.exist?(git_dir)
60
+ @_version = `git --git-dir=#{git_dir} describe --tags #{ref} 2>&1`
61
+
62
+ if $?.to_i > 0
63
+ # HEAD is not a tagged verison, get the short SHA1 instead
64
+ @_version = `git --git-dir=#{git_dir} show #{ref} --format=format:"%h" --quiet 2>&1`
65
+ else
66
+ # HEAD is a tagged version, if version is prefixed with "v" it will be stripped off
67
+ @_version.gsub!(/^v/,"")
68
+ end
69
+ @_version.strip!
70
+
71
+ # Get the date in epoch time
72
+ date = `git --git-dir=#{git_dir} show #{ref} --format=format:"%ct" --quiet 2>&1`
73
+ if date =~ /\d+/
74
+ @_date = Time.at(date.to_i)
75
+ else
76
+ @_date = Time.now
77
+ end
78
+
79
+ end
80
+ rescue RuntimeError => e
81
+ end
82
+
83
+ end
84
+
85
+ # Find the git dir
86
+ def find_git_dir(path)
87
+ path = Pathname.new(path).realpath
88
+ while path.parent != path && !(path + ".git").directory?
89
+ path = path.parent
90
+ end
91
+
92
+ path = path + ".git"
93
+
94
+ raise "Could not find suitable .git dir in #{path}" if !path.directory?
95
+
96
+ path
97
+ end
98
+
99
+ end
100
+ end
101
+
@@ -0,0 +1,32 @@
1
+ module HtmlMockup::Release::Scm
2
+ class Base
3
+
4
+ attr_reader :config
5
+
6
+ def initialize(config={})
7
+ @config = config
8
+ end
9
+
10
+ # Returns the release version string from the SCM
11
+ #
12
+ # @return String The current version string
13
+ def version
14
+ raise "Implement in subclass"
15
+ end
16
+
17
+ # Returns the release version date from the SCM
18
+ def date
19
+ raise "Implement in subclass"
20
+ end
21
+
22
+ # Returns a Release::Scm object with the previous version's data
23
+ #
24
+ # @return HtmlMockup::Release::Scm The previous version
25
+ def previous
26
+ raise "Implement in subclass"
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ require File.dirname(__FILE__) + "/scm/git"
@@ -0,0 +1,312 @@
1
+ require File.dirname(__FILE__) + "/cli"
2
+
3
+ module HtmlMockup
4
+ class Release
5
+
6
+ attr_reader :config, :project
7
+
8
+ attr_reader :finalizers, :injections, :stack, :cleanups
9
+
10
+ def self.default_stack
11
+ []
12
+ end
13
+
14
+ def self.default_finalizers
15
+ [[:dir, {}]]
16
+ end
17
+
18
+ # @option config [Symbol] :scm The SCM to use (default = :git)
19
+ # @option config [String, Pathname] :target_path The path/directory to put the release into
20
+ # @option config [String, Pathname]:build_path Temporary path used to build the release
21
+ # @option config [Boolean] :cleanup_build Wether or not to remove the build_path after we're done (default = true)
22
+ def initialize(project, config = {})
23
+ defaults = {
24
+ :scm => :git,
25
+ :source_path => Pathname.new(Dir.pwd) + "html",
26
+ :target_path => Pathname.new(Dir.pwd) + "releases",
27
+ :build_path => Pathname.new(Dir.pwd) + "build",
28
+ :cleanup_build => true
29
+ }
30
+
31
+ @config = {}.update(defaults).update(config)
32
+ @project = project
33
+ @finalizers = []
34
+ @injections = []
35
+ @stack = []
36
+ @cleanups = []
37
+ end
38
+
39
+ # Accessor for target_path
40
+ # The target_path is the path where the finalizers will put the release
41
+ #
42
+ # @return Pathname the target_path
43
+ def target_path
44
+ Pathname.new(self.config[:target_path])
45
+ end
46
+
47
+ # Accessor for build_path
48
+ # The build_path is a temporary directory where the release will be built
49
+ #
50
+ # @return Pathname the build_path
51
+ def build_path
52
+ Pathname.new(self.config[:build_path])
53
+ end
54
+
55
+ # Accessor for source_path
56
+ # The source path is the root of the mockup
57
+ #
58
+ # @return Pathanem the source_path
59
+ def source_path
60
+ Pathname.new(self.config[:source_path])
61
+ end
62
+
63
+ # Get the current SCM object
64
+ def scm(force = false)
65
+ return @_scm if @_scm && !force
66
+
67
+ case self.config[:scm]
68
+ when :git
69
+ @_scm = Release::Scm::Git.new(:path => self.source_path)
70
+ else
71
+ raise "Unknown SCM #{options[:scm].inspect}"
72
+ end
73
+ end
74
+
75
+ # Inject variables into files with an optional filter
76
+ #
77
+ # @examples
78
+ # release.inject({"VERSION" => release.version, "DATE" => release.date}, :into => %w{_doc/toc.html})
79
+ # release.inject({"CHANGELOG" => {:file => "", :filter => BlueCloth}}, :into => %w{_doc/changelog.html})
80
+ def inject(variables, options)
81
+ @injections << [variables, options]
82
+ end
83
+
84
+ # Use a certain pre-processor
85
+ #
86
+ # @examples
87
+ # release.use :sprockets, sprockets_config
88
+ def use(processor, options = {})
89
+ @stack << [processor, options]
90
+ end
91
+
92
+ # Write out the whole release into a directory, zip file or anything you can imagine
93
+ # #finalize can be called multiple times, it just will run all of them.
94
+ #
95
+ # The default finalizer is :dir
96
+ #
97
+ # @param [Symbol, Proc] Finalizer to use
98
+ #
99
+ # @examples
100
+ # release.finalize :zip
101
+ def finalize(finalizer, options = {})
102
+ @finalizers << [finalizer, options]
103
+ end
104
+
105
+ # Files to clean up in the build directory just before finalization happens
106
+ #
107
+ # @param [String] Pattern to glob within build directory
108
+ #
109
+ # @examples
110
+ # release.cleanup "**/.DS_Store"
111
+ def cleanup(pattern)
112
+ @cleanups << pattern
113
+ end
114
+
115
+ # Generates a banner if a block is given, or returns the currently set banner.
116
+ # It automatically takes care of adding comment marks around the banner.
117
+ #
118
+ # The default banner looks like this:
119
+ #
120
+ # =======================
121
+ # = Version : v1.0.0 =
122
+ # = Date : 2012-06-20 =
123
+ # =======================
124
+ #
125
+ #
126
+ # @option options [:css,:js,:html,false] :comment Wether or not to comment the output and in what style. (default=js)
127
+ def banner(options = {}, &block)
128
+ options = {
129
+ :comment => :js
130
+ }.update(options)
131
+
132
+ if block_given?
133
+ @_banner = yield.to_s
134
+ elsif !@_banner
135
+ banner = []
136
+ banner << "Version : #{self.scm.version}"
137
+ banner << "Date : #{self.scm.date.strftime("%Y-%m-%d")}"
138
+
139
+ size = banner.inject(0){|mem,b| b.size > mem ? b.size : mem }
140
+ banner.map!{|b| "= #{b.ljust(size)} =" }
141
+ div = "=" * banner.first.size
142
+ banner.unshift(div)
143
+ banner << div
144
+ @_banner = banner.join("\n")
145
+ end
146
+
147
+ if options[:comment]
148
+ self.comment(@_banner, :style => options[:comment])
149
+ else
150
+ @_banner
151
+ end
152
+ end
153
+
154
+ # Actually perform the release
155
+ def run!
156
+ # Validate paths
157
+ validate_paths!
158
+
159
+ # Extract valid mockup
160
+ extract!
161
+
162
+ # Run stack
163
+ run_stack!
164
+
165
+ # Run injections
166
+ run_injections!
167
+
168
+ # Run cleanups
169
+ run_cleanups!
170
+
171
+ # Run finalizers
172
+ run_finalizers!
173
+
174
+ # Cleanup
175
+ cleanup! if self.config[:cleanup_build]
176
+
177
+ end
178
+
179
+ def log(part, msg)
180
+ puts part.class.to_s + " : " + msg.to_s
181
+ end
182
+
183
+ # @param [Array] globs an array of file path globs that will be globbed against the build_path
184
+ # @param [Array] excludes an array of regexps that will be excluded from the result
185
+ def get_files(globs, excludes = [])
186
+ files = globs.map{|g| Dir.glob(self.build_path + g) }.flatten
187
+ if excludes.any?
188
+ files.reject{|c| excludes.detect{|e| e.match(c) } }
189
+ else
190
+ files
191
+ end
192
+ end
193
+
194
+ protected
195
+
196
+ # ==============
197
+ # = The runway =
198
+ # ==============
199
+
200
+ def validate_paths!
201
+ # Make sure the build_path is empty
202
+ raise ArgumentError, "Build path \"#{self.build_path}\" is not empty" if self.build_path.exist?
203
+
204
+ # Make sure the target_path exists
205
+ raise ArgumentError, "Target path \"#{self.target_path}\" does not exist" if !self.target_path.exist?
206
+ end
207
+
208
+ def extract!
209
+ extractor = Extractor.new(self.project, self.build_path)
210
+ extractor.run!
211
+ end
212
+
213
+ def run_stack!
214
+ @stack = self.class.default_stack.dup if @stack.empty?
215
+ @stack.each do |processor, options|
216
+ get_callable(processor, HtmlMockup::Release::Processors).call(self, options)
217
+ end
218
+ end
219
+
220
+ def run_injections!
221
+ @injections.each do |injection|
222
+ Injector.new(injection[0], injection[1]).call(self)
223
+ end
224
+ end
225
+
226
+ def run_finalizers!
227
+ @finalizers = self.class.default_finalizers.dup if @finalizers.empty?
228
+ @finalizers.each do |finalizer, options|
229
+ get_callable(finalizer, HtmlMockup::Release::Finalizers).call(self, options)
230
+ end
231
+ end
232
+
233
+ def run_cleanups!
234
+ # We switch to the build path and append the globbed files for safety, so even if you manage to sneak in a
235
+ # pattern like "/**/*" it won't do you any good as it will be reappended to the path
236
+ Dir.chdir(self.build_path.to_s) do
237
+ @cleanups.each do |pattern|
238
+ Dir.glob(pattern).each do |file|
239
+ path = File.join(self.build_path.to_s, file)
240
+ log(self, "Cleaning up \"#{path}\" in build")
241
+ rm(path)
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ def cleanup!
248
+ log(self, "Cleaning up build path #{self.build_path}")
249
+ rm_rf(self.build_path)
250
+ end
251
+
252
+ # Makes callable into a object that responds to call.
253
+ #
254
+ # @param [#call, Symbol, Class] callable If callable already responds to #call will just return callable, a Symbol will be searched for in the scope parameter, a class will be instantiated (and checked if it will respond to #call)
255
+ # @param [Module] scope The scope in which to search callable in if it's a Symbol
256
+ def get_callable(callable, scope)
257
+ return callable if callable.respond_to?(:call)
258
+
259
+ if callable.kind_of?(Symbol)
260
+ # TODO camelcase callable
261
+ callable = callable.to_s.capitalize.to_sym
262
+ if scope.constants.include?(callable)
263
+ c = scope.const_get(callable)
264
+ callable = c if c.is_a?(Class)
265
+ end
266
+ end
267
+
268
+ if callable.kind_of?(Class)
269
+ callable = callable.new
270
+ end
271
+
272
+ if callable.respond_to?(:call)
273
+ callable
274
+ else
275
+ raise ArgumentError, "Could not resolve #{callable.inspect}. Callable must be an object that responds to #call or a symbol that resolve to such an object or a class with a #call instance method."
276
+ end
277
+
278
+ end
279
+
280
+ # @param [String] string The string to comment
281
+ #
282
+ # @option options [:html, :css, :js] :style The comment style to use (default=:js, which is the same as :css)
283
+ # @option options [Boolean] :per_line Comment per line or make one block? (default=true)
284
+ def comment(string, options = {})
285
+ options = {
286
+ :style => :css,
287
+ :per_line => true
288
+ }.update(options)
289
+
290
+ commenters = {
291
+ :html => Proc.new{|s| "<!-- #{s} -->" },
292
+ :css => Proc.new{|s| "/* #{s} */" },
293
+ :js => Proc.new{|s| "/* #{s} */" }
294
+ }
295
+
296
+ commenter = commenters[options[:style]] || commenters[:js]
297
+
298
+ if options[:per_line]
299
+ string = string.split(/\r?\n/)
300
+ string.map{|s| commenter.call(s) }.join("\n")
301
+ else
302
+ commenter.call(s)
303
+ end
304
+ end
305
+ end
306
+ end
307
+
308
+ require File.dirname(__FILE__) + "/extractor"
309
+ require File.dirname(__FILE__) + "/release/scm"
310
+ require File.dirname(__FILE__) + "/release/injector"
311
+ require File.dirname(__FILE__) + "/release/finalizers"
312
+ require File.dirname(__FILE__) + "/release/processors"
@@ -6,17 +6,28 @@ require File.dirname(__FILE__) + "/rack/html_validator"
6
6
 
7
7
  module HtmlMockup
8
8
  class Server
9
- attr_accessor :options,:server_options, :root, :partial_path
10
9
 
11
- def initialize(root,partial_path,options={},server_options={})
10
+ attr_reader :options
11
+
12
+ attr_accessor :html_path, :partial_path
13
+
14
+ attr_accessor :port, :handler
15
+
16
+ def initialize(html_path, partial_path, options={})
12
17
  @stack = ::Rack::Builder.new
13
18
 
14
19
  @middleware = []
15
- @root = root
20
+ @html_path = html_path
16
21
  @partial_path = partial_path
17
- @options,@server_options = options,server_options
22
+ @options = {
23
+ :handler => nil, # Autodetect
24
+ :port => 9000
25
+ }.update(options)
26
+
27
+ @port = @options[:port]
28
+ @handler = @options[:handler]
18
29
  end
19
-
30
+
20
31
  # Use the specified Rack middleware
21
32
  def use(middleware, *args, &block)
22
33
  @middleware << [middleware, args, block]
@@ -25,7 +36,7 @@ module HtmlMockup
25
36
  def handler
26
37
  if self.options[:handler]
27
38
  begin
28
- @handler = ::Rack::Handler.get(self.options[:handler])
39
+ @handler = ::Rack::Handler.get(self.handler)
29
40
  rescue LoadError
30
41
  rescue NameError
31
42
  end
@@ -35,9 +46,9 @@ module HtmlMockup
35
46
  end
36
47
  @handler ||= detect_rack_handler
37
48
  end
38
-
39
- def run
40
- self.handler.run self.application, @server_options do |server|
49
+
50
+ def run!
51
+ self.handler.run self.application, self.server_options do |server|
41
52
  trap(:INT) do
42
53
  ## Use thins' hard #stop! if available, otherwise just #stop
43
54
  server.respond_to?(:stop!) ? server.stop! : server.stop
@@ -45,6 +56,7 @@ module HtmlMockup
45
56
  end
46
57
  end
47
58
  end
59
+ alias :run :run!
48
60
 
49
61
  def application
50
62
  return @app if @app
@@ -56,7 +68,7 @@ module HtmlMockup
56
68
  @middleware.each { |c,a,b| @stack.use(c, *a, &b) }
57
69
 
58
70
  @stack.use Rack::HtmlValidator if self.options["validate"]
59
- @stack.run Rack::HtmlMockup.new(self.root, self.partial_path)
71
+ @stack.run Rack::HtmlMockup.new(self.html_path, self.partial_path)
60
72
 
61
73
  @app = @stack.to_app
62
74
  end
@@ -64,6 +76,14 @@ module HtmlMockup
64
76
 
65
77
  protected
66
78
 
79
+ # Generate server options for handler
80
+ def server_options
81
+ {
82
+ :Port => self.port
83
+ }
84
+ end
85
+
86
+
67
87
  # Sinatra's detect_rack_handler
68
88
  def detect_rack_handler
69
89
  servers = %w[mongrel thin webrick]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html_mockup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,27 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-27 00:00:00.000000000Z
12
+ date: 2012-10-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
16
- requirement: &70245084497540 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.12.0
21
+ version: 0.16.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70245084497540
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.16.0
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rack
27
- requirement: &70245084496900 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,7 +37,12 @@ dependencies:
32
37
  version: 1.0.0
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70245084496900
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
36
46
  description:
37
47
  email: flurin@digitpaint.nl
38
48
  executables:
@@ -48,8 +58,23 @@ files:
48
58
  - examples/partials/test.part.rhtml
49
59
  - examples/script/server
50
60
  - lib/html_mockup/cli.rb
61
+ - lib/html_mockup/extractor.rb
62
+ - lib/html_mockup/mockupfile.rb
63
+ - lib/html_mockup/project.rb
51
64
  - lib/html_mockup/rack/html_mockup.rb
52
65
  - lib/html_mockup/rack/html_validator.rb
66
+ - lib/html_mockup/rack/sleep.rb
67
+ - lib/html_mockup/release.rb
68
+ - lib/html_mockup/release/finalizers.rb
69
+ - lib/html_mockup/release/finalizers/dir.rb
70
+ - lib/html_mockup/release/finalizers/zip.rb
71
+ - lib/html_mockup/release/injector.rb
72
+ - lib/html_mockup/release/processors.rb
73
+ - lib/html_mockup/release/processors/requirejs.rb
74
+ - lib/html_mockup/release/processors/sass.rb
75
+ - lib/html_mockup/release/processors/yuicompressor.rb
76
+ - lib/html_mockup/release/scm.rb
77
+ - lib/html_mockup/release/scm/git.rb
53
78
  - lib/html_mockup/server.rb
54
79
  - lib/html_mockup/template.rb
55
80
  - lib/html_mockup/w3c_validator.rb
@@ -66,6 +91,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
91
  - - ! '>='
67
92
  - !ruby/object:Gem::Version
68
93
  version: '0'
94
+ segments:
95
+ - 0
96
+ hash: -1984595046850543499
69
97
  required_rubygems_version: !ruby/object:Gem::Requirement
70
98
  none: false
71
99
  requirements:
@@ -74,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
102
  version: '0'
75
103
  requirements: []
76
104
  rubyforge_project:
77
- rubygems_version: 1.8.6
105
+ rubygems_version: 1.8.24
78
106
  signing_key:
79
107
  specification_version: 3
80
108
  summary: HTML Mockup is a set of tools to create self-containing HTML mockups.