davidrichards-tegu_gears 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|