awhyte-rtex 2.1.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.
- data/CHANGELOG +48 -0
- data/Manifest +25 -0
- data/README.rdoc +46 -0
- data/README_RAILS.rdoc +58 -0
- data/Rakefile +22 -0
- data/bin/rtex +82 -0
- data/init.rb +1 -0
- data/lib/rtex.rb +37 -0
- data/lib/rtex/document.rb +245 -0
- data/lib/rtex/escaping.rb +28 -0
- data/lib/rtex/framework/merb.rb +13 -0
- data/lib/rtex/framework/rails.rb +209 -0
- data/lib/rtex/tempdir.rb +107 -0
- data/lib/rtex/version.rb +77 -0
- data/rails/init.rb +2 -0
- data/rtex.gemspec +41 -0
- data/test/document_test.rb +86 -0
- data/test/filter_test.rb +24 -0
- data/test/fixtures/first.tex +50 -0
- data/test/fixtures/first.tex.erb +48 -0
- data/test/fixtures/fragment.tex.erb +1 -0
- data/test/fixtures/text.textile +4 -0
- data/test/tempdir_test.rb +45 -0
- data/test/test_helper.rb +26 -0
- data/vendor/instiki/LICENSE +3 -0
- data/vendor/instiki/redcloth_for_tex.rb +736 -0
- metadata +116 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module RTeX
|
2
|
+
|
3
|
+
module Escaping
|
4
|
+
|
5
|
+
# Escape text using +replacements+
|
6
|
+
def escape(text)
|
7
|
+
replacements.inject(text.to_s) do |corpus, (pattern, replacement)|
|
8
|
+
corpus.gsub(pattern, replacement)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# List of replacements
|
13
|
+
def replacements
|
14
|
+
@replacements ||= [
|
15
|
+
[/([{}])/, '\\\\\1'],
|
16
|
+
[/\\/, '\textbackslash{}'],
|
17
|
+
[/\^/, '\textasciicircum{}'],
|
18
|
+
[/~/, '\textasciitilde{}'],
|
19
|
+
[/\|/, '\textbar{}'],
|
20
|
+
[/\</, '\textless{}'],
|
21
|
+
[/\>/, '\textgreater{}'],
|
22
|
+
[/([_$&%#])/, '\\\\\1']
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module RTeX
|
4
|
+
module Framework #:nodoc:
|
5
|
+
module Rails #:nodoc:
|
6
|
+
|
7
|
+
def self.setup
|
8
|
+
RTeX::Document.options[:tempdir] = File.expand_path(File.join(RAILS_ROOT, 'tmp'))
|
9
|
+
if ActionView::Base.respond_to?(:register_template_handler)
|
10
|
+
ActionView::Base.register_template_handler(:rtex, TemplateHandler)
|
11
|
+
else
|
12
|
+
ActionView::Template.register_template_handler(:rtex, TemplateHandler)
|
13
|
+
end
|
14
|
+
ActionController::Base.send(:include, ControllerMethods)
|
15
|
+
ActionView::Base.send(:include, HelperMethods)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
class TemplateHandler < ::ActionView::TemplateHandlers::ERB
|
20
|
+
# Due to significant changes in ActionView over the lifespan of Rails,
|
21
|
+
# tagging compiled templates to set a thread local variable flag seems
|
22
|
+
# to be the least brittle approach.
|
23
|
+
def compile(template)
|
24
|
+
# Insert assignment, but not before the #coding: line, if present
|
25
|
+
super.sub(/^(?!#)/m, "Thread.current[:_rendering_rtex] = true;\n")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
module ControllerMethods
|
31
|
+
|
32
|
+
def self.included(base) #:nodoc:
|
33
|
+
base.alias_method_chain :render, :rtex
|
34
|
+
end
|
35
|
+
|
36
|
+
# Extends the base +ActionController#render+ method by checking whether
|
37
|
+
# the template is RTeX-capable and creating an RTeX::Document in that
|
38
|
+
# case.
|
39
|
+
#
|
40
|
+
# If you have view templates saved, for example, as "show.html.erb" and
|
41
|
+
# "show.pdf.rtex" in an ItemsController, one could write the controller
|
42
|
+
# action like this:
|
43
|
+
#
|
44
|
+
# def show
|
45
|
+
# # set up instance variables
|
46
|
+
# # ...
|
47
|
+
# respond_to do |format|
|
48
|
+
# format.html
|
49
|
+
# format.pdf { render :filename => "Item Information.pdf" }
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# As well as the options available to a new RTeX::Document, the
|
54
|
+
# following may be supplied here:
|
55
|
+
#
|
56
|
+
# [+:disposition+] 'inline' (default) or 'attachment'
|
57
|
+
# [+:filename+] +nil+ (default) results in the final part of the
|
58
|
+
# request URL being used.
|
59
|
+
#
|
60
|
+
# To see the underlying LaTeX code that generated the PDF, create
|
61
|
+
# another view called "show.tex.rtex" which includes the single line
|
62
|
+
#
|
63
|
+
# <%= include_template_from "items/show.pdf.rtex" -%>
|
64
|
+
#
|
65
|
+
# and include the following in the controller action
|
66
|
+
#
|
67
|
+
# format.tex { render :filename => "item_information_source.tex" }
|
68
|
+
#
|
69
|
+
# This may be helpful during debugging or for saving pre-compiled parts
|
70
|
+
# of LaTeX documents for later processing into PDFs.
|
71
|
+
#
|
72
|
+
def render_with_rtex(options=nil, extra_options={}, &block)
|
73
|
+
if conditions_for_rtex(options)
|
74
|
+
render_rtex(default_template, options, extra_options, &block)
|
75
|
+
else
|
76
|
+
render_without_rtex(options, extra_options, &block)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def render_rtex(template, options={}, extra_options={}, &block)
|
83
|
+
RTeX::Tempdir.open do |dir|
|
84
|
+
# Make LaTeX temporary directory accessible to view
|
85
|
+
@rtex_dir = dir.to_s
|
86
|
+
|
87
|
+
# Render view into LaTeX document string
|
88
|
+
latex = render_without_rtex(options, extra_options, &block)
|
89
|
+
|
90
|
+
# HTTP response options
|
91
|
+
send_options = {
|
92
|
+
:disposition => (options.delete(:disposition) || 'inline'),
|
93
|
+
:url_based_filename => true,
|
94
|
+
:filename => (options.delete(:filename) || nil),
|
95
|
+
:type => template.mime_type.to_s }
|
96
|
+
|
97
|
+
# Document options
|
98
|
+
rtex_options = options.merge(:tempdir => dir).reverse_merge(
|
99
|
+
:processed => true,
|
100
|
+
:tex_inputs => File.join(RAILS_ROOT,'lib','latex') )
|
101
|
+
|
102
|
+
# Content type requested
|
103
|
+
content_type = template.content_type.to_sym
|
104
|
+
|
105
|
+
# Send appropriate data to client
|
106
|
+
case content_type
|
107
|
+
when :tex
|
108
|
+
send_data latex, send_options.merge(:length => latex.length)
|
109
|
+
when :pdf
|
110
|
+
pdf = RTeX::Document.new(latex, rtex_options).to_pdf
|
111
|
+
send_data pdf, send_options.merge(:length => pdf.length)
|
112
|
+
else
|
113
|
+
raise "RTeX: unknown content type requested: #{content_type}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def conditions_for_rtex(options)
|
119
|
+
begin
|
120
|
+
# Do not override the +:text+ rendering, because send_data uses this
|
121
|
+
!options[:text] &&
|
122
|
+
# Check whether the default template is one managed by the
|
123
|
+
# RTeX::Framework::Rails::TemplateHandler
|
124
|
+
:rtex == default_template.extension.to_sym
|
125
|
+
rescue
|
126
|
+
false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
module HelperMethods
|
133
|
+
|
134
|
+
# Make the supplied text safe for the LaTeX processor, in the same way
|
135
|
+
# that h() works for HTML documents.
|
136
|
+
#
|
137
|
+
def latex_escape(*args)
|
138
|
+
# Since Rails' I18n implementation aliases l() to localize(), LaTeX
|
139
|
+
# escaping should only be done if RTeX is doing the rendering.
|
140
|
+
# Otherwise, control should be be passed to localize().
|
141
|
+
if Thread.current[:_rendering_rtex]
|
142
|
+
RTeX::Document.escape(*args)
|
143
|
+
else
|
144
|
+
localize(*args)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
alias :l :latex_escape
|
148
|
+
|
149
|
+
# Obtain the temporary directory RTeX is currently using
|
150
|
+
attr_reader :rtex_dir
|
151
|
+
|
152
|
+
# Copy the contents of another template into the current one, like
|
153
|
+
# rendering a partial but without have to add an underscore to the
|
154
|
+
# filename.
|
155
|
+
#
|
156
|
+
def include_template_from(from)
|
157
|
+
other_file = @template.view_paths.find_template(from).relative_path
|
158
|
+
render(:inline => File.read(other_file))
|
159
|
+
end
|
160
|
+
|
161
|
+
# Include an image file into the current LaTeX document.
|
162
|
+
#
|
163
|
+
# For example,
|
164
|
+
#
|
165
|
+
# \includegraphics{<%= graphics_file("some file.png") -%>}
|
166
|
+
#
|
167
|
+
# will copy "public/images/some file.png" to a the current RTeX
|
168
|
+
# temporary directory (with a LaTeX-safe filename) and reference it in
|
169
|
+
# the document like this:
|
170
|
+
#
|
171
|
+
# \includegraphics{/tmp/rtex/rtex-random-number/image-file.png}
|
172
|
+
#
|
173
|
+
def graphics_file(file, reference_original_file=false)
|
174
|
+
source = Pathname.new file
|
175
|
+
source = Pathname.new(ActionView::Helpers::ASSETS_DIR) + "images" + source unless source.absolute?
|
176
|
+
|
177
|
+
if reference_original_file
|
178
|
+
source.to_s
|
179
|
+
else
|
180
|
+
destination = Pathname.new(@rtex_dir) + latex_safe_filename(source.basename)
|
181
|
+
FileUtils.copy source, destination
|
182
|
+
destination.to_s
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Save the supplied image data to the RTeX temporary directory and
|
187
|
+
# return the filename for inclusion in the current document.
|
188
|
+
#
|
189
|
+
# \includegraphics{<%= graphics_data(object.to_png) -%>}
|
190
|
+
#
|
191
|
+
def graphics_data(data)
|
192
|
+
destination = Pathname.new(@rtex_dir) + latex_safe_filename(filename)
|
193
|
+
File.open(destination, "w") { |f| f.write data }
|
194
|
+
dst.to_s
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def latex_safe_filename(input)
|
200
|
+
# Replace non-ASCII characters
|
201
|
+
input = ActiveSupport::Inflector.transliterate(input.to_s).to_s
|
202
|
+
# Replace non-LaTeX-friendly characters with a dash
|
203
|
+
input.gsub(/[^a-z0-9\-_\+\.]+/i, '-')
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
data/lib/rtex/tempdir.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
module RTeX
|
5
|
+
|
6
|
+
# Utility class to manage the lifetime of a temporary directory in which
|
7
|
+
# LaTeX can do its processing
|
8
|
+
#
|
9
|
+
class Tempdir
|
10
|
+
|
11
|
+
# Create a new temporary directory, yield for processing, then remove it.
|
12
|
+
#
|
13
|
+
# RTeX::Tempdir.open do |tempdir|
|
14
|
+
# # Use newly created temporary directory
|
15
|
+
# # ...
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# When the block completes, the directory is removed.
|
19
|
+
#
|
20
|
+
# Arguments can be either an specific directory name,
|
21
|
+
#
|
22
|
+
# RTeX::Tempdir.open("/tmp/somewhere") do ...
|
23
|
+
#
|
24
|
+
# or options to assist the generation of a randomly named unique directory,
|
25
|
+
#
|
26
|
+
# RTeX::Tempdir.open(:parent_path => PARENT, :basename => BASENAME) do ...
|
27
|
+
#
|
28
|
+
# which would result in a new directory
|
29
|
+
# "[PARENT]/rtex/[BASENAME]-[random hash]/" being created.
|
30
|
+
#
|
31
|
+
# The default +:parent_path+ is +Dir.tmpdir", and +:basename+ 'rtex'.
|
32
|
+
#
|
33
|
+
def self.open(*args)
|
34
|
+
tempdir = new(*args)
|
35
|
+
tempdir.create!
|
36
|
+
|
37
|
+
# Yield the path and wait for the block to finish
|
38
|
+
result = yield tempdir
|
39
|
+
|
40
|
+
# We don't remove the temporary directory when exceptions occur,
|
41
|
+
# so that the source of the exception can be dubbed (logfile kept)
|
42
|
+
tempdir.remove!
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
# Manually control tempdir creation and removal. Accepts the same arguments
|
47
|
+
# as +RTeX::Tempdir.open+, but the user must call +#create!+ and +#remove!+
|
48
|
+
# themselves:
|
49
|
+
#
|
50
|
+
# tempdir = RTeX::Tempdir.new
|
51
|
+
# tempdir.create!
|
52
|
+
# # Use the temporary directory
|
53
|
+
# # ...
|
54
|
+
# tempdir.remove!
|
55
|
+
#
|
56
|
+
def initialize(*args)
|
57
|
+
options = args.last.is_a?(Hash) ? options.pop : {}
|
58
|
+
specific_path = args.first
|
59
|
+
|
60
|
+
if specific_path
|
61
|
+
@path = specific_path
|
62
|
+
else
|
63
|
+
@parent_path = options[:parent_path] || Dir.tmpdir
|
64
|
+
@basename = options[:basename] || 'rtex'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def create!
|
69
|
+
FileUtils.mkdir_p path
|
70
|
+
@removed = false
|
71
|
+
path
|
72
|
+
end
|
73
|
+
|
74
|
+
# Is the temporary directory present on the filesystem
|
75
|
+
def exists?
|
76
|
+
File.exists?(path)
|
77
|
+
end
|
78
|
+
|
79
|
+
# The filesystem location of the temporary directory
|
80
|
+
def path
|
81
|
+
@path ||= File.expand_path(File.join(@parent_path, 'rtex', "#{@basename}-#{self.class.uuid}"))
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return the +#path+ for use in string expansion
|
85
|
+
alias :to_s :path
|
86
|
+
|
87
|
+
# Forcibly remove this temporary directory
|
88
|
+
def remove!
|
89
|
+
return false if @removed
|
90
|
+
FileUtils.rm_rf path
|
91
|
+
@removed = true
|
92
|
+
end
|
93
|
+
|
94
|
+
# Try using uuidgen, but if that doesn't work drop down to
|
95
|
+
# a poor-man's UUID; timestamp, thread & object hashes
|
96
|
+
# Note: I don't want to add any dependencies (so no UUID library)
|
97
|
+
#
|
98
|
+
def self.uuid
|
99
|
+
if (result = `uuidgen`.strip rescue nil).empty?
|
100
|
+
"#{Time.now.to_i}-#{Thread.current.hash}-#{hash}"
|
101
|
+
else
|
102
|
+
result
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
data/lib/rtex/version.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# (The MIT License)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2008 Jamis Buck <jamis@37signals.com>,
|
4
|
+
# with modifications by Bruce Williams <bruce@codefluency.com>
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
# a copy of this software and associated documentation files (the
|
8
|
+
# 'Software'), to deal in the Software without restriction, including
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
# the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
20
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
21
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
22
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
23
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
module RTeX
|
25
|
+
|
26
|
+
|
27
|
+
# A class for describing the current version of a library. The version
|
28
|
+
# consists of three parts: the +major+ number, the +minor+ number, and the
|
29
|
+
# +tiny+ (or +patch+) number.
|
30
|
+
class Version
|
31
|
+
|
32
|
+
# A convenience method for instantiating a new Version instance with the
|
33
|
+
# given +major+, +minor+, and +tiny+ components.
|
34
|
+
def self.[](major, minor, tiny)
|
35
|
+
new(major, minor, tiny)
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :major, :minor, :tiny
|
39
|
+
|
40
|
+
# Create a new Version object with the given components.
|
41
|
+
def initialize(major, minor, tiny)
|
42
|
+
@major, @minor, @tiny = major, minor, tiny
|
43
|
+
end
|
44
|
+
|
45
|
+
# Compare this version to the given +version+ object.
|
46
|
+
def <=>(version)
|
47
|
+
to_i <=> version.to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
# Converts this version object to a string, where each of the three
|
51
|
+
# version components are joined by the '.' character. E.g., 2.0.0.
|
52
|
+
def to_s
|
53
|
+
@to_s ||= [@major, @minor, @tiny].join(".")
|
54
|
+
end
|
55
|
+
|
56
|
+
# Converts this version to a canonical integer that may be compared
|
57
|
+
# against other version objects.
|
58
|
+
def to_i
|
59
|
+
@to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_a
|
63
|
+
[@major, @minor, @tiny]
|
64
|
+
end
|
65
|
+
|
66
|
+
MAJOR = 2
|
67
|
+
MINOR = 1
|
68
|
+
TINY = 1
|
69
|
+
|
70
|
+
# The current version as a Version instance
|
71
|
+
CURRENT = new(MAJOR, MINOR, TINY)
|
72
|
+
# The current version as a String
|
73
|
+
STRING = CURRENT.to_s
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/rails/init.rb
ADDED
data/rtex.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{rtex}
|
5
|
+
s.version = "2.1.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Bruce Williams, Wiebe Cazemier"]
|
9
|
+
s.date = %q{2009-09-24}
|
10
|
+
s.default_executable = %q{rtex}
|
11
|
+
s.description = %q{LaTeX preprocessor for PDF generation; Rails plugin}
|
12
|
+
s.email = %q{bruce@codefluency.com}
|
13
|
+
s.executables = ["rtex"]
|
14
|
+
s.extra_rdoc_files = ["bin/rtex", "CHANGELOG", "lib/rtex/document.rb", "lib/rtex/escaping.rb", "lib/rtex/framework/merb.rb", "lib/rtex/framework/rails.rb", "lib/rtex/tempdir.rb", "lib/rtex/version.rb", "lib/rtex.rb", "README.rdoc", "README_RAILS.rdoc"]
|
15
|
+
s.files = ["bin/rtex", "CHANGELOG", "init.rb", "lib/rtex/document.rb", "lib/rtex/escaping.rb", "lib/rtex/framework/merb.rb", "lib/rtex/framework/rails.rb", "lib/rtex/tempdir.rb", "lib/rtex/version.rb", "lib/rtex.rb", "Manifest", "rails/init.rb", "Rakefile", "README.rdoc", "README_RAILS.rdoc", "test/document_test.rb", "test/filter_test.rb", "test/fixtures/first.tex", "test/fixtures/first.tex.erb", "test/fixtures/fragment.tex.erb", "test/fixtures/text.textile", "test/tempdir_test.rb", "test/test_helper.rb", "vendor/instiki/LICENSE", "vendor/instiki/redcloth_for_tex.rb", "rtex.gemspec"]
|
16
|
+
s.has_rdoc = true
|
17
|
+
s.homepage = %q{http://rtex.rubyforge.org}
|
18
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rtex", "--main", "README.rdoc"]
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.rubyforge_project = %q{rtex}
|
21
|
+
s.rubygems_version = %q{1.3.1}
|
22
|
+
s.summary = %q{LaTeX preprocessor for PDF generation; Rails plugin}
|
23
|
+
s.test_files = ["test/document_test.rb", "test/filter_test.rb", "test/tempdir_test.rb", "test/test_helper.rb"]
|
24
|
+
|
25
|
+
if s.respond_to? :specification_version then
|
26
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
27
|
+
s.specification_version = 2
|
28
|
+
|
29
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
30
|
+
s.add_development_dependency(%q<Shoulda>, [">= 0"])
|
31
|
+
s.add_development_dependency(%q<echoe>, [">= 0"])
|
32
|
+
else
|
33
|
+
s.add_dependency(%q<Shoulda>, [">= 0"])
|
34
|
+
s.add_dependency(%q<echoe>, [">= 0"])
|
35
|
+
end
|
36
|
+
else
|
37
|
+
s.add_dependency(%q<Shoulda>, [">= 0"])
|
38
|
+
s.add_dependency(%q<echoe>, [">= 0"])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|