stunted 0.0.1 → 0.0.2
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/.gitignore +2 -0
- data/README.md +6 -0
- data/Rakefile +0 -8
- data/lib/stunted.rb +4 -0
- data/lib/stunted/chaining.rb +1 -0
- data/lib/stunted/functional-hash.rb +6 -28
- data/lib/stunted/hash-array.rb +20 -0
- data/lib/stunted/mocking.rb +21 -0
- data/lib/stunted/shapeable.rb +18 -0
- data/lib/stunted/stutils.rb +27 -0
- data/lib/stunted/version.rb +1 -1
- data/stunted.gemspec +3 -5
- data/test/functional-hash-test.rb +19 -4
- data/test/hash-array-tests.rb +74 -0
- data/test/helper.rb +2 -1
- data/test/mocking-tests.rb +118 -0
- data/test/shapeable-tests.rb +28 -0
- data/test/stutils-test.rb +83 -0
- metadata +31 -30
data/README.md
CHANGED
@@ -9,3 +9,9 @@ written by other people.
|
|
9
9
|
|
10
10
|
Documentation is in the [wiki](https://github.com/marick/stunted/wiki).
|
11
11
|
There is a mailing list for [functional programming in Ruby](http://groups.google.com/group/rubyfoopers).
|
12
|
+
|
13
|
+
Contributors:
|
14
|
+
|
15
|
+
* Brian Marick
|
16
|
+
* Edward Monical-Vuylsteke
|
17
|
+
* Lar Van Der Jagt
|
data/Rakefile
CHANGED
@@ -7,14 +7,6 @@ Rake::TestTask.new(:test) do |test|
|
|
7
7
|
test.verbose = true
|
8
8
|
end
|
9
9
|
|
10
|
-
require 'rcov/rcovtask'
|
11
|
-
Rcov::RcovTask.new do |test|
|
12
|
-
test.libs << 'test'
|
13
|
-
test.pattern = 'test/**/test_*.rb'
|
14
|
-
test.verbose = true
|
15
|
-
test.rcov_opts << '--exclude "gems/*"'
|
16
|
-
end
|
17
|
-
|
18
10
|
task :default => :test
|
19
11
|
|
20
12
|
require 'rdoc/task'
|
data/lib/stunted.rb
CHANGED
data/lib/stunted/chaining.rb
CHANGED
@@ -8,6 +8,7 @@ module Stunted
|
|
8
8
|
if fn
|
9
9
|
define_method(name) { fn }
|
10
10
|
else
|
11
|
+
puts "Lambda rigamarole could be just { block }"
|
11
12
|
define_method(name) { lambda(&block) } # Todo: why is this lambda rigamarole required?
|
12
13
|
end
|
13
14
|
module_function name if respond_to?(:module_function, true)
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Stunted
|
2
2
|
|
3
3
|
class FunctionalHash < Hash
|
4
|
+
include Shapeable
|
5
|
+
extend ShapeableClassMethods
|
4
6
|
|
5
7
|
def initialize(hash = {})
|
6
8
|
hash.each do | k, v |
|
@@ -32,6 +34,10 @@ module Stunted
|
|
32
34
|
end
|
33
35
|
alias_method :+, :merge
|
34
36
|
|
37
|
+
def transform(symbol)
|
38
|
+
merge(symbol => yield(self[symbol]))
|
39
|
+
end
|
40
|
+
|
35
41
|
def change_within(*args)
|
36
42
|
if (args.first.is_a? Hash)
|
37
43
|
merge(args.first)
|
@@ -71,23 +77,11 @@ module Stunted
|
|
71
77
|
self.class[*keys.zip(self.values_at(*keys)).flatten(1)]
|
72
78
|
end
|
73
79
|
|
74
|
-
def self.shaped_class(*shapes)
|
75
|
-
klass = Class.new(self)
|
76
|
-
shapes.each do | mod |
|
77
|
-
klass.send(:include, mod)
|
78
|
-
end
|
79
|
-
klass
|
80
|
-
end
|
81
|
-
|
82
80
|
def self.make_maker(*shapes)
|
83
81
|
klass = shaped_class(*shapes)
|
84
82
|
->(inits={}) { klass.new(inits) }
|
85
83
|
end
|
86
84
|
|
87
|
-
def become(*shapes)
|
88
|
-
self.class.shaped_class(*shapes).new(self)
|
89
|
-
end
|
90
|
-
|
91
85
|
def component(hash) # For now, just one pair
|
92
86
|
field = hash.keys.first
|
93
87
|
shape = hash.values.first
|
@@ -104,20 +98,4 @@ module Stunted
|
|
104
98
|
private :[]=, :clear, :delete, :delete_if
|
105
99
|
end
|
106
100
|
|
107
|
-
|
108
|
-
module FHUtil
|
109
|
-
def F(hash = {})
|
110
|
-
FunctionalHash.new(hash)
|
111
|
-
end
|
112
|
-
|
113
|
-
def Fonly(tuples)
|
114
|
-
F(tuples.first)
|
115
|
-
end
|
116
|
-
|
117
|
-
def Fall(tuples)
|
118
|
-
tuples.map { | row | F(row) }
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
101
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Stunted
|
2
|
+
class HashArray < Array
|
3
|
+
include Shapeable
|
4
|
+
extend ShapeableClassMethods
|
5
|
+
|
6
|
+
def collapse_and_aggregate(*keys)
|
7
|
+
keys.reduce(first) do | accumulator, key |
|
8
|
+
collection = collect(&key)
|
9
|
+
collection = yield(collection) if block_given?
|
10
|
+
accumulator.merge(key => collection)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def segregate_by_key(key)
|
15
|
+
group_by(&key).values.collect do | inner |
|
16
|
+
self.class.new(inner)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Module
|
2
|
+
def with_replacement_methods(hash)
|
3
|
+
old_pairs = hash.collect do | key, value |
|
4
|
+
old_method = instance_method(key)
|
5
|
+
define_method(key, value)
|
6
|
+
[key, old_method]
|
7
|
+
end
|
8
|
+
yield
|
9
|
+
ensure
|
10
|
+
old_pairs.each { | pair | define_method(*pair) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Stunted
|
15
|
+
|
16
|
+
class FunctionalHash
|
17
|
+
def with_replacement_methods(hash, &block)
|
18
|
+
self.class.with_replacement_methods(hash, &block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Stunted
|
2
|
+
module ShapeableClassMethods
|
3
|
+
def shaped_class(*shapes)
|
4
|
+
klass = Class.new(self)
|
5
|
+
shapes.each do | mod |
|
6
|
+
klass.send(:include, mod)
|
7
|
+
end
|
8
|
+
klass
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Shapeable
|
13
|
+
def become(*shapes)
|
14
|
+
return self if shapes.empty?
|
15
|
+
self.class.shaped_class(*shapes).new(self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Stunted
|
2
|
+
module Stutils
|
3
|
+
def F(hash = {}, *shapes)
|
4
|
+
FunctionalHash.new(hash).become(*shapes)
|
5
|
+
end
|
6
|
+
|
7
|
+
def Fonly(tuples, *shapes)
|
8
|
+
F(tuples.first, *shapes)
|
9
|
+
end
|
10
|
+
|
11
|
+
def Fall(tuples, *args)
|
12
|
+
first = args.first
|
13
|
+
if first.is_a?(Hash)
|
14
|
+
array_shapes = first[:array] || []
|
15
|
+
hash_shapes = first[:hash] || []
|
16
|
+
else
|
17
|
+
hash_shapes = args
|
18
|
+
array_shapes = []
|
19
|
+
end
|
20
|
+
HashArray.new(tuples.map { | row | F(row, *hash_shapes) }).
|
21
|
+
become(*array_shapes)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Stutil = Stutils unless defined?(Stutil) # Backward compatibility
|
26
|
+
FHUtil = Stutils unless defined?(FHUtil) # Backward compatibility
|
27
|
+
end
|
data/lib/stunted/version.rb
CHANGED
data/stunted.gemspec
CHANGED
@@ -20,11 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
|
23
|
-
s.add_runtime_dependency "hamster"
|
24
|
-
|
25
23
|
s.add_development_dependency "shoulda", ">= 0"
|
26
|
-
s.add_development_dependency "
|
27
|
-
s.add_development_dependency "bundler", "~> 1.0.0"
|
24
|
+
s.add_development_dependency "bundler"
|
28
25
|
s.add_development_dependency "jeweler", "~> 1.6.4"
|
29
|
-
s.add_development_dependency "
|
26
|
+
s.add_development_dependency "test-unit"
|
27
|
+
s.add_development_dependency "wrong"
|
30
28
|
end
|
@@ -2,7 +2,7 @@ require 'helper'
|
|
2
2
|
|
3
3
|
class FunctionalHashTest < Test::Unit::TestCase
|
4
4
|
include Stunted
|
5
|
-
include Stunted::
|
5
|
+
include Stunted::Stutils
|
6
6
|
|
7
7
|
|
8
8
|
context "non-lazy behavior" do
|
@@ -24,7 +24,9 @@ class FunctionalHashTest < Test::Unit::TestCase
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
|
27
28
|
context "lazy behavior" do
|
29
|
+
puts "==== What is the behavior with procs good for?"
|
28
30
|
should "take procs as arguments and evaluate them on demand" do
|
29
31
|
@sut = FunctionalHash.new(:a => lambda { @global })
|
30
32
|
@global = 33
|
@@ -86,7 +88,17 @@ class FunctionalHashTest < Test::Unit::TestCase
|
|
86
88
|
assert { empty.object_id != sut.object_id }
|
87
89
|
end
|
88
90
|
|
89
|
-
|
91
|
+
should "allow transformations of particular key values" do
|
92
|
+
sut = FunctionalHash.new(value: 5)
|
93
|
+
new_sut = sut.transform(:value) { | value | value * 3 }
|
94
|
+
assert { new_sut.value == 15 }
|
95
|
+
|
96
|
+
sut = FunctionalHash.new("string-key" => 1)
|
97
|
+
new_sut = sut.transform("string-key") { | value | value * 3 }
|
98
|
+
assert { new_sut["string-key"] == 3 }
|
99
|
+
end
|
100
|
+
|
101
|
+
context "removing within" do
|
90
102
|
setup do
|
91
103
|
@hashlike = F(:val => 3,
|
92
104
|
:other => "other",
|
@@ -104,7 +116,7 @@ class FunctionalHashTest < Test::Unit::TestCase
|
|
104
116
|
end
|
105
117
|
|
106
118
|
should "allow nesting" do
|
107
|
-
actual = @hashlike.change_within(:nested, val => 44)
|
119
|
+
actual = @hashlike.change_within(:nested, :val => 44)
|
108
120
|
assert { actual.is_a?(FunctionalHash) }
|
109
121
|
assert { actual.val == 3 }
|
110
122
|
assert { actual.nested.val == 44 }
|
@@ -112,7 +124,7 @@ class FunctionalHashTest < Test::Unit::TestCase
|
|
112
124
|
end
|
113
125
|
|
114
126
|
should "allow n levels of nesting" do
|
115
|
-
actual = @hashlike.change_within(:nested, :nested, val => 444)
|
127
|
+
actual = @hashlike.change_within(:nested, :nested, :val => 444)
|
116
128
|
assert { actual.is_a?(FunctionalHash) }
|
117
129
|
assert { actual.val == 3 }
|
118
130
|
assert { actual.nested.val == 33 }
|
@@ -261,6 +273,9 @@ class FunctionalHashTest < Test::Unit::TestCase
|
|
261
273
|
end
|
262
274
|
|
263
275
|
end
|
276
|
+
|
277
|
+
|
278
|
+
|
264
279
|
end
|
265
280
|
end
|
266
281
|
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HashArrayTest < Test::Unit::TestCase
|
4
|
+
include Stunted
|
5
|
+
include Stunted::Stutils
|
6
|
+
|
7
|
+
context "collapsing a hash-array into one hash" do
|
8
|
+
should "aggregate named key values into an array" do
|
9
|
+
input = Fall([ {:id => 33, :name => "fred"},
|
10
|
+
{:id => 33, :name => "betsy"} ])
|
11
|
+
|
12
|
+
actual = input.collapse_and_aggregate(:name)
|
13
|
+
expected = {:id => 33, :name => ["fred", "betsy"]}
|
14
|
+
assert { actual == expected }
|
15
|
+
end
|
16
|
+
|
17
|
+
should "aggregate allow multiple keys" do
|
18
|
+
input = Fall([ {:id => 33, :name => "fred", :hair => 3},
|
19
|
+
{:id => 33, :name => "betsy", :hair => 44} ])
|
20
|
+
|
21
|
+
actual = input.collapse_and_aggregate(:name, :hair)
|
22
|
+
expected = {:id => 33, :name => ["fred", "betsy"], :hair => [3, 44]}
|
23
|
+
assert { actual == expected }
|
24
|
+
end
|
25
|
+
|
26
|
+
should "silently do the wrong thing if non-constant keys are not named" do
|
27
|
+
input = Fall([ {:id => 33, :name => "fred", :hair => 3},
|
28
|
+
{:id => 33, :name => "betsy", :hair => 44} ])
|
29
|
+
actual = input.collapse_and_aggregate(:name)
|
30
|
+
deny { actual.hair == [3, 44] }
|
31
|
+
end
|
32
|
+
|
33
|
+
should "take a block that can be used to process the arrays" do
|
34
|
+
input = Fall([ {:id => 33, :name => "fred", :hair => 3},
|
35
|
+
{:id => 33, :name => "betsy", :hair => 44},
|
36
|
+
{:id => 33, :name => "fred", :hair => 44},
|
37
|
+
{:id => 33, :name => "betsy", :hair => 3}])
|
38
|
+
actual = input.collapse_and_aggregate(:name, :hair) { | a | a.sort.uniq }
|
39
|
+
expected = {:id => 33, :name => ["betsy", "fred"], :hair => [3, 44]}
|
40
|
+
assert { actual == expected }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "segregation by keys" do
|
45
|
+
setup do
|
46
|
+
@input = Fall([ {:id => 1, :name => "fred"},
|
47
|
+
{:id => 2, :name => "betsy"},
|
48
|
+
{:id => 1, :name => "dawn"} ])
|
49
|
+
|
50
|
+
@actual = @input.segregate_by_key(:id)
|
51
|
+
@expected = [ [ {:id => 1, :name => "fred"},
|
52
|
+
{:id => 1, :name => "dawn"} ],
|
53
|
+
[ {:id => 2, :name => "betsy"} ] ]
|
54
|
+
end
|
55
|
+
|
56
|
+
should "produce an array of arrays" do
|
57
|
+
assert { @actual == @expected }
|
58
|
+
end
|
59
|
+
|
60
|
+
should "make the inner arrays HashArrays" do
|
61
|
+
assert { @actual.first.is_a?(Stunted::HashArray) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "shapeability" do
|
66
|
+
module ArrayShaped
|
67
|
+
def neg_count; -count; end
|
68
|
+
end
|
69
|
+
|
70
|
+
should "also apply to hash arrays" do
|
71
|
+
assert { Fall([ {:a => 1}]).become(ArrayShaped).neg_count == -1 }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/test/helper.rb
CHANGED
@@ -9,11 +9,12 @@ rescue Bundler::BundlerError => e
|
|
9
9
|
end
|
10
10
|
require 'test/unit'
|
11
11
|
require 'shoulda'
|
12
|
-
require '
|
12
|
+
require 'wrong'
|
13
13
|
|
14
14
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
15
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
16
|
require 'stunted'
|
17
17
|
|
18
18
|
class Test::Unit::TestCase
|
19
|
+
include Wrong
|
19
20
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class FunctionalHashTest < Test::Unit::TestCase
|
4
|
+
include Stunted
|
5
|
+
include Stunted::Stutils
|
6
|
+
|
7
|
+
context "mocking out methods via a class" do
|
8
|
+
class ClassWithMethods < FunctionalHash
|
9
|
+
def my_method; "value of unmocked method"; end
|
10
|
+
end
|
11
|
+
|
12
|
+
should "be able to temporarily replace a module's instance methods" do
|
13
|
+
ClassWithMethods.with_replacement_methods(my_method: -> { "replaced value" }) do
|
14
|
+
assert { ClassWithMethods.new.my_method == "replaced value" }
|
15
|
+
end
|
16
|
+
assert { ClassWithMethods.new.my_method == "value of unmocked method" }
|
17
|
+
end
|
18
|
+
|
19
|
+
should "undo the replacement no matter what" do
|
20
|
+
assert_raises(Exception) do
|
21
|
+
ClassWithMethods.with_replacement_methods(my_method: -> { "replaced value" }) do
|
22
|
+
raise Exception.new("boom")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
assert { ClassWithMethods.new.my_method == "value of unmocked method" }
|
26
|
+
end
|
27
|
+
|
28
|
+
should "keep the modified instance methods for all versions of the object" do
|
29
|
+
ClassWithMethods.with_replacement_methods(my_method: -> a { merge(value: self.value * a) }) do
|
30
|
+
first = ClassWithMethods.new(value: 1)
|
31
|
+
second = first.my_method(5)
|
32
|
+
third = second.my_method(10)
|
33
|
+
|
34
|
+
assert { second.value == 5 }
|
35
|
+
assert { third.value == 50 }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
should "The overriding of the method ends at the end of the block" do
|
40
|
+
initial = "declared local"
|
41
|
+
ClassWithMethods.with_replacement_methods(my_method: -> a { merge(value: self.value * a)}) do
|
42
|
+
initial = ClassWithMethods.new
|
43
|
+
end
|
44
|
+
assert { initial.my_method == "value of unmocked method" }
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
module ModuleWithMethods
|
50
|
+
def my_method; "value of unmocked method"; end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "mocking out methods on a module" do
|
54
|
+
class ClassWithIncludedMethods < FunctionalHash
|
55
|
+
include ModuleWithMethods
|
56
|
+
end
|
57
|
+
|
58
|
+
should "be able to temporarily replace a module's instance methods" do
|
59
|
+
ModuleWithMethods.with_replacement_methods(my_method: -> { "replaced value" }) do
|
60
|
+
assert { ClassWithIncludedMethods.new.my_method == "replaced value" }
|
61
|
+
end
|
62
|
+
assert { ClassWithIncludedMethods.new.my_method == "value of unmocked method" }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'mocking out methods for a "made" class' do
|
67
|
+
maker = FunctionalHash.make_maker(ModuleWithMethods)
|
68
|
+
|
69
|
+
should "be able to temporarily replace a made class's instance methods" do
|
70
|
+
maker.().class.with_replacement_methods(my_method: -> { "replaced value" }) do
|
71
|
+
assert { maker.().my_method == "replaced value" }
|
72
|
+
end
|
73
|
+
assert { maker.().my_method == "value of unmocked method" }
|
74
|
+
end
|
75
|
+
|
76
|
+
should "The overriding of the method ends at the end of the block" do
|
77
|
+
initial = "declared local"
|
78
|
+
ClassWithMethods.with_replacement_methods(my_method: -> a { merge(value: self.value * a)}) do
|
79
|
+
initial = ClassWithMethods.new
|
80
|
+
end
|
81
|
+
assert { initial.my_method == "value of unmocked method" }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "mocking particular objects" do
|
86
|
+
should "be able to apply the mock function to an object, not just a module." do
|
87
|
+
object = ClassWithMethods.new
|
88
|
+
object.with_replacement_methods(my_method: -> { "replaced value" }) do
|
89
|
+
assert { object.my_method == "replaced value" }
|
90
|
+
end
|
91
|
+
assert { object.my_method == "value of unmocked method" }
|
92
|
+
end
|
93
|
+
|
94
|
+
should_eventually "changing one object's methods doesn't affect another's" do
|
95
|
+
mocked = ClassWithMethods.new
|
96
|
+
unmocked = ClassWithMethods.new
|
97
|
+
mocked.with_replacement_methods(my_method: -> { "replaced value" }) do
|
98
|
+
assert { unmocked.my_method == "value of unmocked method" }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
context "mocking out lambdas attached to a class" do
|
105
|
+
class ClassWithLambdas < FunctionalHash
|
106
|
+
extend Stunted::Defn
|
107
|
+
defn :my_lambda, -> { "value of unmocked lambda" }
|
108
|
+
end
|
109
|
+
|
110
|
+
should_eventually "be able to temporarily replace a module's `defn`ed lambdas" do
|
111
|
+
ClassWithLambdas.with_replacement_methods(my_lambda: -> { "replaced value" }) do
|
112
|
+
assert { ClassWithLambdas.new.my_lambda.() == "replaced value" }
|
113
|
+
end
|
114
|
+
assert { ClassWithLambdas.new.my_lambda.() == "value of unmocked lambda" }
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ShapeableTests < Test::Unit::TestCase
|
4
|
+
include Stunted
|
5
|
+
include Stunted::Stutils
|
6
|
+
|
7
|
+
# Mostly tested through use in including classes
|
8
|
+
|
9
|
+
module Shapely
|
10
|
+
end
|
11
|
+
|
12
|
+
should "normally return a new class" do
|
13
|
+
# For some reason, putting this inside an `assert` makes test hang.
|
14
|
+
klass = FunctionalHash.new.become(Shapely).class
|
15
|
+
# The test library hangs if it tries to print a class created by
|
16
|
+
# Class.new
|
17
|
+
test = (klass == FunctionalHash); deny { test }
|
18
|
+
test = klass.ancestors.include?(FunctionalHash); assert { test }
|
19
|
+
end
|
20
|
+
|
21
|
+
should "not return a new class if there is nothing to become" do
|
22
|
+
# For some reason, putting this inside an `assert` makes test hang.
|
23
|
+
klass = FunctionalHash.new.become().class
|
24
|
+
test = (klass == FunctionalHash); assert { test }
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class StutilsTest < Test::Unit::TestCase
|
4
|
+
include Stunted
|
5
|
+
include Stunted::Stutils
|
6
|
+
|
7
|
+
module Upable
|
8
|
+
def up; "up!"; end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Downable
|
12
|
+
def down; "down!"; end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "F" do
|
16
|
+
should "create a functional hash" do
|
17
|
+
# Only is a function that's not on the real Hash
|
18
|
+
assert { F(:a => 1, :b => 2).only(:a).is_a? FunctionalHash }
|
19
|
+
end
|
20
|
+
|
21
|
+
should "allow shape to be specified" do
|
22
|
+
h = F({}, Upable, Downable)
|
23
|
+
assert { h.up == "up!" }
|
24
|
+
assert { h.down == "down!" }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "Fonly" do
|
29
|
+
should "create a functional hash from within an array" do
|
30
|
+
h = Fonly([{}], Upable, Downable)
|
31
|
+
assert { h.is_a? FunctionalHash }
|
32
|
+
assert { h.up == "up!" }
|
33
|
+
assert { h.down == "down!" }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "Fall" do
|
38
|
+
should "create a HashArray wrapping Functional Hashes" do
|
39
|
+
a = Fall([{}])
|
40
|
+
assert { a.is_a?(HashArray) }
|
41
|
+
assert { a.first.is_a?(FunctionalHash) }
|
42
|
+
end
|
43
|
+
|
44
|
+
should "shape the hash entries if given one or more arguments" do
|
45
|
+
a = Fall([{}], Upable)
|
46
|
+
assert {a.first.respond_to?(:up) }
|
47
|
+
deny {a.first.respond_to?(:down) }
|
48
|
+
|
49
|
+
a = Fall([{}], Upable, Downable)
|
50
|
+
assert {a.first.respond_to?(:up) }
|
51
|
+
assert {a.first.respond_to?(:down) }
|
52
|
+
end
|
53
|
+
|
54
|
+
should "allow the shape of the array to be given" do
|
55
|
+
a = Fall([{}], :array => Upable)
|
56
|
+
assert {a.up == "up!" }
|
57
|
+
deny {a.first.respond_to?(:up) }
|
58
|
+
end
|
59
|
+
|
60
|
+
should "allow more than one shape for the array" do
|
61
|
+
a = Fall([{}], :array => [Upable, Downable])
|
62
|
+
assert {a.up == "up!" }
|
63
|
+
assert {a.down == "down!" }
|
64
|
+
end
|
65
|
+
|
66
|
+
should "allow both array and hash to be shaped" do
|
67
|
+
a = Fall([{}], :array => Upable, :hash => Downable)
|
68
|
+
assert {a.respond_to?(:up) }
|
69
|
+
deny {a.respond_to?(:down) }
|
70
|
+
assert {a.first.respond_to?(:down) }
|
71
|
+
deny {a.first.respond_to?(:up) }
|
72
|
+
end
|
73
|
+
|
74
|
+
should "allow both array and hash to be shaped with multiple shapes" do
|
75
|
+
a = Fall([{}], :array => [Upable, Downable], :hash => [Upable, Downable])
|
76
|
+
assert {a.respond_to?(:up) }
|
77
|
+
assert {a.respond_to?(:down) }
|
78
|
+
assert {a.first.respond_to?(:down) }
|
79
|
+
assert {a.first.respond_to?(:up) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stunted
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,22 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-20 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: hamster
|
16
|
-
requirement: &2156591000 !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *2156591000
|
25
14
|
- !ruby/object:Gem::Dependency
|
26
15
|
name: shoulda
|
27
|
-
requirement: &
|
16
|
+
requirement: &2153385740 !ruby/object:Gem::Requirement
|
28
17
|
none: false
|
29
18
|
requirements:
|
30
19
|
- - ! '>='
|
@@ -32,10 +21,10 @@ dependencies:
|
|
32
21
|
version: '0'
|
33
22
|
type: :development
|
34
23
|
prerelease: false
|
35
|
-
version_requirements: *
|
24
|
+
version_requirements: *2153385740
|
36
25
|
- !ruby/object:Gem::Dependency
|
37
|
-
name:
|
38
|
-
requirement: &
|
26
|
+
name: bundler
|
27
|
+
requirement: &2153385340 !ruby/object:Gem::Requirement
|
39
28
|
none: false
|
40
29
|
requirements:
|
41
30
|
- - ! '>='
|
@@ -43,32 +32,32 @@ dependencies:
|
|
43
32
|
version: '0'
|
44
33
|
type: :development
|
45
34
|
prerelease: false
|
46
|
-
version_requirements: *
|
35
|
+
version_requirements: *2153385340
|
47
36
|
- !ruby/object:Gem::Dependency
|
48
|
-
name:
|
49
|
-
requirement: &
|
37
|
+
name: jeweler
|
38
|
+
requirement: &2153384800 !ruby/object:Gem::Requirement
|
50
39
|
none: false
|
51
40
|
requirements:
|
52
41
|
- - ~>
|
53
42
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.
|
43
|
+
version: 1.6.4
|
55
44
|
type: :development
|
56
45
|
prerelease: false
|
57
|
-
version_requirements: *
|
46
|
+
version_requirements: *2153384800
|
58
47
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
60
|
-
requirement: &
|
48
|
+
name: test-unit
|
49
|
+
requirement: &2153384380 !ruby/object:Gem::Requirement
|
61
50
|
none: false
|
62
51
|
requirements:
|
63
|
-
- -
|
52
|
+
- - ! '>='
|
64
53
|
- !ruby/object:Gem::Version
|
65
|
-
version:
|
54
|
+
version: '0'
|
66
55
|
type: :development
|
67
56
|
prerelease: false
|
68
|
-
version_requirements: *
|
57
|
+
version_requirements: *2153384380
|
69
58
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
71
|
-
requirement: &
|
59
|
+
name: wrong
|
60
|
+
requirement: &2153383860 !ruby/object:Gem::Requirement
|
72
61
|
none: false
|
73
62
|
requirements:
|
74
63
|
- - ! '>='
|
@@ -76,7 +65,7 @@ dependencies:
|
|
76
65
|
version: '0'
|
77
66
|
type: :development
|
78
67
|
prerelease: false
|
79
|
-
version_requirements: *
|
68
|
+
version_requirements: *2153383860
|
80
69
|
description: Support code for functional programming
|
81
70
|
email: marick@exampler.com
|
82
71
|
executables: []
|
@@ -100,12 +89,20 @@ files:
|
|
100
89
|
- lib/stunted.rb
|
101
90
|
- lib/stunted/chaining.rb
|
102
91
|
- lib/stunted/functional-hash.rb
|
92
|
+
- lib/stunted/hash-array.rb
|
93
|
+
- lib/stunted/mocking.rb
|
94
|
+
- lib/stunted/shapeable.rb
|
95
|
+
- lib/stunted/stutils.rb
|
103
96
|
- lib/stunted/version.rb
|
104
97
|
- stunted.gemspec
|
105
98
|
- test/chainable-tests.rb
|
106
99
|
- test/defn-tests.rb
|
107
100
|
- test/functional-hash-test.rb
|
101
|
+
- test/hash-array-tests.rb
|
108
102
|
- test/helper.rb
|
103
|
+
- test/mocking-tests.rb
|
104
|
+
- test/shapeable-tests.rb
|
105
|
+
- test/stutils-test.rb
|
109
106
|
homepage: http://github.com/marick/stunted
|
110
107
|
licenses:
|
111
108
|
- MIT
|
@@ -135,4 +132,8 @@ test_files:
|
|
135
132
|
- test/chainable-tests.rb
|
136
133
|
- test/defn-tests.rb
|
137
134
|
- test/functional-hash-test.rb
|
135
|
+
- test/hash-array-tests.rb
|
138
136
|
- test/helper.rb
|
137
|
+
- test/mocking-tests.rb
|
138
|
+
- test/shapeable-tests.rb
|
139
|
+
- test/stutils-test.rb
|