verbvector 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/README.textile +96 -0
- data/Rakefile +12 -0
- data/lib/verbvector.rb +295 -0
- data/lib/verbvector/version.rb +8 -0
- data/test/fixtures/tense_list +21 -0
- data/test/test_generator.rb +135 -0
- data/verbvector.gemspec +21 -0
- metadata +64 -0
data/Gemfile
ADDED
data/README.textile
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
h1. Lingustics::Verbs::Verbvector::VerbvectorGenerator
|
2
|
+
|
3
|
+
This is, what I hope will become a burgeoning array of tools for those interested in languages and linguistics. Any given language's conjugation can be seen as a _vector_, an intersection of several _aspects_ that singularly identify the correct grammatical choice for a given utterance.
|
4
|
+
|
5
|
+
In Latin, for example, this is defined by *voice*, *mood*, *tense*, *number*, and *person*. Other languages have a more- or less-specific vector. The function of this module, therefore, is to make the exhaustive generation of these vectors simple.
|
6
|
+
|
7
|
+
h1. Usage
|
8
|
+
|
9
|
+
Usage is performed by passing a DSL into the module. The module then returns a module based on the _language_ parameter. This resultant module can be mixed-in to your class (say, *LanguageVerb*) to define all the possible method calls (for playing nice with responds_to in case *LanguageVerb* uses metaprogramming in its responses)
|
10
|
+
|
11
|
+
h1. DSL
|
12
|
+
|
13
|
+
Initial attempts at producing a verb tense generator wound up producing an incredibly confusing snarl of @eval@ statments and other oddities and wound up neither being specific enough to be easily used nor portable enough to be generally helpful.
|
14
|
+
|
15
|
+
The DSL is simple and recognizes the following statements:
|
16
|
+
|
17
|
+
h2. language :LanguageName _block_
|
18
|
+
|
19
|
+
The first statement. It takes a block that will be further processed and which contains other defining statements for the :Language. The only accepted argument is a symbol for the Language name.
|
20
|
+
|
21
|
+
h2. all_vectors :position _block_
|
22
|
+
|
23
|
+
This defines an aspect common to *all* vectors. Aspects are defined in an anonymous hash included in the block. The :position symbol is not presently used but instead stacks from left to right. This may have reason to change.
|
24
|
+
|
25
|
+
Inside the block one gives the _aspect_ as a symbol and then gives an array containing the valid specifications for that aspect.
|
26
|
+
|
27
|
+
Example:
|
28
|
+
|
29
|
+
<pre>
|
30
|
+
{
|
31
|
+
:voice => %w(active passive),
|
32
|
+
:mood => %w(indicative subjunctive imperative)
|
33
|
+
}
|
34
|
+
</pre>
|
35
|
+
|
36
|
+
h2. vectors_that Regexp _block_
|
37
|
+
|
38
|
+
This defines an aspect common to *all* vectors. Aspects are defined in an anonymous hash included in the block. Thus for all existing vectors in the vector stack that match the Regexp, this aspect/specification will be added
|
39
|
+
|
40
|
+
<pre>
|
41
|
+
{
|
42
|
+
:tense => %w(present imperfect future
|
43
|
+
perfect pastperfect futureperfect)
|
44
|
+
}
|
45
|
+
</pre>
|
46
|
+
|
47
|
+
h2. exception (:remove|:add), :branch _block_
|
48
|
+
|
49
|
+
Languages are not perfectly rational in their combinations. Occasionally you may need to manually add a vector or, more likely, prune a vector.
|
50
|
+
|
51
|
+
h1. Example
|
52
|
+
|
53
|
+
_Taken from the test/ directory_
|
54
|
+
|
55
|
+
<pre>
|
56
|
+
|
57
|
+
Lingustics::Verbs::Verbvector::VerbvectorGenerator.new do
|
58
|
+
language :Latin do
|
59
|
+
all_vectors :start_with do
|
60
|
+
{
|
61
|
+
:voice => %w(active passive),
|
62
|
+
:mood => %w(indicative subjunctive imperative)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
vectors_that /.*_indicative_mood/ do
|
66
|
+
{
|
67
|
+
:tense => %w(present imperfect future
|
68
|
+
perfect pastperfect futureperfect)
|
69
|
+
}
|
70
|
+
end
|
71
|
+
vectors_that /.*_subjunctive_mood/ do
|
72
|
+
{
|
73
|
+
:tense => %w(present imperfect
|
74
|
+
perfect pastperfect)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
vectors_that /.*_imperative_mood/ do
|
78
|
+
{
|
79
|
+
:tense => %w(present)
|
80
|
+
}
|
81
|
+
end
|
82
|
+
all_vectors :end_with do
|
83
|
+
{
|
84
|
+
:number => %w(singular plural),
|
85
|
+
:person => %w(first second third)
|
86
|
+
}
|
87
|
+
end
|
88
|
+
exception :remove, :passive_voice_imperative_mood_present_tense
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
</pre>
|
93
|
+
|
94
|
+
h1. Author
|
95
|
+
|
96
|
+
Steven G. Harms
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
#Added to get testing working
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new(:test)
|
7
|
+
|
8
|
+
require "rake/rdoctask"
|
9
|
+
Rake::RDocTask.new do |rd|
|
10
|
+
rd.rdoc_files.include("lib/**/*.rb")
|
11
|
+
rd.rdoc_dir = "rdoc"
|
12
|
+
end
|
data/lib/verbvector.rb
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require "verbvector"
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
# Generalized module for handling lingustics processing
|
6
|
+
module Linguistics
|
7
|
+
# Generalized module related to the conjugation of verbs
|
8
|
+
module Verbs
|
9
|
+
# Each language's specific verb conjugation can be described in terms of a
|
10
|
+
# verbal vector, the particular intersection of its voice, tense, number,
|
11
|
+
# and person.
|
12
|
+
module Verbvector
|
13
|
+
# A class designed to generate a module that can be mixed in into a
|
14
|
+
# given type of verb (French, Latin, Spanish). I'm not trying to be
|
15
|
+
# obscure, but I'm trying to be very generalizable.
|
16
|
+
class VerbvectorGenerator
|
17
|
+
attr_reader :language, :aspect_list, :vector_list, :cluster_methods,
|
18
|
+
:respondable_methods
|
19
|
+
|
20
|
+
# Class methods go here
|
21
|
+
class << self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Initialization
|
25
|
+
#
|
26
|
+
# Takes the descriptive block of the tense structure in a DSL format
|
27
|
+
def initialize(&b)
|
28
|
+
@aspect_list = []
|
29
|
+
@vector_list = []
|
30
|
+
@respondable_methods = []
|
31
|
+
@cluster_methods = {}
|
32
|
+
@language = ""
|
33
|
+
|
34
|
+
# Let's remember the difference between instance_ and class_eval.
|
35
|
+
#
|
36
|
+
# "class_eval sets things up as if you were in the body of a class
|
37
|
+
# definition, so method definitions will define instance methods:"
|
38
|
+
#
|
39
|
+
# "instance_eval on a class acts as if you were working inside the
|
40
|
+
# singleton class of self. Therefore, any methods you define will
|
41
|
+
# become class methods." - prog ruby 1.9 v3, prag programmers p.388
|
42
|
+
#
|
43
|
+
# As such, instance_eval here is about to apply this eval to the
|
44
|
+
# eigenclass. That means any instance of this class will have this
|
45
|
+
# method.
|
46
|
+
instance_eval &b
|
47
|
+
|
48
|
+
@aspect_list.sort!
|
49
|
+
end
|
50
|
+
|
51
|
+
# Vectors are specified at their most atomic
|
52
|
+
# Sometimes it is handy to return all the values that match
|
53
|
+
# "up to" a given aspect and then uniq'ify them
|
54
|
+
|
55
|
+
def match_vector_upto_aspect(s)
|
56
|
+
@vector_list.compact.sort.grep(/#{s}/).map{ |x|
|
57
|
+
x.sub(/(^.*#{s}).*/,%q(\1))
|
58
|
+
}.uniq
|
59
|
+
end
|
60
|
+
|
61
|
+
# Creates the anonymous module based on the contents of @vector_list.
|
62
|
+
# The method names in this module are just stubs _except_ those that
|
63
|
+
# are loaded into the @cluster_methods hash. By generating all the
|
64
|
+
# method names we allow +responds_to?+ to work as expected.
|
65
|
+
|
66
|
+
def method_extension_module
|
67
|
+
v = @vector_list
|
68
|
+
c = @cluster_methods
|
69
|
+
l = @language
|
70
|
+
r = @respondable_methods
|
71
|
+
|
72
|
+
Module.new do
|
73
|
+
# This defines instance methods on the Module
|
74
|
+
# m.instance_methods #=> [:say_foo, :say_bar, :say_bat]
|
75
|
+
|
76
|
+
# Note, you can't use @someArray in the iteration because
|
77
|
+
# self has changed to this anonymous module. Since a block
|
78
|
+
# is a binding, it has the local context (including 'v')
|
79
|
+
# bundled up with it -- despte self having changed!
|
80
|
+
# Therefore, the following works.
|
81
|
+
|
82
|
+
# Define a method for each name in vector_list.
|
83
|
+
raise("Language was not defined." ) if l.nil?
|
84
|
+
|
85
|
+
v.each do |m|
|
86
|
+
r << "#{m}"
|
87
|
+
define_method "#{l.downcase}_#{m}".to_sym do
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Write something to spit out the vectors as well.
|
92
|
+
define_method :vector_list do
|
93
|
+
return v
|
94
|
+
end
|
95
|
+
|
96
|
+
define_method :respondable_methods do
|
97
|
+
return r
|
98
|
+
end
|
99
|
+
|
100
|
+
# Spit out the clustered methods
|
101
|
+
c.each_pair do |k,v|
|
102
|
+
define_method k do
|
103
|
+
v.call
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Language takes a symbol for +l+ the language whose verb we seek to
|
111
|
+
# model. It then takes a block for the sub-specification of the verbs
|
112
|
+
# of that language.
|
113
|
+
def language(*l,&b)
|
114
|
+
return @language if (l[0].nil? and not @language.nil?)
|
115
|
+
@language = l[0]
|
116
|
+
instance_eval &b
|
117
|
+
end
|
118
|
+
|
119
|
+
# Method generates tense vectors based on aspects that are assumed to
|
120
|
+
# apply to all possible vectors. These would be seen as the most
|
121
|
+
# general aspects possible For example, while only *some* vectors are
|
122
|
+
# present tense, *all* vectors have a voice.
|
123
|
+
|
124
|
+
def all_vectors(position,&b)
|
125
|
+
# Make sure there is a block given
|
126
|
+
return unless (block_given? or yield.first)
|
127
|
+
|
128
|
+
# Sentinel condition for stopping recursive call
|
129
|
+
return @vector_list unless yield.first
|
130
|
+
|
131
|
+
# Provided that there was a block, collect the DSL hash into
|
132
|
+
# specifications
|
133
|
+
specifications = yield
|
134
|
+
|
135
|
+
# Extract the first k/v
|
136
|
+
specification = specifications.first
|
137
|
+
|
138
|
+
# Based on the key for each row, take that content and postpend
|
139
|
+
# it.to_s to the ending of each value held in the hash element value
|
140
|
+
expanded_specification = combinatorialize(specification)
|
141
|
+
|
142
|
+
# Remove the expanded value from the specifications hash
|
143
|
+
specifications.delete(specification[0])
|
144
|
+
|
145
|
+
# Keep a record of aspects we have seen
|
146
|
+
@aspect_list.push specification[0]
|
147
|
+
@aspect_list.uniq!
|
148
|
+
|
149
|
+
# If it's the first go round put the first set of values in. In
|
150
|
+
# general these should be the leftmost and theremfore most general
|
151
|
+
if @vector_list.empty?
|
152
|
+
@vector_list = expanded_specification
|
153
|
+
else
|
154
|
+
# If there's already a definition in the tens list, for each of
|
155
|
+
# the _existing_ values add the array of strings seen in
|
156
|
+
# expanded_specifications thereunto. Hold them in 'temp' and then
|
157
|
+
# set @vector_list to temp.
|
158
|
+
temp = []
|
159
|
+
@vector_list.each do |base|
|
160
|
+
expanded_specification.each do |u|
|
161
|
+
temp.push base+"_#{u}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
@vector_list = temp
|
165
|
+
end
|
166
|
+
|
167
|
+
# Recursive call, sentnel contition is at the top of the method
|
168
|
+
all_vectors(position) do
|
169
|
+
specifications
|
170
|
+
end
|
171
|
+
|
172
|
+
instance_eval &b
|
173
|
+
end
|
174
|
+
|
175
|
+
# Method appends vector definitions _if_ the +condition+ (a RegEx) is satisfied
|
176
|
+
def vectors_that(condition,&b)
|
177
|
+
matching_stems = @vector_list.grep condition
|
178
|
+
temp = []
|
179
|
+
|
180
|
+
specifications = yield
|
181
|
+
|
182
|
+
# Extract the first k/v
|
183
|
+
specification = specifications.first
|
184
|
+
|
185
|
+
# Based on the key for each row, take that content and postpend
|
186
|
+
# it.to_s to the ending of each value held in the hash element value
|
187
|
+
expanded_specification = combinatorialize(specification)
|
188
|
+
|
189
|
+
# Remove the expanded value from the specifications hash
|
190
|
+
specifications.delete(specification[0])
|
191
|
+
|
192
|
+
# Keep a record of aspects we have seen
|
193
|
+
@aspect_list.push specification[0]
|
194
|
+
@aspect_list.uniq!
|
195
|
+
|
196
|
+
# So we grepped the desired stems and stored them in matching_stems
|
197
|
+
# First we delete those stems (becasue we're going to further specify) them
|
198
|
+
matching_stems.each do |x|
|
199
|
+
@vector_list.delete x
|
200
|
+
expanded_specification.each do |u|
|
201
|
+
temp.push x+"_#{u}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Combine the original list with the freshly expanded list
|
206
|
+
@vector_list = (@vector_list + temp).sort
|
207
|
+
end
|
208
|
+
|
209
|
+
# Languages are not entirely rational, while something _ought_ exist
|
210
|
+
# by the rules of rational combination, some times they simply _don't_
|
211
|
+
# exist. That's what this method is for.
|
212
|
+
#
|
213
|
+
# +action+ :: +:remove+ or +:add+
|
214
|
+
# +id+ :: method name to remove
|
215
|
+
# _block_ :: used to add
|
216
|
+
def exception(action, id, &b)
|
217
|
+
if action == :remove
|
218
|
+
@vector_list.delete_if {|x| x =~ /#{id.to_s}/ }
|
219
|
+
elsif action == :add
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Method to take a hash key where the key is an _aspect_ and the value
|
224
|
+
# is an array of specifications valid for that _aspect_.
|
225
|
+
def combinatorialize(h)
|
226
|
+
results = []
|
227
|
+
h[1].each do |k|
|
228
|
+
results.push "#{k}_#{h[0]}"
|
229
|
+
end
|
230
|
+
results
|
231
|
+
end
|
232
|
+
|
233
|
+
# Method to allow "clusters" of simiar vectors to be identified (see:
|
234
|
+
# +match_upto+) based on the 0th string match. These clusters can be
|
235
|
+
# called by the method namd provided as the 2nd argument (0-index)
|
236
|
+
#
|
237
|
+
# This allows
|
238
|
+
# active_voice_indicative_mood_imperfect_tense_singular_number_third_p
|
239
|
+
# erson and other such to be 'clustered' as
|
240
|
+
# active_voice_indicative_mood_imperfect_tense. This means the actual
|
241
|
+
# method will probably be done in the "cluster" and some sort of
|
242
|
+
# secondary logic (method_missing) will do the final resolution
|
243
|
+
#
|
244
|
+
# Nevertheless, per the logic of this library, by defining all the
|
245
|
+
# atomic, we play nice and give respond_to? all the information it
|
246
|
+
# needs
|
247
|
+
#
|
248
|
+
# *Example:* <tt>cluster_on /active_voice.*third/, "as method", :active_thirds</tt>
|
249
|
+
#
|
250
|
+
#
|
251
|
+
# This means you want to collect several method names that match the Regexp and make them identifiable by a call to the
|
252
|
+
# method +active_thirds+
|
253
|
+
#
|
254
|
+
# Alternatively, you might want to use a String or Symbol (making use of match_upto).
|
255
|
+
#
|
256
|
+
# *Example:* <tt>cluster_on :tense, "as method", :tense_list</tt>
|
257
|
+
#
|
258
|
+
# +match+ :: The String or Regex match_upto will use for matching
|
259
|
+
# +junk+ :: Syntactic sugar, a string that makes the DSL look sensible
|
260
|
+
# +method_name+ :: The method in the anonymous module that returns the matched method names. See +create_module+
|
261
|
+
def cluster_on(match, junk, method_name)
|
262
|
+
|
263
|
+
clustered_matches =
|
264
|
+
if match.class == Regexp
|
265
|
+
@vector_list.grep match
|
266
|
+
elsif match.class.to_s =~ /^(String|Symbol)/
|
267
|
+
# Get the items that match_upto the specified clustering token
|
268
|
+
match_vector_upto_aspect(match.to_s)
|
269
|
+
else
|
270
|
+
# This shouldn't happen, we should get a Regexp or a String/Symbol
|
271
|
+
raise "Didn't fire for clustered match: #{match}"
|
272
|
+
end
|
273
|
+
|
274
|
+
unless clustered_matches.nil?
|
275
|
+
# No, this should not be done:
|
276
|
+
# ...and add them to the @vector_list.
|
277
|
+
# @vector_list += clustered_matches
|
278
|
+
# Clustered methods need to be defined, for real, somewhere. We
|
279
|
+
# should not claim to respond to them here, but rather let the
|
280
|
+
# framework using verbvector have the responsibility for
|
281
|
+
# implementation.
|
282
|
+
|
283
|
+
|
284
|
+
# Now, define a Proc that can correspond to arg[2]
|
285
|
+
@cluster_methods[method_name] = Proc.new do
|
286
|
+
clustered_matches
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
active_voice_imperative_mood_present_tense
|
2
|
+
active_voice_indicative_mood_present_tense
|
3
|
+
active_voice_indicative_mood_imperfect_tense
|
4
|
+
active_voice_indicative_mood_future_tense
|
5
|
+
active_voice_indicative_mood_perfect_tense
|
6
|
+
active_voice_indicative_mood_pastperfect_tense
|
7
|
+
active_voice_indicative_mood_futureperfect_tense
|
8
|
+
passive_voice_indicative_mood_present_tense
|
9
|
+
passive_voice_indicative_mood_imperfect_tense
|
10
|
+
passive_voice_indicative_mood_future_tense
|
11
|
+
passive_voice_indicative_mood_perfect_tense
|
12
|
+
passive_voice_indicative_mood_pastperfect_tense
|
13
|
+
passive_voice_indicative_mood_futureperfect_tense
|
14
|
+
active_voice_subjunctive_mood_present_tense
|
15
|
+
active_voice_subjunctive_mood_imperfect_tense
|
16
|
+
active_voice_subjunctive_mood_perfect_tense
|
17
|
+
active_voice_subjunctive_mood_pastperfect_tense
|
18
|
+
passive_voice_subjunctive_mood_present_tense
|
19
|
+
passive_voice_subjunctive_mood_imperfect_tense
|
20
|
+
passive_voice_subjunctive_mood_perfect_tense
|
21
|
+
passive_voice_subjunctive_mood_pastperfect_tense
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
require "verbvector"
|
5
|
+
|
6
|
+
class TestVerbVector < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@vv =
|
9
|
+
Linguistics::Verbs::Verbvector::VerbvectorGenerator.new do
|
10
|
+
language :Latin do
|
11
|
+
all_vectors :start_with do
|
12
|
+
{
|
13
|
+
:voice => %w(active passive),
|
14
|
+
:mood => %w(indicative subjunctive imperative)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
vectors_that /.*_indicative_mood/ do
|
18
|
+
{
|
19
|
+
:tense => %w(present imperfect future
|
20
|
+
perfect pastperfect futureperfect)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
vectors_that /.*_subjunctive_mood/ do
|
24
|
+
{
|
25
|
+
:tense => %w(present imperfect
|
26
|
+
perfect pastperfect)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
vectors_that /.*_imperative_mood/ do
|
30
|
+
{
|
31
|
+
:tense => %w(present)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
all_vectors :end_with do
|
35
|
+
{
|
36
|
+
:number => %w(singular plural),
|
37
|
+
:person => %w(first second third)
|
38
|
+
}
|
39
|
+
end
|
40
|
+
exception :remove, :passive_voice_imperative_mood_present_tense
|
41
|
+
cluster_on :tense, "as method", :tense_list
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_basics
|
47
|
+
v = Linguistics::Verbs::Verbvector::VerbvectorGenerator.new do
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_not_nil(v)
|
51
|
+
assert_equal(:Latin, @vv.language)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_vector_matcher
|
55
|
+
assert_equal 2, @vv.match_vector_upto_aspect("voice").length
|
56
|
+
assert_equal 5, @vv.match_vector_upto_aspect("mood").length
|
57
|
+
assert_equal 21, @vv.match_vector_upto_aspect("tense").length
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_tense_resolution
|
61
|
+
tense_list = Array.new(File.open(File.join(File.dirname(__FILE__), *%w[fixtures tense_list])).read.split /\s/)
|
62
|
+
tense_list.sort!
|
63
|
+
|
64
|
+
tc = Class.new
|
65
|
+
tc.extend @vv.method_extension_module
|
66
|
+
|
67
|
+
assert_equal tense_list, tc.tense_list
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_clustering_with_regex
|
71
|
+
t =
|
72
|
+
Linguistics::Verbs::Verbvector::VerbvectorGenerator.new do
|
73
|
+
language :Latin do
|
74
|
+
all_vectors :start_with do
|
75
|
+
{
|
76
|
+
:voice => %w(active passive),
|
77
|
+
:mood => %w(indicative subjunctive imperative)
|
78
|
+
}
|
79
|
+
end
|
80
|
+
vectors_that /.*_indicative_mood/ do
|
81
|
+
{
|
82
|
+
:tense => %w(present imperfect future
|
83
|
+
perfect pastperfect futureperfect)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
vectors_that /.*_subjunctive_mood/ do
|
87
|
+
{
|
88
|
+
:tense => %w(present imperfect
|
89
|
+
perfect pastperfect)
|
90
|
+
}
|
91
|
+
end
|
92
|
+
vectors_that /.*_imperative_mood/ do
|
93
|
+
{
|
94
|
+
:tense => %w(present)
|
95
|
+
}
|
96
|
+
end
|
97
|
+
all_vectors :end_with do
|
98
|
+
{
|
99
|
+
:number => %w(singular plural),
|
100
|
+
:person => %w(first second third)
|
101
|
+
}
|
102
|
+
end
|
103
|
+
exception :remove, :passive_voice_imperative_mood_present_tense
|
104
|
+
cluster_on /active_voice.*third/, "as method", :active_thirds
|
105
|
+
end
|
106
|
+
end
|
107
|
+
tc = Class.new
|
108
|
+
tc.extend t.method_extension_module
|
109
|
+
assert_respond_to(tc, :active_thirds)
|
110
|
+
assert_equal(22, tc.active_thirds.length)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_extension
|
114
|
+
tc = Class.new
|
115
|
+
tc.extend @vv.method_extension_module
|
116
|
+
assert_respond_to(tc, :latin_active_voice_indicative_mood_imperfect_tense_singular_number_third_person)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_clustering
|
120
|
+
assert_respond_to(@vv, :vectors_that)
|
121
|
+
assert_respond_to(@vv, :cluster_on)
|
122
|
+
|
123
|
+
tc = Class.new
|
124
|
+
tc.extend @vv.method_extension_module
|
125
|
+
|
126
|
+
# Make sure that each cluster method is /not/ defined. We want these to
|
127
|
+
# be defined "for real," not as a proxy.
|
128
|
+
|
129
|
+
cms=@vv.cluster_methods[:tense_list].call
|
130
|
+
cms.each do |m|
|
131
|
+
assert_true not(tc.respond_to? m.to_sym)
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
data/verbvector.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "verbvector/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "verbvector"
|
7
|
+
s.version = Linguistics::Verbs::Verbvector::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Steven G. Harms"]
|
10
|
+
s.email = ["github@sgharms.oib.com"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/verbvector"
|
12
|
+
s.summary = %q{Generates the verb tense "vectors" based on a DSL description syntax.}
|
13
|
+
s.description = %q{Use a DSL to describe the verb tense framework of a language and have it generate methods corresponding to each unique vector.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "verbvector"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: verbvector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Steven G. Harms
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-06-02 00:00:00 Z
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Use a DSL to describe the verb tense framework of a language and have it generate methods corresponding to each unique vector.
|
17
|
+
email:
|
18
|
+
- github@sgharms.oib.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- Gemfile
|
28
|
+
- README.textile
|
29
|
+
- Rakefile
|
30
|
+
- lib/verbvector.rb
|
31
|
+
- lib/verbvector/version.rb
|
32
|
+
- test/fixtures/tense_list
|
33
|
+
- test/test_generator.rb
|
34
|
+
- verbvector.gemspec
|
35
|
+
homepage: http://rubygems.org/gems/verbvector
|
36
|
+
licenses: []
|
37
|
+
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project: verbvector
|
58
|
+
rubygems_version: 1.8.5
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: Generates the verb tense "vectors" based on a DSL description syntax.
|
62
|
+
test_files:
|
63
|
+
- test/fixtures/tense_list
|
64
|
+
- test/test_generator.rb
|