subtrigger 0.2.7 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.md +158 -0
- data/Rakefile +3 -9
- data/VERSION +1 -1
- data/lib/subtrigger.rb +83 -118
- data/lib/subtrigger/dsl.rb +42 -0
- data/lib/subtrigger/path.rb +82 -0
- data/lib/subtrigger/revision.rb +95 -0
- data/lib/subtrigger/rule.rb +165 -0
- data/lib/subtrigger/template.rb +96 -0
- data/subtrigger.gemspec +24 -24
- data/test/test_helper.rb +3 -0
- data/test/test_path.rb +50 -0
- data/test/test_revision.rb +43 -0
- data/test/test_rule.rb +66 -0
- data/test/test_template.rb +42 -0
- metadata +48 -26
- data/LICENSE +0 -20
- data/README.rdoc +0 -56
- data/bin/subtrigger +0 -9
- data/lib/subtrigger/email.rb +0 -56
- data/lib/subtrigger/repository.rb +0 -140
- data/lib/subtrigger/trigger.rb +0 -60
- data/test/helper.rb +0 -11
- data/test/test_email.rb +0 -44
- data/test/test_repository.rb +0 -75
- data/test/test_subtrigger.rb +0 -44
- data/test/test_trigger.rb +0 -37
@@ -0,0 +1,82 @@
|
|
1
|
+
module Subtrigger
|
2
|
+
# Our own little implementation of the path, allowing us to look up
|
3
|
+
# the location of executable files. This is because Subversion hooks
|
4
|
+
# run in a clean environment, without any environment variables such as
|
5
|
+
# <tt>$PATH</tt>. We therefore need to run (for example)
|
6
|
+
# <tt>/usr/bin/svn update</tt> rather than <tt>svn update</tt>.
|
7
|
+
#
|
8
|
+
# There is a list of default locations that will be searched, but you
|
9
|
+
# may add your own if you want to. This is useful if you've got a custom
|
10
|
+
# installation on your machine you want to use.
|
11
|
+
#
|
12
|
+
# Note: testing whether an executable exists in a given path is done using
|
13
|
+
# the unix program <tt>test</tt>, which will most likely not work on
|
14
|
+
# windows machines (untested).
|
15
|
+
#
|
16
|
+
# @example Getting the path to an executable
|
17
|
+
# Path.new.to('svn') #=> '/usr/bin'
|
18
|
+
#
|
19
|
+
# @example Adding a preferred location
|
20
|
+
# path = Path.new
|
21
|
+
# path << '/opt/local'
|
22
|
+
# path.to('svn') => '/opt/local'
|
23
|
+
#
|
24
|
+
# @author Arjan van der Gaag
|
25
|
+
# @since 0.3.0
|
26
|
+
class Path
|
27
|
+
|
28
|
+
# The default list of paths to look in, covering most of the use cases.
|
29
|
+
DEFAULT_PATHS = %w{/opt/subversion/bin /usr/sbin /usr/bin}
|
30
|
+
|
31
|
+
# Custom exception raised when a program is not found in any of the
|
32
|
+
# locations known.
|
33
|
+
NotFound = Class.new(Exception)
|
34
|
+
|
35
|
+
# A list of absolute paths on te filesystems to where the svn executables
|
36
|
+
# might be located. These are scanned in order to find the executables
|
37
|
+
# to use.
|
38
|
+
attr_reader :locations
|
39
|
+
|
40
|
+
# Start a new list of paths, starting with the <tt>DEFAULT_PATHS</tt>
|
41
|
+
def initialize
|
42
|
+
@locations = DEFAULT_PATHS.dup # use a copy to prevent global state
|
43
|
+
@exists = Hash.new do |hash, p|
|
44
|
+
hash[p] = system('test -x ' + p)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add a new path to the stack before the existing ones.
|
49
|
+
#
|
50
|
+
# @param [String] new_path is a new possible location of executables
|
51
|
+
# @return [Array<String>] the total list of paths
|
52
|
+
def <<(new_path)
|
53
|
+
@locations.unshift(new_path)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Scan all the known paths to find the given program.
|
57
|
+
#
|
58
|
+
# Note: this probably only works on unix-like systems.
|
59
|
+
#
|
60
|
+
# @todo implement memoization per argument
|
61
|
+
# @param [String] program is the name of the executable to find, like
|
62
|
+
# <tt>svn</tt>
|
63
|
+
# @return [String] the correct path to this program or nil
|
64
|
+
# @raise NotFoundException when the program is not found in any of the
|
65
|
+
# known locations
|
66
|
+
def to(program)
|
67
|
+
location = locations.find { |path| exists? File.join(path, program) }
|
68
|
+
raise NotFound.new(program) unless location
|
69
|
+
location
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Make the actual test if a given path points to an executable file.
|
75
|
+
#
|
76
|
+
# @param [String] path is the absolute path to test
|
77
|
+
# @return [Boolean] whether the path is an executable file
|
78
|
+
def exists?(path)
|
79
|
+
@exists[path]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Subtrigger
|
2
|
+
# A simple wrapper around the output of Subversion's <tt>svnlook</tt>
|
3
|
+
# command.
|
4
|
+
#
|
5
|
+
# This class will let you make simple queries against the properties of a
|
6
|
+
# Subversion revision. It parses its output into keys and values so you can
|
7
|
+
# perform operations on them.
|
8
|
+
#
|
9
|
+
# == Attributes
|
10
|
+
#
|
11
|
+
# It knows about the following attributes:
|
12
|
+
#
|
13
|
+
# * Revision number
|
14
|
+
# * Author
|
15
|
+
# * Timestamp
|
16
|
+
# * Log message
|
17
|
+
# * changed directories
|
18
|
+
#
|
19
|
+
# This works by passing in the number of the revision to use, the raw
|
20
|
+
# output of <tt>svnlook info</tt> and the raw output of
|
21
|
+
# <tt>svnlook dirs-changed</tt>.
|
22
|
+
#
|
23
|
+
# == Special attributes
|
24
|
+
#
|
25
|
+
# Revision knows about changed projects. This is extracted from the list
|
26
|
+
# of changed directories. A project is a directory that is directly above
|
27
|
+
# a directory named <tt>trunk</tt>, <tt>branches</tt> or <tt>tags</tt>. So
|
28
|
+
# when a directory <tt>/internal/accounting/trunk</tt> is changed, the
|
29
|
+
# project <tt>/internal/accounting</tt> is reported.
|
30
|
+
#
|
31
|
+
# @example Example of raw input for <tt>info</tt>
|
32
|
+
# john
|
33
|
+
# 2010-07-05 17:00:00 +0200 (Mon, 01 Jan 2010)
|
34
|
+
# 215
|
35
|
+
# Description of log
|
36
|
+
#
|
37
|
+
# @example Usage
|
38
|
+
# @revision = Revision.new('...')
|
39
|
+
# @revision.author # => 'john'
|
40
|
+
# @revision.message # => 'Description of log'
|
41
|
+
# @revision.date # => (instance of Time)
|
42
|
+
# @revision.projects # => ['/project1', 'project2', ...]
|
43
|
+
#
|
44
|
+
# @author Arjan van der Gaag
|
45
|
+
# @since 0.3.0
|
46
|
+
class Revision
|
47
|
+
# The raw output of the svnlook command.
|
48
|
+
attr_reader :raw
|
49
|
+
|
50
|
+
# A list of all directories that were changed in this revision
|
51
|
+
attr_reader :dirs_changed
|
52
|
+
|
53
|
+
# the parsed Hash of attributes for this revision
|
54
|
+
attr_reader :attributes
|
55
|
+
|
56
|
+
def initialize(revision_number, info, dirs_changed)
|
57
|
+
@attributes = { :number => revision_number.to_i }
|
58
|
+
@raw = info
|
59
|
+
@dirs_changed = dirs_changed.split
|
60
|
+
parse
|
61
|
+
end
|
62
|
+
|
63
|
+
%w{author date message number}.each do |name|
|
64
|
+
define_method(name) do
|
65
|
+
attributes[name.to_sym]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Creates a list of directory paths in the repository that have changes
|
70
|
+
# and contain a <tt>trunk</tt>, <tt>branches</tt> or <tt>tags</tt>
|
71
|
+
# directory.
|
72
|
+
#
|
73
|
+
# For example, a changed path in like <tt>/topdir/project_name/trunk</tt>
|
74
|
+
# would result in <tt>/topdir/project_name</tt>.
|
75
|
+
#
|
76
|
+
# @return [Array<String>] list of changed project paths
|
77
|
+
def projects
|
78
|
+
pattern = /\/(trunk|branches|tags)/
|
79
|
+
dirs_changed.grep(pattern).map do |dir|
|
80
|
+
dir.split(pattern, 2).first
|
81
|
+
end.uniq
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# Parses the raw log of svnlook into a Hash of attributes.
|
87
|
+
def parse
|
88
|
+
raise ArgumentError, 'Could not parse Subversion info: expected at least 4 lines' if raw.split("\n").size < 4
|
89
|
+
author, timestamp, size, message = raw.split("\n", 4)
|
90
|
+
attributes[:author] = author
|
91
|
+
attributes[:date] = Time.parse(timestamp)
|
92
|
+
attributes[:message] = message.chomp
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module Subtrigger
|
2
|
+
# A <tt>Rule</tt> object knows when to fire some kind of action for some
|
3
|
+
# kind of revision. When the Subversion hook is fired, a Rule can inspect it
|
4
|
+
# and choose whether or not to fire its trigger (a piece code defined by the
|
5
|
+
# user).
|
6
|
+
#
|
7
|
+
# In the first example, the rule will output <tt>fired</tt> whenever a
|
8
|
+
# <tt>Revision</tt> comes along with a message containing <tt>foo</tt>.
|
9
|
+
#
|
10
|
+
# In the second example, we find all applicable rules for a given
|
11
|
+
# <tt>Revision</tt> object. We can then run each of them.
|
12
|
+
#
|
13
|
+
# @example 1: Define a simple Rule
|
14
|
+
# Rule.new(/foo/) { puts 'fired' }
|
15
|
+
#
|
16
|
+
# @example 2: Finding and firing Rules
|
17
|
+
# rev = Revision.new
|
18
|
+
# Rule.matching(rev).map { |rule| rule.run(rev) }
|
19
|
+
#
|
20
|
+
# @since 0.3.0
|
21
|
+
# @author Arjan van der Gaag
|
22
|
+
class Rule
|
23
|
+
|
24
|
+
# Exception for when trying to apply a rule to something other than an
|
25
|
+
# instance of Revision.
|
26
|
+
CannotCompare = Class.new(Exception)
|
27
|
+
|
28
|
+
# A hash of Revision attributes and regular expressions to match against
|
29
|
+
attr_reader :criteria
|
30
|
+
|
31
|
+
# The callback to run on a match
|
32
|
+
attr_reader :block
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
@rules = []
|
37
|
+
|
38
|
+
# Keep track of Rule objects that are created in a class instance variable
|
39
|
+
#
|
40
|
+
# @param [Rule] child is the new Rule object
|
41
|
+
# @return [Array<Rule>] the total list of children
|
42
|
+
def self.register(child)
|
43
|
+
@rules << child
|
44
|
+
end
|
45
|
+
|
46
|
+
public
|
47
|
+
|
48
|
+
# Return an array of all rules currently defined.
|
49
|
+
#
|
50
|
+
# @return [Array<Rule>]
|
51
|
+
def self.rules
|
52
|
+
@rules
|
53
|
+
end
|
54
|
+
|
55
|
+
# Reset the list of known rules, deleting all currently known rules.
|
56
|
+
#
|
57
|
+
# @return nil
|
58
|
+
def self.reset
|
59
|
+
@rules = []
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return an array of all existing Rule objects that match the given
|
63
|
+
# revision.
|
64
|
+
#
|
65
|
+
# @param [Revision] revision is the revision to compare rules to.
|
66
|
+
# @return [Array<Rule>] list of all matching rules
|
67
|
+
def self.matching(revision)
|
68
|
+
@rules.select { |child| child === revision }
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create a new Rule object with criteria for different properties of a
|
72
|
+
# Revision. The required block defines the callback to run. It will have
|
73
|
+
# the current Revision object yielded to it.
|
74
|
+
#
|
75
|
+
# Criteria are Ruby objects that should match (`===`) a Revision's
|
76
|
+
# attributes. These would usually be regular expressions, but they
|
77
|
+
# can be strings or custom objects if you want to.
|
78
|
+
#
|
79
|
+
# @overload initialize(pattern, &block)
|
80
|
+
# Define a rule with a pattern matching the log message
|
81
|
+
# @param [Regex] pattern is the regular expression to match against
|
82
|
+
# the revision's log message
|
83
|
+
# @overload initialize(options, &block)
|
84
|
+
# Define a rule with various criteria in a hash.
|
85
|
+
# @param [Hash] options defines matching criteria.
|
86
|
+
# @option options :author Criterium for Revision#author
|
87
|
+
# @option options :date Criterium for Revision#date
|
88
|
+
# @option options :number Criterium for Revision#number
|
89
|
+
# @option options :project Criterium for Revision#project
|
90
|
+
def initialize(pattern_or_options, &block)
|
91
|
+
raise ArgumentError, 'a Rule requires a block' unless block_given?
|
92
|
+
|
93
|
+
# If not given a hash, we build a hash defaulting on message
|
94
|
+
unless pattern_or_options.is_a?(Hash)
|
95
|
+
pattern_or_options = { :message => pattern_or_options }
|
96
|
+
end
|
97
|
+
|
98
|
+
@criteria, @block = pattern_or_options, block
|
99
|
+
@criteria.inspect
|
100
|
+
self.class.register self
|
101
|
+
end
|
102
|
+
|
103
|
+
# Call this Rule's callback method with the give Revision object.
|
104
|
+
# @return [nil]
|
105
|
+
def run(rev)
|
106
|
+
@rev = rev
|
107
|
+
block.call(@rev, collect_captures)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Use {Rule#matches?} to see if this <tt>Rule</tt> matches the given
|
111
|
+
# <tt>Revision</tt>.
|
112
|
+
#
|
113
|
+
# @param [Object] the object to compare to
|
114
|
+
# @return [Boolean]
|
115
|
+
# @see Rule#matches?
|
116
|
+
def ===(other)
|
117
|
+
matches?(other)
|
118
|
+
rescue CannotCompare
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
# See if the current rule matches a given subversion revision.
|
123
|
+
#
|
124
|
+
# @param [Revision] revision the Revision object to compare to.
|
125
|
+
# @return [Boolean]
|
126
|
+
# @see Rule#===
|
127
|
+
# @raise Subtrigger::Rule::CannotCompare when comparing to something other
|
128
|
+
# than a revision.
|
129
|
+
def matches?(revision)
|
130
|
+
raise CannotCompare unless @criteria.keys.all? { |k| k == :all || revision.respond_to?(k) }
|
131
|
+
match = @criteria.any?
|
132
|
+
@criteria.each_pair do |key, value|
|
133
|
+
if key == :all
|
134
|
+
match = (value === revision)
|
135
|
+
else
|
136
|
+
match &= (value === revision.send(key.to_sym))
|
137
|
+
end
|
138
|
+
end
|
139
|
+
match
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
# When using regular expressions to match against string values, we
|
145
|
+
# want to be able to get to any captured groups. This method scans all
|
146
|
+
# string values with their Regex matchers and collects all captured
|
147
|
+
# groups into a namespaced hash.
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
# Rule.new /hello, (.+)!/ do |revision, matches|
|
151
|
+
# puts matches.inspect
|
152
|
+
# end
|
153
|
+
# # => { :message => ['world'] }
|
154
|
+
#
|
155
|
+
# @return [Hash] all captured groups per Revision attribute tested
|
156
|
+
# @todo this only passes on capture groups, not the entire match ($&)
|
157
|
+
def collect_captures
|
158
|
+
criteria.inject({}) do |output, (key, value)|
|
159
|
+
next if key == :all
|
160
|
+
output[key] = @rev.send(key.to_sym).scan(value).flatten if value.is_a?(Regexp)
|
161
|
+
output
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Subtrigger
|
2
|
+
# Reads, parses and manages inline templates.
|
3
|
+
#
|
4
|
+
# When you define string templates at the end of your rules file, this class
|
5
|
+
# can parse and keep track of them. You can then easily retrieve them again
|
6
|
+
# and optionally format it like with <tt>String#%</tt>.
|
7
|
+
#
|
8
|
+
# You simply define a new template using <tt>@@</tt>, followed by a name and
|
9
|
+
# then the textual contents (see the example below).
|
10
|
+
#
|
11
|
+
# You can read the templates and use them, for example, in e-mails.
|
12
|
+
#
|
13
|
+
# @example Defining templates
|
14
|
+
# # at the end of your Ruby file:
|
15
|
+
# __END__
|
16
|
+
# @@ Template 1
|
17
|
+
# Foo
|
18
|
+
# @@ Template 2
|
19
|
+
# Hello, %s!
|
20
|
+
#
|
21
|
+
# @example Parsing templates
|
22
|
+
# Template.parse(__DATA__.read)
|
23
|
+
#
|
24
|
+
# @example Using templates
|
25
|
+
# Template.find('Template 1') # => 'Foo'
|
26
|
+
#
|
27
|
+
# @example Formatting templates
|
28
|
+
# Template.find('Template 2') # => 'Hello, %s!'
|
29
|
+
# Template.find('Template 2').format('world') # => 'Hello, world!'
|
30
|
+
#
|
31
|
+
# @author Arjan van der Gaag
|
32
|
+
# @since 0.3.0
|
33
|
+
class Template
|
34
|
+
# The unique identifier for this template
|
35
|
+
attr_reader :name
|
36
|
+
|
37
|
+
# The actual contents of the template
|
38
|
+
attr_reader :string
|
39
|
+
|
40
|
+
# List of defined templates the class tracks
|
41
|
+
@children = []
|
42
|
+
|
43
|
+
# The pattern that separates one template from another
|
44
|
+
TEMPLATE_DELIMITER = /^@@ (.*)\n/
|
45
|
+
|
46
|
+
# Exception raised when a string cannot be parsed into templates,
|
47
|
+
# because the delimiter cannot be found
|
48
|
+
Unparseable = Class.new(Exception)
|
49
|
+
|
50
|
+
# Parse the contents of a string and extract templates from it. These are
|
51
|
+
# tracked so you can use {Template#find} to retrieve them by name.
|
52
|
+
#
|
53
|
+
# @param [String] the contents of your rules file's <tt>__DATA__.read</tt>
|
54
|
+
# @return [nil]
|
55
|
+
def self.parse(string)
|
56
|
+
raise Unparseable, "Could not split into templates: #{string.inspect}" unless string =~ TEMPLATE_DELIMITER
|
57
|
+
string.split(TEMPLATE_DELIMITER).map(&:chomp).slice(1..-1).each_slice(2) do |name, content|
|
58
|
+
@children << new(name, content)
|
59
|
+
end
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
# Finds and returns the content of the template by the given name.
|
64
|
+
#
|
65
|
+
# @param [String] name is the name of the template
|
66
|
+
# @return [String] is Template#content
|
67
|
+
def self.find(name)
|
68
|
+
@children.find { |child|
|
69
|
+
child.name == name
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
# Convert to string using the textual contents of the template
|
74
|
+
# @return [String]
|
75
|
+
def to_s
|
76
|
+
string
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the contents of the template and interpolate any given
|
80
|
+
# arguments into it.
|
81
|
+
#
|
82
|
+
# @example Getting a template and using interpolation
|
83
|
+
# template.to_s # => 'Dear %s...'
|
84
|
+
# template.format 'John' # => 'Dear John...'
|
85
|
+
# @return [String] the formatted template contents
|
86
|
+
def format(*args)
|
87
|
+
to_s % [*args]
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param [String] name is the unique identifier of a template
|
91
|
+
# @param [String] string is the contents of the template.
|
92
|
+
def initialize(name, string)
|
93
|
+
@name, @string = name, string
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/subtrigger.gemspec
CHANGED
@@ -5,65 +5,65 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{subtrigger}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Arjan van der Gaag"]
|
12
|
-
s.date = %q{2010-
|
13
|
-
s.default_executable = %q{subtrigger}
|
12
|
+
s.date = %q{2010-07-16}
|
14
13
|
s.description = %q{This gem allows you to create simple Ruby triggers for Subversion commit messages, responding to keywords in your log messages to send e-mails, deploy sites or do whatever you need.}
|
15
14
|
s.email = %q{arjan@arjanvandergaag.nl}
|
16
|
-
s.executables = ["subtrigger"]
|
17
15
|
s.extra_rdoc_files = [
|
18
|
-
"
|
19
|
-
"README.rdoc"
|
16
|
+
"README.md"
|
20
17
|
]
|
21
18
|
s.files = [
|
22
19
|
".document",
|
23
20
|
".gitignore",
|
24
|
-
"
|
25
|
-
"README.rdoc",
|
21
|
+
"README.md",
|
26
22
|
"Rakefile",
|
27
23
|
"VERSION",
|
28
|
-
"bin/subtrigger",
|
29
24
|
"lib/subtrigger.rb",
|
30
|
-
"lib/subtrigger/
|
31
|
-
"lib/subtrigger/
|
32
|
-
"lib/subtrigger/
|
25
|
+
"lib/subtrigger/dsl.rb",
|
26
|
+
"lib/subtrigger/path.rb",
|
27
|
+
"lib/subtrigger/revision.rb",
|
28
|
+
"lib/subtrigger/rule.rb",
|
29
|
+
"lib/subtrigger/template.rb",
|
33
30
|
"subtrigger.gemspec",
|
34
|
-
"test/
|
35
|
-
"test/
|
36
|
-
"test/
|
37
|
-
"test/
|
38
|
-
"test/
|
31
|
+
"test/test_helper.rb",
|
32
|
+
"test/test_path.rb",
|
33
|
+
"test/test_revision.rb",
|
34
|
+
"test/test_rule.rb",
|
35
|
+
"test/test_template.rb"
|
39
36
|
]
|
40
37
|
s.homepage = %q{http://github.com/avdgaag/subtrigger}
|
41
38
|
s.rdoc_options = ["--charset=UTF-8"]
|
42
39
|
s.require_paths = ["lib"]
|
43
|
-
s.rubygems_version = %q{1.3.
|
40
|
+
s.rubygems_version = %q{1.3.7}
|
44
41
|
s.summary = %q{Create post-commit triggers for Subversion commit messages}
|
45
42
|
s.test_files = [
|
46
|
-
"test/
|
47
|
-
"test/
|
48
|
-
"test/
|
49
|
-
"test/
|
50
|
-
"test/
|
43
|
+
"test/test_helper.rb",
|
44
|
+
"test/test_path.rb",
|
45
|
+
"test/test_revision.rb",
|
46
|
+
"test/test_rule.rb",
|
47
|
+
"test/test_template.rb"
|
51
48
|
]
|
52
49
|
|
53
50
|
if s.respond_to? :specification_version then
|
54
51
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
52
|
s.specification_version = 3
|
56
53
|
|
57
|
-
if Gem::Version.new(Gem::
|
54
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
55
|
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
59
56
|
s.add_development_dependency(%q<mocha>, [">= 0"])
|
57
|
+
s.add_runtime_dependency(%q<pony>, [">= 0"])
|
60
58
|
else
|
61
59
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
62
60
|
s.add_dependency(%q<mocha>, [">= 0"])
|
61
|
+
s.add_dependency(%q<pony>, [">= 0"])
|
63
62
|
end
|
64
63
|
else
|
65
64
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
66
65
|
s.add_dependency(%q<mocha>, [">= 0"])
|
66
|
+
s.add_dependency(%q<pony>, [">= 0"])
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|