method_missing 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in method_missing.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,30 @@
1
+ Copyright (c)2011, Mike Burns
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above
12
+ copyright notice, this list of conditions and the following
13
+ disclaimer in the documentation and/or other materials provided
14
+ with the distribution.
15
+
16
+ * Neither the name of Mike Burns nor the names of other
17
+ contributors may be used to endorse or promote products derived
18
+ from this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,89 @@
1
+ method\_missing
2
+ ==============
3
+
4
+ The Method gem you've been waiting for.
5
+
6
+ The method\_missing gem brings the functional tools you need to Ruby: method composition, sequencing, and repeating. These are useful for when you must combine methods, apply different methods to the same argument, or "grow" a method.
7
+
8
+ I'll explain, but first:
9
+
10
+ Installing
11
+ ----------
12
+
13
+ gem install method_missing
14
+
15
+ Usage: Composing
16
+ ----------------
17
+
18
+ This is the classic. In algebra class you learned that `f(g(x))` can also be written `(f . g)(x)`. This is handy because now you have this `(f . g)` object that you can pass to integrals and whatnot.
19
+
20
+ So in Ruby, using the method\_missing gem:
21
+
22
+ def escape_everything(text)
23
+ everything_ecaper.call(text)
24
+ end
25
+
26
+ def everything_ecaper
27
+ method(:html_escape) * method(:escape_javascript) * method(:json_escape)
28
+ end
29
+
30
+ And more algebraically:
31
+
32
+ (f * g).call(x) == f.call(g.call(x))
33
+
34
+ Usage: Sequencing
35
+ -----------------
36
+
37
+ This doesn't come up as often but when it does, oh boy, does it ever! This is useful for when you have a bunch of methods to apply to the same argument. For example, using the method\_missing gem:
38
+
39
+ def valid_options?(options_checker, options)
40
+ options_checker.at_most_one(
41
+ (method(:sort_mod_time) / method(:sort_access_time)).call(options)) &&
42
+ options_checker.at_most_one(
43
+ (method(:sort_file_size) / method(:sort_mod_time)).call(options))
44
+ end
45
+
46
+ Again, more algebraically:
47
+
48
+ (f / g / h).call(x) == [f.call(x), g.call(x), h.call(x)]
49
+
50
+ Usage: Repeating
51
+ ----------------
52
+
53
+ This one comes up the least in most people's day-to-day life. It's the concept of applying a method to its output, repeatedly. Here's a contrived example, using the method\_missing gem:
54
+
55
+ def church_encoding(n)
56
+ if n.zero?
57
+ lambda{|f,x| x}
58
+ else
59
+ lambda{|f,x| (f ^ n).call(x)}
60
+ end
61
+ end
62
+
63
+ Algebraically:
64
+
65
+ (f ^ 3).call(4) == f.call(f.call(f.call(4)))
66
+
67
+ Usage: Combining
68
+ ----------------
69
+
70
+ These are objects which can be combined! Here's an example, also contrived, that turns an object into a number, *n*, then adds four to *n* and multiplies *n* by six, then sums those results, using primitive methods that add one to a number and multiply a number by two:
71
+
72
+ def interesting_math(o)
73
+ four_and_six.call(o).sum
74
+ end
75
+
76
+ def four_and_six
77
+ ((add1 ^ 4) / (mul2 ^ 3)) * method(:to_i)
78
+ end
79
+
80
+ To show this algebraically:
81
+
82
+ (((add1 ^ 4) / (mul2 ^ 3)) * method(:to_i)).call(o) == [
83
+ add1.call(add1.call(add1.call(add1.call(o.to_i)))),
84
+ mul2.call(mul2.call(mul2.call(o.to_i)))
85
+ ]
86
+
87
+ Copyright
88
+ ---------
89
+ Copyright 2011 Mike Burns
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,2 @@
1
+ require "method_missing/version"
2
+ require 'method_missing/method_extension'
@@ -0,0 +1,42 @@
1
+ require 'method_missing/cons_method'
2
+
3
+ class ApMethod
4
+ def initialize(methods)
5
+ @methods = methods
6
+ end
7
+
8
+ def *(f)
9
+ ApMethod.new(@methods.map {|m| ConsMethod.new(m, f)})
10
+ end
11
+
12
+ def /(f)
13
+ ApMethod.new(@methods + [f])
14
+ end
15
+
16
+ def ^(power)
17
+ ApMethod.new(@methods.map {|m| m ^ power})
18
+ end
19
+
20
+
21
+ def call(*x)
22
+ @methods.map{|m| m.call(*x)}
23
+ end
24
+
25
+ def to_proc
26
+ lambda {|*x|
27
+ @methods.map {|m| m.call(*x) }
28
+ }
29
+ end
30
+
31
+ def inspect
32
+ "#<ApMethod: #{@methods.map{|m| m.inspect}.join(' / ')}>"
33
+ end
34
+
35
+ def arity
36
+ @methods.first.arity
37
+ end
38
+
39
+ def [](*x)
40
+ call(*x)
41
+ end
42
+ end
@@ -0,0 +1,53 @@
1
+ require 'method_missing/ap_method'
2
+
3
+ class ConsMethod
4
+ def initialize(f,g)
5
+ @f = f
6
+ @g = g
7
+ end
8
+
9
+ def *(h)
10
+ ConsMethod.new(self, h)
11
+ end
12
+
13
+ def /(h)
14
+ ApMethod.new([@f, @g, h])
15
+ end
16
+
17
+ def ^(n)
18
+ if n < 2
19
+ self
20
+ else
21
+ ConsMethod.new(self, self ^ (n-1))
22
+ end
23
+ end
24
+
25
+
26
+ def owner
27
+ @g.owner
28
+ end
29
+
30
+ def receiver
31
+ @g.receiver
32
+ end
33
+
34
+ def to_proc
35
+ Proc.new {|x| @f.call(*@g.call(*x)) }
36
+ end
37
+
38
+ def inspect
39
+ "#<ConsMethod: #{@f.inspect} * #{@g.inspect}>"
40
+ end
41
+
42
+ def arity
43
+ @g.arity
44
+ end
45
+
46
+ def call(x)
47
+ @f.call(*@g.call(*x))
48
+ end
49
+
50
+ def [](*x)
51
+ call(*x)
52
+ end
53
+ end
@@ -0,0 +1,20 @@
1
+ require 'method_missing/cons_method'
2
+ require 'method_missing/ap_method'
3
+
4
+ class Method
5
+ def *(g)
6
+ ConsMethod.new(self,g)
7
+ end
8
+
9
+ def /(g)
10
+ ApMethod.new([self, g])
11
+ end
12
+
13
+ def ^(power)
14
+ if power < 2
15
+ self
16
+ else
17
+ ConsMethod.new(self, self ^ (power-1))
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module MethodMissing
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "method_missing/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "method_missing"
7
+ s.version = MethodMissing::VERSION
8
+ s.authors = ["Mike Burns"]
9
+ s.email = ["mike@mike-burns.com"]
10
+ s.homepage = "http://github.com/mike-burns/method_missing"
11
+ s.license = 'BSD'
12
+ s.summary = %q{Compose, sequence, and repeat Ruby methods.}
13
+ s.description = %q{
14
+ The methods on methods that you've been missing.
15
+
16
+ This gem adds the #* #/ and #^ methods so you can compose, sequence, and
17
+ repeat methods.
18
+
19
+ By composing methods you can express that one method calls another more
20
+ obviously.
21
+
22
+ By sequencing methods you can express that a series of methods have the
23
+ same argument more succinctly.
24
+
25
+ By repeating a method you can compose it with itself as needed, to build
26
+ upon itself.
27
+ }
28
+
29
+ s.rubyforge_project = "method_missing"
30
+
31
+ s.files = `git ls-files`.split("\n")
32
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
33
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
34
+ s.require_paths = ["lib"]
35
+
36
+ s.add_development_dependency('rspec')
37
+ s.add_development_dependency('rake')
38
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+ require 'method_missing/ap_method'
3
+ require 'method_missing/method_extension'
4
+
5
+ describe ApMethod do
6
+ def add3_m(x)
7
+ x + 3
8
+ end
9
+ def mul2_m(x)
10
+ x * 2
11
+ end
12
+ def sub1_m(x)
13
+ x - 1
14
+ end
15
+ def div4_m(x)
16
+ x / 4
17
+ end
18
+
19
+ let(:sub1) { method(:sub1_m) }
20
+ let(:mul2) { method(:mul2_m) }
21
+ let(:add3) { method(:add3_m) }
22
+ let(:div4) { method(:div4_m) }
23
+
24
+ subject { ApMethod.new([sub1, mul2, div4]) }
25
+
26
+ it "composes" do
27
+ (subject * add3).call(4).should ==
28
+ [sub1.call(add3.call(4)),
29
+ mul2.call(add3.call(4)),
30
+ div4.call(add3.call(4))]
31
+ end
32
+
33
+ it "sequences" do
34
+ result = [sub1.call(4), mul2.call(4), div4.call(4), add3.call(4)]
35
+ (subject / add3).call(4).should == result
36
+ end
37
+
38
+ it "repeats" do
39
+ power = 3
40
+ argument = 4
41
+ result = [
42
+ (sub1 ^ power).call(argument),
43
+ (mul2 ^ power).call(argument),
44
+ (div4 ^ power).call(argument)]
45
+ (subject ^ power).call(argument).should == result
46
+ end
47
+
48
+ context 'the Method interface' do
49
+ it "is callable" do
50
+ subject.call(4).should == [sub1.call(4), mul2.call(4), div4.call(4)]
51
+ end
52
+
53
+ it "has #[] as an alias for #call" do
54
+ subject[4].should == [sub1.call(4), mul2.call(4), div4.call(4)]
55
+ end
56
+
57
+ it "knows the arity of the method" do
58
+ subject.arity.should == 1
59
+ end
60
+
61
+ it "inspects into something nice" do
62
+ expected = "#<ApMethod: #{sub1.inspect} / #{mul2.inspect} / #{div4.inspect}>"
63
+ subject.inspect.should == expected
64
+ end
65
+
66
+ it "converts itself to a Proc" do
67
+ subject.to_proc.should be_a(Proc)
68
+ subject.to_proc.call(4).should == [sub1.call(4), mul2.call(4), div4.call(4)]
69
+ end
70
+
71
+ it "is unable to know the receiver" do
72
+ expect { subject.receiver }.to raise_error
73
+ end
74
+
75
+ it "is unable to know the method owner" do
76
+ expect { subject.owner }.to raise_error
77
+ end
78
+
79
+ it "is unable to know its name" do
80
+ expect { subject.name }.to raise_error
81
+ end
82
+
83
+ it "is unable to unbind" do
84
+ expect { subject.unbind }.to raise_error
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+ require 'method_missing/cons_method'
3
+
4
+ describe ConsMethod do
5
+ def add3_m(x)
6
+ x + 3
7
+ end
8
+ def mul2_m(x)
9
+ x * 2
10
+ end
11
+ def sub1_m(x)
12
+ x - 1
13
+ end
14
+
15
+ let(:sub1) { method(:sub1_m) }
16
+ let(:mul2) { method(:mul2_m) }
17
+ let(:add3) { method(:add3_m) }
18
+
19
+ subject { ConsMethod.new(sub1, mul2) }
20
+
21
+ it "composes" do
22
+ (subject * add3).call(4).should == sub1.call(mul2.call(add3.call(4)))
23
+ end
24
+
25
+ it "sequences" do
26
+ (subject / add3).call(4).should == [sub1.call(4), mul2.call(4), add3.call(4)]
27
+ end
28
+
29
+ it "repeats" do
30
+ result = sub1.call(mul2.call(
31
+ sub1.call(mul2.call(
32
+ sub1.call(mul2.call(4))))))
33
+ (subject ^ 3).call(4).should == result
34
+ end
35
+
36
+ context 'the Method interface' do
37
+ it "is callable" do
38
+ subject.call(4).should == sub1.call(mul2.call(4))
39
+ end
40
+
41
+ it "has #[] as an alias for #call" do
42
+ subject[4].should == sub1.call(mul2.call(4))
43
+ end
44
+
45
+ it "knows the arity of the method" do
46
+ subject.arity.should == 1
47
+ end
48
+
49
+ it "inspects into something nice" do
50
+ expected = "#<ConsMethod: #{sub1.inspect} * #{mul2.inspect}>"
51
+ subject.inspect.should == expected
52
+ end
53
+
54
+ it "converts itself to a Proc" do
55
+ subject.to_proc.should be_a(Proc)
56
+ subject.to_proc.call(4).should == sub1.call(mul2.call(4))
57
+ end
58
+
59
+ it "knows the receiver" do
60
+ subject.receiver.inspect.should == mul2.receiver.inspect
61
+ end
62
+
63
+ it "knows the method owner" do
64
+ subject.owner.should == mul2.owner
65
+ end
66
+
67
+ it "is unable to know its name" do
68
+ expect { subject.name }.to raise_error
69
+ end
70
+
71
+ it "is unable to unbind" do
72
+ expect { subject.unbind }.to raise_error
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'method_missing'
3
+
4
+ describe Method do
5
+ def sub1_m(x)
6
+ x - 1
7
+ end
8
+ def mul2_m(x)
9
+ x * 2
10
+ end
11
+
12
+ let(:sub1) { method(:sub1_m) }
13
+ let(:mul2) { method(:mul2_m) }
14
+
15
+ it "adds a composition operator to a method" do
16
+ (sub1 * mul2).call(3).should == sub1.call(mul2.call(3))
17
+ end
18
+
19
+ it "adds a sequencing operator to a method" do
20
+ (sub1 / mul2).call(3).should == [sub1.call(3), mul2.call(3)]
21
+ end
22
+
23
+ it "adds a repeat operator to a method" do
24
+ (mul2 ^ 3).call(3).should == mul2.call(mul2.call(mul2.call(3)))
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ require 'rspec'
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: method_missing
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Mike Burns
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-10-26 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rake
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: "\n The methods on methods that you've been missing.\n\n This gem adds the #* #/ and #^ methods so you can compose, sequence, and\n repeat methods.\n\n By composing methods you can express that one method calls another more\n obviously.\n\n By sequencing methods you can express that a series of methods have the\n same argument more succinctly.\n\n By repeating a method you can compose it with itself as needed, to build\n upon itself.\n "
49
+ email:
50
+ - mike@mike-burns.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - LICENSE
61
+ - README.md
62
+ - Rakefile
63
+ - lib/method_missing.rb
64
+ - lib/method_missing/ap_method.rb
65
+ - lib/method_missing/cons_method.rb
66
+ - lib/method_missing/method_extension.rb
67
+ - lib/method_missing/version.rb
68
+ - method_missing.gemspec
69
+ - spec/method_missing/ap_method_spec.rb
70
+ - spec/method_missing/cons_method_spec.rb
71
+ - spec/method_missing_spec.rb
72
+ - spec/spec_helper.rb
73
+ homepage: http://github.com/mike-burns/method_missing
74
+ licenses:
75
+ - BSD
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ requirements: []
100
+
101
+ rubyforge_project: method_missing
102
+ rubygems_version: 1.8.10
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Compose, sequence, and repeat Ruby methods.
106
+ test_files: []
107
+