stunted 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|