jinx 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.yardopts +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +27 -0
- data/History.md +6 -0
- data/LEGAL +5 -0
- data/LICENSE +22 -0
- data/README.md +44 -0
- data/Rakefile +41 -0
- data/examples/family/README.md +10 -0
- data/examples/family/ext/build.xml +35 -0
- data/examples/family/ext/src/family/Address.java +68 -0
- data/examples/family/ext/src/family/Child.java +24 -0
- data/examples/family/ext/src/family/DomainObject.java +26 -0
- data/examples/family/ext/src/family/Household.java +36 -0
- data/examples/family/ext/src/family/Parent.java +48 -0
- data/examples/family/ext/src/family/Person.java +42 -0
- data/examples/family/lib/family.rb +15 -0
- data/examples/family/lib/family/address.rb +6 -0
- data/examples/family/lib/family/domain_object.rb +6 -0
- data/examples/family/lib/family/household.rb +6 -0
- data/examples/family/lib/family/parent.rb +16 -0
- data/examples/family/lib/family/person.rb +6 -0
- data/examples/model/README.md +25 -0
- data/examples/model/ext/build.xml +35 -0
- data/examples/model/ext/src/domain/Child.java +192 -0
- data/examples/model/ext/src/domain/Dependent.java +29 -0
- data/examples/model/ext/src/domain/DomainObject.java +26 -0
- data/examples/model/ext/src/domain/Independent.java +83 -0
- data/examples/model/ext/src/domain/Parent.java +129 -0
- data/examples/model/ext/src/domain/Person.java +14 -0
- data/examples/model/lib/model.rb +13 -0
- data/examples/model/lib/model/child.rb +13 -0
- data/examples/model/lib/model/domain_object.rb +6 -0
- data/examples/model/lib/model/independent.rb +11 -0
- data/examples/model/lib/model/parent.rb +17 -0
- data/jinx.gemspec +22 -0
- data/lib/jinx.rb +3 -0
- data/lib/jinx/active_support/README.txt +2 -0
- data/lib/jinx/active_support/core_ext/string.rb +7 -0
- data/lib/jinx/active_support/core_ext/string/inflections.rb +167 -0
- data/lib/jinx/active_support/inflections.rb +55 -0
- data/lib/jinx/active_support/inflector.rb +398 -0
- data/lib/jinx/cli/application.rb +36 -0
- data/lib/jinx/cli/command.rb +214 -0
- data/lib/jinx/helpers/array.rb +108 -0
- data/lib/jinx/helpers/boolean.rb +42 -0
- data/lib/jinx/helpers/case_insensitive_hash.rb +39 -0
- data/lib/jinx/helpers/class.rb +149 -0
- data/lib/jinx/helpers/collection.rb +33 -0
- data/lib/jinx/helpers/collections.rb +11 -0
- data/lib/jinx/helpers/collector.rb +20 -0
- data/lib/jinx/helpers/conditional_enumerator.rb +21 -0
- data/lib/jinx/helpers/enumerable.rb +242 -0
- data/lib/jinx/helpers/enumerate.rb +35 -0
- data/lib/jinx/helpers/error.rb +15 -0
- data/lib/jinx/helpers/file_separator.rb +65 -0
- data/lib/jinx/helpers/filter.rb +52 -0
- data/lib/jinx/helpers/flattener.rb +38 -0
- data/lib/jinx/helpers/hash.rb +12 -0
- data/lib/jinx/helpers/hashable.rb +502 -0
- data/lib/jinx/helpers/inflector.rb +36 -0
- data/lib/jinx/helpers/key_transformer_hash.rb +43 -0
- data/lib/jinx/helpers/lazy_hash.rb +44 -0
- data/lib/jinx/helpers/log.rb +106 -0
- data/lib/jinx/helpers/math.rb +12 -0
- data/lib/jinx/helpers/merge.rb +60 -0
- data/lib/jinx/helpers/module.rb +18 -0
- data/lib/jinx/helpers/multi_enumerator.rb +31 -0
- data/lib/jinx/helpers/options.rb +92 -0
- data/lib/jinx/helpers/os.rb +19 -0
- data/lib/jinx/helpers/partial_order.rb +37 -0
- data/lib/jinx/helpers/pretty_print.rb +207 -0
- data/lib/jinx/helpers/set.rb +8 -0
- data/lib/jinx/helpers/stopwatch.rb +76 -0
- data/lib/jinx/helpers/transformer.rb +24 -0
- data/lib/jinx/helpers/transitive_closure.rb +55 -0
- data/lib/jinx/helpers/uniquifier.rb +50 -0
- data/lib/jinx/helpers/validation.rb +33 -0
- data/lib/jinx/helpers/visitor.rb +370 -0
- data/lib/jinx/import/class_path_modifier.rb +77 -0
- data/lib/jinx/import/java.rb +337 -0
- data/lib/jinx/importer.rb +240 -0
- data/lib/jinx/metadata.rb +155 -0
- data/lib/jinx/metadata/attribute_enumerator.rb +73 -0
- data/lib/jinx/metadata/dependency.rb +244 -0
- data/lib/jinx/metadata/id_alias.rb +23 -0
- data/lib/jinx/metadata/introspector.rb +179 -0
- data/lib/jinx/metadata/inverse.rb +170 -0
- data/lib/jinx/metadata/java_property.rb +169 -0
- data/lib/jinx/metadata/propertied.rb +500 -0
- data/lib/jinx/metadata/property.rb +401 -0
- data/lib/jinx/metadata/property_characteristics.rb +114 -0
- data/lib/jinx/resource.rb +862 -0
- data/lib/jinx/resource/copy_visitor.rb +36 -0
- data/lib/jinx/resource/inversible.rb +90 -0
- data/lib/jinx/resource/match_visitor.rb +180 -0
- data/lib/jinx/resource/matcher.rb +20 -0
- data/lib/jinx/resource/merge_visitor.rb +73 -0
- data/lib/jinx/resource/mergeable.rb +185 -0
- data/lib/jinx/resource/reference_enumerator.rb +49 -0
- data/lib/jinx/resource/reference_path_visitor.rb +38 -0
- data/lib/jinx/resource/reference_visitor.rb +55 -0
- data/lib/jinx/resource/unique.rb +35 -0
- data/lib/jinx/version.rb +3 -0
- data/spec/defaults_spec.rb +30 -0
- data/spec/definitions/model/alias/child.rb +5 -0
- data/spec/definitions/model/base/child.rb +5 -0
- data/spec/definitions/model/base/domain_object.rb +5 -0
- data/spec/definitions/model/base/independent.rb +5 -0
- data/spec/definitions/model/defaults/child.rb +5 -0
- data/spec/definitions/model/dependency/child.rb +5 -0
- data/spec/definitions/model/dependency/parent.rb +6 -0
- data/spec/definitions/model/inverse/child.rb +5 -0
- data/spec/definitions/model/inverse/independent.rb +5 -0
- data/spec/definitions/model/inverse/parent.rb +5 -0
- data/spec/definitions/model/mandatory/child.rb +6 -0
- data/spec/dependency_spec.rb +47 -0
- data/spec/family_spec.rb +64 -0
- data/spec/inverse_spec.rb +53 -0
- data/spec/mandatory_spec.rb +43 -0
- data/spec/metadata_spec.rb +68 -0
- data/spec/resource_spec.rb +30 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/model.rb +19 -0
- data/test/fixtures/line_separator/cr_line_sep.txt +1 -0
- data/test/fixtures/line_separator/crlf_line_sep.txt +3 -0
- data/test/fixtures/line_separator/lf_line_sep.txt +3 -0
- data/test/fixtures/mixed/ext/build.xml +35 -0
- data/test/fixtures/mixed/ext/src/mixed/Case/Example.java +5 -0
- data/test/helper.rb +7 -0
- data/test/lib/jinx/command_test.rb +41 -0
- data/test/lib/jinx/helpers/boolean_test.rb +27 -0
- data/test/lib/jinx/helpers/class_test.rb +60 -0
- data/test/lib/jinx/helpers/collections_test.rb +402 -0
- data/test/lib/jinx/helpers/file_separator_test.rb +29 -0
- data/test/lib/jinx/helpers/inflector_test.rb +11 -0
- data/test/lib/jinx/helpers/lazy_hash_test.rb +32 -0
- data/test/lib/jinx/helpers/module_test.rb +24 -0
- data/test/lib/jinx/helpers/options_test.rb +66 -0
- data/test/lib/jinx/helpers/partial_order_test.rb +41 -0
- data/test/lib/jinx/helpers/pretty_print_test.rb +83 -0
- data/test/lib/jinx/helpers/stopwatch_test.rb +16 -0
- data/test/lib/jinx/helpers/transitive_closure_test.rb +80 -0
- data/test/lib/jinx/helpers/visitor_test.rb +288 -0
- data/test/lib/jinx/import/java_test.rb +78 -0
- data/test/lib/jinx/import/mixed_case_test.rb +16 -0
- metadata +272 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module Jinx
|
4
|
+
# Operating system methods.
|
5
|
+
module OS
|
6
|
+
include Config
|
7
|
+
|
8
|
+
# @return [System] the operating system type +:windows+, +:linux+, +:mac+, +:solaris+, or +:other+
|
9
|
+
def self.os_type
|
10
|
+
case CONFIG['host_os']
|
11
|
+
when /mswin|windows/i then :windows
|
12
|
+
when /linux/i then :linux
|
13
|
+
when /darwin/i then :mac
|
14
|
+
when /sunos|solaris/i then :solaris
|
15
|
+
else :other
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Jinx
|
2
|
+
# A PartialOrder is a Comparable which restricted scope. Classes which include PartialOrder
|
3
|
+
# are required to implement the <=> operator with the following semantics:
|
4
|
+
# * _a_ <=> _b_ returns -1, 0, or 1 if a and b are comparable, nil otherwise
|
5
|
+
# A PartialOrder thus relaxes comparison symmetry, e.g.
|
6
|
+
# a < b
|
7
|
+
# does not imply
|
8
|
+
# b >= a.
|
9
|
+
# Example:
|
10
|
+
# module Queued
|
11
|
+
# attr_reader :queue
|
12
|
+
# def <=>(other)
|
13
|
+
# queue.index(self) <=> queue.index(other) if queue.equal?(other.queue)
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
# q1 = [a, b] # a, b are Queued
|
17
|
+
# q2 = [c] # c is a Queued
|
18
|
+
# a < b #=> true
|
19
|
+
# b < c #=> nil
|
20
|
+
module PartialOrder
|
21
|
+
include Comparable
|
22
|
+
|
23
|
+
Comparable.instance_methods(false).each do |m|
|
24
|
+
define_method(m.to_sym) do |other|
|
25
|
+
self <=> other ? super : nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] true if other is an instance of this object's class and other == self,
|
30
|
+
# false otherwise
|
31
|
+
def eql?(other)
|
32
|
+
self.class === other and super
|
33
|
+
end
|
34
|
+
|
35
|
+
alias :== :eql?
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'date'
|
3
|
+
require 'pp'
|
4
|
+
require 'stringio'
|
5
|
+
require 'jinx/helpers/options'
|
6
|
+
require 'jinx/helpers/collections'
|
7
|
+
|
8
|
+
require 'jinx/helpers/inflector'
|
9
|
+
|
10
|
+
class PrettyPrint
|
11
|
+
# The standard +prettyprint+ gem SingleLine is adjusted to add an output accessor and an optional output argument to {#initialize}.
|
12
|
+
class SingleLine
|
13
|
+
# @return [String] the print target
|
14
|
+
attr_reader :output
|
15
|
+
|
16
|
+
alias :base__initialize :initialize
|
17
|
+
private :base__initialize
|
18
|
+
|
19
|
+
# Overrides the standard SingleLine initializer to supply an output parameter default.
|
20
|
+
def initialize(output='', maxwidth=nil, newline=nil)
|
21
|
+
base__initialize(output, maxwidth, newline)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# A PrintWrapper prints arguments by calling a printer proc.
|
27
|
+
class PrintWrapper < Proc
|
28
|
+
# Creates a new PrintWrapper on the given arguments.
|
29
|
+
def initialize(*args)
|
30
|
+
super()
|
31
|
+
@args = args
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param args this wrapper's print block parameters
|
35
|
+
# @return [PrintWrapper] self
|
36
|
+
def wrap(*args)
|
37
|
+
@args = args
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Calls this PrintWrapper's print procedure on the arguments set in the initializer.
|
42
|
+
def to_s
|
43
|
+
@args.empty? ? 'nil' : call(*@args)
|
44
|
+
end
|
45
|
+
|
46
|
+
alias :inspect :to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
class Object
|
50
|
+
# @return [String] this object's class demodulized name and object id
|
51
|
+
def print_class_and_id
|
52
|
+
"#{self.class.qp}@#{object_id}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# qp, an abbreviation for quick-print, calls {#print_class_and_id} in this base implementation.
|
56
|
+
alias :qp :print_class_and_id
|
57
|
+
|
58
|
+
# Formats this object with the standard {PrettyPrint}.
|
59
|
+
#
|
60
|
+
# @param [Hash, Symbol, nil] opts the print options
|
61
|
+
# @option opts [Boolean] :single_line print the output on a single line
|
62
|
+
# @return [String] the formatted print result
|
63
|
+
def pp_s(opts=nil)
|
64
|
+
s = StringIO.new
|
65
|
+
if Options.get(:single_line, opts) then
|
66
|
+
PP.singleline_pp(self, s)
|
67
|
+
else
|
68
|
+
PP.pp(self, s)
|
69
|
+
end
|
70
|
+
s.rewind
|
71
|
+
s.read.chomp
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Numeric
|
76
|
+
# Alias #{Object#qp} to {#to_s} in this primitive class.
|
77
|
+
alias :qp :to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
class String
|
81
|
+
# Alias #{Object#qp} to {#to_s} in this primitive class.
|
82
|
+
alias :qp :to_s
|
83
|
+
end
|
84
|
+
|
85
|
+
class TrueClass
|
86
|
+
# Alias #{Object#qp} to {#to_s} in this primitive class.
|
87
|
+
alias :qp :to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
class FalseClass
|
91
|
+
# Alias #{Object#qp} to {#to_s} in this primitive class.
|
92
|
+
alias :qp :to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
class NilClass
|
96
|
+
# Alias #{Object#qp} to {#to_s} in this primitive class.
|
97
|
+
alias :qp :inspect
|
98
|
+
end
|
99
|
+
|
100
|
+
class Symbol
|
101
|
+
# Alias #{Object#qp} to {#to_s} in this primitive class.
|
102
|
+
alias :qp :inspect
|
103
|
+
end
|
104
|
+
|
105
|
+
class Module
|
106
|
+
# @return [String ] the demodulized name
|
107
|
+
def qp
|
108
|
+
name[/\w+$/]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module Enumerable
|
113
|
+
# Prints this Enumerable with a filter that calls qp on each item.
|
114
|
+
# Non-collection Enumerable classes override this method to delegate to {Object#qp}.
|
115
|
+
#
|
116
|
+
# Unlike {Object#qp}, this implementation accepts the {Object#pp_s} options.
|
117
|
+
# The options are used to format this Enumerable, but are not propagated to the
|
118
|
+
# enumerated items.
|
119
|
+
#
|
120
|
+
# @param (see Object#pp_s)
|
121
|
+
# @return [String] the formatted result
|
122
|
+
def qp(opts=nil)
|
123
|
+
wrap { |item| item.qp }.pp_s(opts)
|
124
|
+
end
|
125
|
+
|
126
|
+
# If a transformer block is given to this method, then the block is applied to each
|
127
|
+
# enumerated item before pretty-printing the result.
|
128
|
+
#
|
129
|
+
# @param (see Object#pp_s)
|
130
|
+
# @yield [item] transforms the item to print
|
131
|
+
# @yieldparam item the item to print
|
132
|
+
# @return (see Oblect#pp_s)
|
133
|
+
def pp_s(opts=nil)
|
134
|
+
# delegate to Object if no block
|
135
|
+
return super unless block_given?
|
136
|
+
# make a print wrapper
|
137
|
+
wrapper = PrintWrapper.new { |item| yield item }
|
138
|
+
# print using the wrapper on each item
|
139
|
+
wrap { |item| wrapper.wrap(item) }.pp_s(opts)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Pretty-prints the content within brackets, as is done by the Array pretty printer.
|
143
|
+
def pretty_print(q)
|
144
|
+
q.group(1, '[', ']') {
|
145
|
+
q.seplist(self) { |v|
|
146
|
+
q.pp v
|
147
|
+
}
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
# Pretty-prints the cycle within brackets, as is done by the Array pretty printer.
|
152
|
+
def pretty_print_cycle(q)
|
153
|
+
q.text(empty? ? '[]' : '[...]')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
module Jinx
|
158
|
+
module Hashable
|
159
|
+
# qp, short for quick-print, prints this Hashable with a filter that calls qp on each key and value.
|
160
|
+
#
|
161
|
+
# @return [String] the quick-print result
|
162
|
+
def qp
|
163
|
+
qph = {}
|
164
|
+
each { |k, v| qph[k.qp] = v.qp }
|
165
|
+
qph.pp_s
|
166
|
+
end
|
167
|
+
|
168
|
+
def pretty_print(q)
|
169
|
+
Hash === self ? q.pp_hash(self) : q.pp_hash(to_hash)
|
170
|
+
end
|
171
|
+
|
172
|
+
def pretty_print_cycle(q)
|
173
|
+
q.text(empty? ? '{}' : '{...}')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class String
|
179
|
+
# Pretty-prints this String using the Object pretty_print rather than Enumerable pretty_print.
|
180
|
+
def pretty_print(q)
|
181
|
+
q.text self
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class DateTime
|
186
|
+
# @return [String] the formatted +strftime+
|
187
|
+
def pretty_print(q)
|
188
|
+
q.text(strftime)
|
189
|
+
end
|
190
|
+
|
191
|
+
# qp, an abbreviation for quick-print, is an alias for {#to_s} in this primitive class.
|
192
|
+
alias :qp :to_s
|
193
|
+
end
|
194
|
+
|
195
|
+
class Set
|
196
|
+
# Formats this set using {Enumerable#pretty_print}.
|
197
|
+
def pretty_print(q)
|
198
|
+
# mark this object as visited; this fragment is inferred from pp.rb and is necessary to detect a cycle
|
199
|
+
Thread.current[:__inspect_key__] << __id__
|
200
|
+
to_a.pretty_print(q)
|
201
|
+
end
|
202
|
+
|
203
|
+
# The pp.rb default pretty printing method for general objects that are detected as part of a cycle.
|
204
|
+
def pretty_print_cycle(q)
|
205
|
+
to_a.pretty_print_cycle(q)
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
class Set
|
4
|
+
# The standard Set {#merge} is an anomaly among Ruby collections, since merge modifies the called Set in-place rather
|
5
|
+
# than return a new Set containing the merged contents. Preserve this unfortunate behavior, but partially address
|
6
|
+
# the anomaly by adding the merge! alias to make it clear that this is an in-place merge.
|
7
|
+
alias :merge! :merge
|
8
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
module Jinx
|
4
|
+
# Stopwatch is a simple execution time accumulator.
|
5
|
+
class Stopwatch
|
6
|
+
# Time accumulates elapsed real time and total CPU time.
|
7
|
+
class Time
|
8
|
+
# @return [Benchmark::Tms] the Tms wrapped by this Time
|
9
|
+
attr_reader :tms
|
10
|
+
|
11
|
+
# @param [Benchmark::Tms, nil] the starting time (default is now)
|
12
|
+
def initialize(tms=nil)
|
13
|
+
@tms = tms || Benchmark::Tms.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Numeric] the cumulative elapsed real clock time
|
17
|
+
def elapsed
|
18
|
+
@tms.real
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Numeric] the cumulative CPU total time
|
22
|
+
def cpu
|
23
|
+
@tms.total
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds the time to execute the given block to this time.
|
27
|
+
#
|
28
|
+
# @return [Numeric] the split execution Time
|
29
|
+
def split(&block)
|
30
|
+
stms = Benchmark.measure(&block)
|
31
|
+
@tms += stms
|
32
|
+
Time.new(stms)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets this benchmark timer to zero.
|
36
|
+
def reset
|
37
|
+
@tms = Benchmark::Tms.new
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Executes the given block
|
42
|
+
#
|
43
|
+
# @return [Numeric] the execution Time
|
44
|
+
def self.measure(&block)
|
45
|
+
new.run(&block)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Creates a new idle Stopwatch.
|
49
|
+
def initialize
|
50
|
+
@time = Time.new
|
51
|
+
end
|
52
|
+
|
53
|
+
# Executes the given block. Accumulates the execution time in this Stopwatch.
|
54
|
+
#
|
55
|
+
# @return [Numeric] the execution run Time
|
56
|
+
def run(&block)
|
57
|
+
@time.split(&block)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Numeric] the cumulative elapsed real clock time spent in {#run} executions
|
61
|
+
def elapsed
|
62
|
+
@time.elapsed
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Numeric] the cumulative CPU total time spent in {#run} executions for the
|
66
|
+
# current process and its children
|
67
|
+
def cpu
|
68
|
+
@time.cpu
|
69
|
+
end
|
70
|
+
|
71
|
+
# Resets this Stopwatch's cumulative time to zero.
|
72
|
+
def reset
|
73
|
+
@time.reset
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Jinx
|
2
|
+
# This Transformer helper class applies a transformer block to a base enumeration.
|
3
|
+
class Transformer
|
4
|
+
include Collection
|
5
|
+
|
6
|
+
def initialize(enum=[], &transformer)
|
7
|
+
@base = enum
|
8
|
+
@xfm = transformer
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sets the base Enumerable on which this Transformer operates and returns this transformer, e.g.:
|
12
|
+
# transformer = Transformer.new { |n| n * 2 }
|
13
|
+
# transformer.on([1, 2, 3]).to_a #=> [2, 4, 6]
|
14
|
+
def on(enum)
|
15
|
+
@base = enum
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
# Calls the block on each item after this Transformer's transformer block is applied.
|
20
|
+
def each
|
21
|
+
@base.each { |item| yield(item.nil? ? nil : @xfm.call(item)) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'jinx/helpers/visitor'
|
2
|
+
|
3
|
+
class Object
|
4
|
+
# Returns the transitive closure over a method or block. This method returns an array partially ordered
|
5
|
+
# by the children method or block, i.e. each node occurs before all other nodes referenced directly or
|
6
|
+
# indirectly by the children.
|
7
|
+
#
|
8
|
+
# If a method symbol or name is provided, then that method is called. Otherwise, the block is called.
|
9
|
+
# In either case, the call is expected to return an object or Enumerable of objects which also respond
|
10
|
+
# to the method or block.
|
11
|
+
#
|
12
|
+
# @param [Symbol, nil] method the child reference, or nil if a block is given
|
13
|
+
# @yield [node] the parent node's children
|
14
|
+
# @yieldparam node the parent node
|
15
|
+
# @example
|
16
|
+
# class Node
|
17
|
+
# attr_reader :parent, :children
|
18
|
+
# def initialize(name, parent=nil)
|
19
|
+
# super()
|
20
|
+
# @name = name
|
21
|
+
# @parent = parent
|
22
|
+
# @children = []
|
23
|
+
# parent.children << self if parent
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def to_s;
|
27
|
+
# end
|
28
|
+
# a = Node.new('a'); b = Node.new('b', a), c = Node.new('c', a); d = Node.new('d', c)
|
29
|
+
# a.transitive_closure { |node| node.children }.to_a.join(", ") #=> a, b, c, d
|
30
|
+
# a.transitive_closure(:children).to_a.join(", ") #=> a, b, c, d
|
31
|
+
def transitive_closure(method=nil)
|
32
|
+
Jinx.fail(ArgumentError, "Missing both a method argument and a block") if method.nil? and not block_given?
|
33
|
+
# If there is a method argument, then the transitive closure is based on that method.
|
34
|
+
# Otherwise, visit the closure in reverse depth-first order.
|
35
|
+
if method then
|
36
|
+
transitive_closure() { |node| node.send(method) }
|
37
|
+
else
|
38
|
+
Jinx::Visitor.new(:depth_first) { |node| yield node }.to_enum(self).to_a.reverse
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module Enumerable
|
44
|
+
# Returns the transitive closure over all items in this Enumerable.
|
45
|
+
#
|
46
|
+
# @see Object#transitive_closure
|
47
|
+
def transitive_closure(method=nil)
|
48
|
+
# delegate to Object if there is a method argument
|
49
|
+
return super(method) if method
|
50
|
+
# this Enumerable's children are this Enumerable's contents
|
51
|
+
closure = super() { |node| node.equal?(self) ? self : yield(node) }
|
52
|
+
# remove this collection from the closure
|
53
|
+
closure[1..-1]
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'jinx/helpers/lazy_hash'
|
3
|
+
|
4
|
+
module Jinx
|
5
|
+
# A utility class to generate value qualifiers.
|
6
|
+
class Uniquifier
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
# Returns a relatively unique integral qualifier. Successive calls to this method
|
10
|
+
# within the same time zone spaced more than a millisecond apart return different
|
11
|
+
# integers. Each generated qualifier is greater than the previous by an unspecified
|
12
|
+
# amount.
|
13
|
+
def self.qualifier
|
14
|
+
# the first date that this method could be called
|
15
|
+
@first ||= Date.new(2011, 12, 01)
|
16
|
+
# days as integer + milliseconds as fraction since the first date
|
17
|
+
diff = DateTime.now - @first
|
18
|
+
# shift a tenth of a milli up into the integer portion
|
19
|
+
decimillis = diff * 24 * 60 * 60 * 10000
|
20
|
+
# truncate the fraction
|
21
|
+
decimillis.truncate
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@cache = Jinx::LazyHash.new { Hash.new }
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param obj the object containing the value
|
29
|
+
# @param value the value to make unique
|
30
|
+
# @return the new unique value, or nil if the given value is nil
|
31
|
+
def uniquify(obj, value)
|
32
|
+
@cache[obj.class][value] ||= value.uniquify if value
|
33
|
+
end
|
34
|
+
|
35
|
+
def clear
|
36
|
+
@cache.clear
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class String
|
42
|
+
# Returns a relatively unique value obtained from the specified base value.
|
43
|
+
# The suffix is generated by {Jinx::Uniquifier.qualifier}. Spaces are removed.
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# 'Test Name'.uniquify #=> Test_Name_330938800614
|
47
|
+
def uniquify
|
48
|
+
gsub(' ', '_') + "_#{Jinx::Uniquifier.qualifier}"
|
49
|
+
end
|
50
|
+
end
|