davidrichards-tegu_gears 0.0.3
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/VERSION.yml +4 -0
- data/lib/examples/factorial.rb +6 -0
- data/lib/examples/fibonacci.rb +7 -0
- data/lib/examples/regression.rb +59 -0
- data/lib/tegu_gears.rb +52 -0
- data/lib/tegu_gears/compose.rb +25 -0
- data/lib/tegu_gears/dynamics.rb +3 -0
- data/lib/tegu_gears/dynamics/node.rb +6 -0
- data/lib/tegu_gears/dynamics/noodle.rb +62 -0
- data/lib/tegu_gears/dynamics/operator.rb +2 -0
- data/lib/tegu_gears/memo_repository.rb +63 -0
- data/lib/tegu_gears/memoize.rb +51 -0
- data/spec/lib/examples/factorial_spec.rb +8 -0
- data/spec/lib/examples/fibonacci_spec.rb +9 -0
- data/spec/lib/tegu_gears/compose_spec.rb +64 -0
- data/spec/lib/tegu_gears/memo_repository_spec.rb +83 -0
- data/spec/lib/tegu_gears/memoize_spec.rb +81 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/tegu_gears_spec.rb +17 -0
- metadata +78 -0
data/VERSION.yml
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
class SSY
|
2
|
+
include TeguGears
|
3
|
+
def process(*y)
|
4
|
+
y_mean = Mean.call(*y)
|
5
|
+
y.inject{|s,e| s + (e - y_mean)**2}
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class SSX < SSY; end
|
10
|
+
|
11
|
+
class SSXY
|
12
|
+
include TeguGears
|
13
|
+
def process(x, y)
|
14
|
+
raise ArgumentError, "x and y need to be the same size" unless x.size == y.size
|
15
|
+
x_mean = Mean.call(*x)
|
16
|
+
y_mean = Mean.call(*y)
|
17
|
+
(0...x.size).inject(0.0) {|s,i| s + (y[i] - y_mean) * (x[i] - x_mean) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Mean
|
22
|
+
include TeguGears
|
23
|
+
def process(*x)
|
24
|
+
x.inject{|s,e| s + e} / x.size.to_f
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class MaximumLikelihoodSlope
|
29
|
+
include TeguGears
|
30
|
+
def process(x, y)
|
31
|
+
raise ArgumentError, "x and y need to be the same size" unless x.size == y.size
|
32
|
+
b = SSXY.call(x, y) / SSX.call(x)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class MaximumLikelihoodIntercept
|
37
|
+
include TeguGears
|
38
|
+
def process(x,y)
|
39
|
+
raise ArgumentError, "x and y need to be the same size" unless x.size == y.size
|
40
|
+
Mean.call(*y) - MaximumLikelihoodSlope.call(x,y) * Mean.call(*x)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class SSE
|
45
|
+
include TeguGears
|
46
|
+
def process(x,y)
|
47
|
+
raise ArgumentError, "x and y need to be the same size" unless x.size == y.size
|
48
|
+
(0...x.size).inject(0.0) do |s, i|
|
49
|
+
s + (y[i] - MaximumLikelihoodIntercept.call(x,y) - MaximumLikelihoodSlope.call(x,y) * x[i] ) ** 2
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class RSquared
|
55
|
+
include TeguGears
|
56
|
+
def process(x,y)
|
57
|
+
(SSY.call(*y) - SSE.call(x,y)) / SSY.call(*y)
|
58
|
+
end
|
59
|
+
end
|
data/lib/tegu_gears.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'activesupport'
|
3
|
+
require 'activesupport'
|
4
|
+
|
5
|
+
# Add to these if you want to add class and instance methods.
|
6
|
+
module TeguGears
|
7
|
+
module InstanceMethods; end
|
8
|
+
module ClassMethods; end
|
9
|
+
end
|
10
|
+
|
11
|
+
$:.unshift(File.dirname(__FILE__))
|
12
|
+
require 'tegu_gears/memoize'
|
13
|
+
Dir.glob("#{File.dirname(__FILE__)}/tegu_gears/*.rb").each { |file| require file }
|
14
|
+
module TeguGears
|
15
|
+
def self.included(base)
|
16
|
+
base.send(:include, Memoize)
|
17
|
+
base.send(:include, InstanceMethods)
|
18
|
+
base.send(:extend, ClassMethods)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Now that I have some tools setup, require some libraries that will be
|
23
|
+
# generally useful. This will be migrated to a configurable load process
|
24
|
+
# at some point soon.
|
25
|
+
|
26
|
+
# Requires libraries, only if they're available.
|
27
|
+
def safe_load(val=nil, &block)
|
28
|
+
begin
|
29
|
+
if block
|
30
|
+
block.call
|
31
|
+
else
|
32
|
+
require val
|
33
|
+
end
|
34
|
+
# Very important that this is Exception, and not StandardError
|
35
|
+
rescue Exception => e
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
safe_load 'rubygems'
|
41
|
+
safe_load 'mathn'
|
42
|
+
safe_load {require bigdecimal; require 'bigdecimal/math'}
|
43
|
+
safe_load 'set'
|
44
|
+
safe_load 'matrix'
|
45
|
+
safe_load 'narray'
|
46
|
+
safe_load 'rnum'
|
47
|
+
safe_load 'gratr'
|
48
|
+
safe_load 'tenacious_g'
|
49
|
+
safe_load 'rbtree'
|
50
|
+
|
51
|
+
# Require the examples too, they are meant to be generally interesting or useful.
|
52
|
+
Dir.glob("#{File.dirname(__FILE__)}/examples/*.rb").each { |file| require file }
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TeguGears #:nodoc:
|
2
|
+
|
3
|
+
module InstanceMethods
|
4
|
+
|
5
|
+
# This allows me to compose two classes using TeguGears.
|
6
|
+
def compose(f)
|
7
|
+
lambda {|*args| f.call(self.call(*args))}
|
8
|
+
end
|
9
|
+
alias :| :compose
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Object
|
14
|
+
# Composes any number of functions. The first argument is the input.
|
15
|
+
# Use Struct if the argument list needs to be more expansive.
|
16
|
+
def compose(*args)
|
17
|
+
param = args.shift
|
18
|
+
args.inject {|composition, f| make_binomial_composition(composition, f)}.call(param)
|
19
|
+
end
|
20
|
+
|
21
|
+
def make_binomial_composition(f, g)
|
22
|
+
lambda{|*args| g.call(f.call(*args))}
|
23
|
+
end
|
24
|
+
private :make_binomial_composition
|
25
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# I'm using explicit nodes here to carry the meaning of the dynamics. I
|
2
|
+
# have done it another way, where I patch in some methods to anything
|
3
|
+
# that moves. That's a really obtrusive way to build a system, it turns
|
4
|
+
# out. So, I'll mix things in explicitly and see how that goes.
|
5
|
+
module DynamicsNode
|
6
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'gratr'
|
3
|
+
require 'modelling'
|
4
|
+
|
5
|
+
class Value
|
6
|
+
include Modelling
|
7
|
+
attributes :value => Proc.new {0.0}
|
8
|
+
end
|
9
|
+
|
10
|
+
class Value
|
11
|
+
attr_accessor :value
|
12
|
+
def initialize(value)
|
13
|
+
@value = value.to_f
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Beta < Value; end
|
18
|
+
class Vote < Value; end
|
19
|
+
class Income < Value; end
|
20
|
+
class Education < Value; end
|
21
|
+
class PartisanIdentification < Value; end
|
22
|
+
class NeighborhoodContext < Value; end
|
23
|
+
|
24
|
+
class Operator
|
25
|
+
include TeguGears
|
26
|
+
|
27
|
+
# Auto-collects the in-nodes, multiplying what should be multiplied
|
28
|
+
def process
|
29
|
+
|
30
|
+
args.inject { |sum, e| sum + e }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class System
|
36
|
+
class << self
|
37
|
+
def instance
|
38
|
+
@@instance ||= new
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :graph
|
43
|
+
def initialize
|
44
|
+
@graph = GRATR::Digraph.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_edge(a,b,w=nil)
|
48
|
+
self.graph.add_edge!(a,b,w)
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing(sym, *args, &block)
|
52
|
+
self.graph.send(sym, *args, &block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Object
|
57
|
+
def method_missing(sym, *args, &block)
|
58
|
+
System.instance.send(sym, *args, &block)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# swap(old, new)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module TeguGears #:nodoc:
|
2
|
+
|
3
|
+
# Should get more sophisticated with a configuration tool at some point.
|
4
|
+
REPOSITORY_CLASS = Hash
|
5
|
+
|
6
|
+
# This works like a pass through to other caching tools, or keeps things
|
7
|
+
# in a hash. Each class hosting TeguGear has its own entry in this
|
8
|
+
# repository, as does each composition. Other caching tools may be
|
9
|
+
# useful to monitor the cache size, distribute it, or reduce different
|
10
|
+
# kinds of caching. I use a key/value interface, because that should
|
11
|
+
# work well with Memcache, Tokyo Cabinet, Redis, Hash, CouchDB, and
|
12
|
+
# other tools.
|
13
|
+
#
|
14
|
+
# I refactored to a centrally-managed repository because distributed
|
15
|
+
# caching doesn't make sense for expensive computations. There's
|
16
|
+
# probably another abstraction available that will allow distributed
|
17
|
+
# caching only when configured, but I'll get to that when I write a
|
18
|
+
# configuration harness.
|
19
|
+
class MemoRepository
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def instance
|
23
|
+
@@instance ||= new(REPOSITORY_CLASS.new)
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(sym, *args, &block)
|
27
|
+
instance.send(sym, *args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :store
|
32
|
+
def initialize(store=Hash.new)
|
33
|
+
@store = store
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(sym, *args, &block)
|
37
|
+
self.store.send(sym, *args, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Expects a hash on the other end, for now.
|
41
|
+
def set(caller, key, value)
|
42
|
+
key = simplify_key(key)
|
43
|
+
self.store[caller] ||= REPOSITORY_CLASS.new
|
44
|
+
self.store[caller][key] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
# Because I opened things up to accept an array of params instead of a single param.
|
48
|
+
def simplify_key(key)
|
49
|
+
(key.is_a?(Array) and key.size == 1) ? key.first : key
|
50
|
+
end
|
51
|
+
protected :simplify_key
|
52
|
+
|
53
|
+
def for(caller, key=nil)
|
54
|
+
self.store[caller] ||= REPOSITORY_CLASS.new
|
55
|
+
key ? self.store[caller][key] : self.store[caller]
|
56
|
+
end
|
57
|
+
|
58
|
+
def flush_for(caller)
|
59
|
+
self.store[caller] = REPOSITORY_CLASS.new
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module TeguGears #:nodoc:
|
2
|
+
# If I can get used to using a struct or a single input variable, I can
|
3
|
+
# do things this way. Otherwise, I need to get deeper into the meta
|
4
|
+
# programming to work around how to call things in a signatureless
|
5
|
+
# environment. Not an easy nut to crack, btw.
|
6
|
+
module Memoize
|
7
|
+
module ClassMethods
|
8
|
+
def instance
|
9
|
+
@inst ||= new
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(sym, *args, &block)
|
13
|
+
instance.send(sym, *args, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
def call(*x)
|
19
|
+
function(*x)
|
20
|
+
end
|
21
|
+
|
22
|
+
def memoize
|
23
|
+
@memoize = true unless defined?(@memoize)
|
24
|
+
@memoize
|
25
|
+
end
|
26
|
+
|
27
|
+
def memoize=(val)
|
28
|
+
@memoize = val
|
29
|
+
end
|
30
|
+
|
31
|
+
def function(*x)
|
32
|
+
self.memoize ? memoized(*x) : process(*x)
|
33
|
+
end
|
34
|
+
|
35
|
+
def memoized(*x)
|
36
|
+
MemoRepository.for(self, x) || MemoRepository.set(self, x, process(*x))
|
37
|
+
end
|
38
|
+
|
39
|
+
def flush
|
40
|
+
MemoRepository.flush_for(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.included(base)
|
46
|
+
base.send(:extend, ClassMethods)
|
47
|
+
base.send(:include, InstanceMethods)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/../../spec_helper")
|
2
|
+
|
3
|
+
describe TeguGears, "compose" do
|
4
|
+
before(:all) do
|
5
|
+
class Square
|
6
|
+
include TeguGears
|
7
|
+
def process(n)
|
8
|
+
n ** 2
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class SquareRoot
|
13
|
+
include TeguGears
|
14
|
+
def process(n)
|
15
|
+
Math.sqrt(n)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should compose two functions manually" do
|
21
|
+
square_root = SquareRoot.instance
|
22
|
+
square = Square.instance
|
23
|
+
(square_root | square).call(2).should be_close(2.0, 0.0001)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to compose two functions from their host classes" do
|
27
|
+
(SquareRoot | Square).call(2).should be_close(2.0, 0.0001)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should read the composition left to right" do
|
31
|
+
plus_two = lambda{|x| x + 2}
|
32
|
+
times_two = lambda{|x| x * 2}
|
33
|
+
# IF this was right to left, it would be 10
|
34
|
+
(plus_two | times_two).call(4).should eql(12)
|
35
|
+
(times_two | plus_two).call(4).should eql(10)
|
36
|
+
end
|
37
|
+
|
38
|
+
context "from Object" do
|
39
|
+
it "should have a version available for any lambda or method" do
|
40
|
+
compose(2, SquareRoot, Square).should be_close(2.0, 0.0001)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be able to take any number of compositions at once" do
|
44
|
+
cube_root = lambda{|x| x ** (1/3.0)}
|
45
|
+
half = lambda{|x| x / 2.0}
|
46
|
+
twice = lambda{|x| x * 2.0}
|
47
|
+
quadruple = lambda{|x| x * 4.0}
|
48
|
+
compose(2, quadruple, twice, half, cube_root).should eql(2.0)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should read left to right" do
|
52
|
+
plus_two = lambda{|x| x + 2}
|
53
|
+
times_two = lambda{|x| x * 2}
|
54
|
+
# IF this was right to left, it would be 10
|
55
|
+
compose(4, plus_two, times_two).should eql(12)
|
56
|
+
compose(4, times_two, plus_two).should eql(10)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
after(:all) do
|
61
|
+
Object.send(:remove_const, :Square)
|
62
|
+
Object.send(:remove_const, :SquareRoot)
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/../../spec_helper")
|
2
|
+
|
3
|
+
describe MemoRepository do
|
4
|
+
it "should default to a hash" do
|
5
|
+
@mr = MemoRepository.new
|
6
|
+
@mr.store.should be_is_a(Hash)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should pass unknown calls to the store" do
|
10
|
+
@mr = MemoRepository.new
|
11
|
+
@mr.store.should_receive(:blah).and_return(true)
|
12
|
+
@mr.blah
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should be able to take a different kind of repository" do
|
16
|
+
@mr = MemoRepository.new([])
|
17
|
+
@mr.store.should be_is_a(Array)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should provide a quasi-singleton" do
|
21
|
+
MemoRepository.instance.should be_is_a(MemoRepository)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should set the REPOSITORY_CLASS to a Hash" do
|
25
|
+
REPOSITORY_CLASS.should eql(Hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should construct REPOSITORY_CLASS for the singleton" do
|
29
|
+
REPOSITORY_CLASS.should_receive(:new).and_return(Hash.new)
|
30
|
+
MemoRepository.instance
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should pass unknown class calls down to the instance" do
|
34
|
+
MemoRepository.instance.should_receive(:weird).and_return(true)
|
35
|
+
MemoRepository.weird
|
36
|
+
end
|
37
|
+
|
38
|
+
context "scoped access" do
|
39
|
+
before(:all) do
|
40
|
+
class A
|
41
|
+
include TeguGears
|
42
|
+
def process(n)
|
43
|
+
n + 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should offer a set interface, that passes a scoped memo into the repository" do
|
49
|
+
MemoRepository.set(A, 1, 2)
|
50
|
+
MemoRepository.store.keys.should be_include(A)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "for" do
|
54
|
+
it "should offer a default empty hash" do
|
55
|
+
MemoRepository.instance.for(:non_existent).should be_is_a(Hash)
|
56
|
+
MemoRepository.instance.for(:non_existent).should be_empty
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should offer whatever cache is available" do
|
60
|
+
MemoRepository.set(A, 1, 2)
|
61
|
+
@a_repo = MemoRepository.for(A)
|
62
|
+
@a_repo.keys.should eql([1])
|
63
|
+
@a_repo.values.should eql([2])
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should take an optional second parameter, which return the specific value that has been cached." do
|
67
|
+
MemoRepository.set(A, 1, 3)
|
68
|
+
MemoRepository.for(A, 1).should eql(3)
|
69
|
+
MemoRepository.flush_for(A)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should be able to flush a single scope" do
|
74
|
+
MemoRepository.for(A).should_not be_empty
|
75
|
+
MemoRepository.flush_for(A)
|
76
|
+
MemoRepository.for(A).should be_empty
|
77
|
+
end
|
78
|
+
|
79
|
+
after(:all) do
|
80
|
+
Object.send(:remove_const, :A)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/../../spec_helper")
|
2
|
+
|
3
|
+
describe Memoize do
|
4
|
+
before(:all) do
|
5
|
+
class A
|
6
|
+
include Memoize
|
7
|
+
|
8
|
+
def process(x)
|
9
|
+
x**2
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "class methods" do
|
15
|
+
it "should make instance available" do
|
16
|
+
A.should be_respond_to(:instance)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return an instance of the host (the class that includes Memoize)" do
|
20
|
+
A.instance.should be_is_a(A)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should pass unknown calls to host instance" do
|
24
|
+
@a = A.instance
|
25
|
+
@a.should_receive(:blah).and_return(true)
|
26
|
+
A.blah
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "instance methods" do
|
31
|
+
|
32
|
+
before(:each) do
|
33
|
+
@a = A.new
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should map call to function" do
|
37
|
+
@a.should_receive(:function).and_return(true)
|
38
|
+
@a.call(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should make memoize accessible" do
|
42
|
+
@a.memoize = false
|
43
|
+
@a.memoize.should eql(false)
|
44
|
+
@a.memoize = true
|
45
|
+
@a.memoize.should eql(true)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should default memoize to true" do
|
49
|
+
@a.memoize.should eql(true)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should expect process to define the class' core function. This is the only method that a host class needs to implement." do
|
53
|
+
@a.should_receive(:process).and_return('found me')
|
54
|
+
@a.call(:anything).should eql('found me')
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should not refer to the cache when memoize is set to false" do
|
58
|
+
@a.memoize = false
|
59
|
+
MemoRepository.should_not_receive(:for)
|
60
|
+
@a.call(2)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should be able to clear the cache" do
|
64
|
+
@a.call(2)
|
65
|
+
@a.flush
|
66
|
+
MemoRepository.for(@a).should be_empty
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should use the MemoRepository" do
|
70
|
+
MemoRepository.flush_for(A)
|
71
|
+
MemoRepository.for(@a)[2].should be_nil
|
72
|
+
@a.call(2)
|
73
|
+
MemoRepository.for(@a)[2].should eql(4)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
after(:all) do
|
78
|
+
Object.send(:remove_const, :A)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "/spec_helper")
|
2
|
+
|
3
|
+
describe "TeguGears" do
|
4
|
+
it "should require rubygems" do
|
5
|
+
defined?(Gem).should eql('constant')
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should require the examples" do
|
9
|
+
defined?(Fibonacci).should eql('constant')
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should automatically include any sub-modules when included" do
|
13
|
+
class A; include TeguGears end
|
14
|
+
A.included_modules.should be_include(TeguGears::Memoize)
|
15
|
+
Object.send(:remove_const, :A)
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: davidrichards-tegu_gears
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Richards
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-28 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: TODO
|
17
|
+
email: davidlamontrichards@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- VERSION.yml
|
26
|
+
- lib/examples
|
27
|
+
- lib/examples/factorial.rb
|
28
|
+
- lib/examples/fibonacci.rb
|
29
|
+
- lib/examples/regression.rb
|
30
|
+
- lib/tegu_gears
|
31
|
+
- lib/tegu_gears/compose.rb
|
32
|
+
- lib/tegu_gears/dynamics
|
33
|
+
- lib/tegu_gears/dynamics/node.rb
|
34
|
+
- lib/tegu_gears/dynamics/noodle.rb
|
35
|
+
- lib/tegu_gears/dynamics/operator.rb
|
36
|
+
- lib/tegu_gears/dynamics.rb
|
37
|
+
- lib/tegu_gears/memo_repository.rb
|
38
|
+
- lib/tegu_gears/memoize.rb
|
39
|
+
- lib/tegu_gears.rb
|
40
|
+
- spec/lib
|
41
|
+
- spec/lib/examples
|
42
|
+
- spec/lib/examples/factorial_spec.rb
|
43
|
+
- spec/lib/examples/fibonacci_spec.rb
|
44
|
+
- spec/lib/tegu_gears
|
45
|
+
- spec/lib/tegu_gears/compose_spec.rb
|
46
|
+
- spec/lib/tegu_gears/memo_repository_spec.rb
|
47
|
+
- spec/lib/tegu_gears/memoize_spec.rb
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
- spec/tegu_gears_spec.rb
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: http://github.com/davidrichards/tegu_gears
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options:
|
54
|
+
- --inline-source
|
55
|
+
- --charset=UTF-8
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.2.0
|
74
|
+
signing_key:
|
75
|
+
specification_version: 2
|
76
|
+
summary: TODO
|
77
|
+
test_files: []
|
78
|
+
|