funkify 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df42a1e54ad9d803baf96647480e58dacff3e5eb
4
+ data.tar.gz: 08175309961eea835db32aa26d019a0b68c169bf
5
+ SHA512:
6
+ metadata.gz: 5098c8b190f2bafc7cac212fd0ff14d3ced074be958d68025f3e1f7945e725051eb150e0547f5c9a1c69417fa9b5f07c0d59abe7c11a8eed942aeb2dce3c2502
7
+ data.tar.gz: a1b28ec738455b1109aee4f1f9cf1f9b22c4150dab18c718df56304ba0ce408994b0d9cbbd0d4e7362fbdef348090a1fafd2e85c1e9ccb36344fa9cbc212d48f
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in funkify.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 John Mair
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,90 @@
1
+ # Funkify
2
+
3
+ **(c) Copyright 2013, Epitron**
4
+
5
+ _Haskell-style partial application and composition for Ruby methods_
6
+
7
+ ## Usage
8
+
9
+ ```ruby
10
+ class MyFunkyClass
11
+ include Funkify
12
+
13
+ def add(x, y)
14
+ x + y
15
+ end
16
+
17
+ # we make a specific method autocurried using the auto_curry method
18
+ auto_curry :add
19
+
20
+ # alternatively, if we invoke auto_curry without a parameter
21
+ # then all subsequent methods will be autocurried
22
+ auto_curry
23
+
24
+ def mult(x, y)
25
+ x * y
26
+ end
27
+
28
+ def negate(x)
29
+ -x
30
+ end
31
+ end
32
+ ```
33
+
34
+ When a method supports autocurrying it can still be invoked normally (if all parameters are provided) however if less than the required number are given a `Proc` is returned
35
+ with the given parameters partially applied:
36
+
37
+ ```ruby
38
+ funky = MyFunkyClass.new
39
+
40
+ funky.add(1, 2) #=> This works normally and returns 3
41
+ add_1 = funky.add(1) #=> The `1` is partially applied and a `Proc` is returned
42
+ add_1.(2) #=> We invoke that `Proc` with the remaining argument and the final result (`3`) is returned.
43
+ ```
44
+
45
+ We can also compose methods using `+`:
46
+
47
+ ```ruby
48
+ add_1_and_multiply_by_5 = funky.mult(5) + funky.add(1)
49
+ add_1_and_multiply_by_5.(10) #=> 55
50
+
51
+ # We can even further compose the above with another method:
52
+
53
+ (funky.negate + add_1_and_multiply_by_5).(10) #=> -55
54
+ ```
55
+
56
+ Other examples:
57
+
58
+ Add 10 to every item in an Enumerable:
59
+
60
+ ```ruby
61
+ (1..5).map(&funky.add(10)) #=> [10, 12, 13, 14, 15]
62
+ ```
63
+
64
+ Multiply by 10 and negate every item in an Enumerable:
65
+
66
+ ```ruby
67
+ (1..5).map &(funky.negate + funky.mult(10)) => [-10, -20, -30, -40, -50]
68
+ ```
69
+
70
+ ## Installation
71
+
72
+ Add this line to your application's Gemfile:
73
+
74
+ gem 'funkify'
75
+
76
+ And then execute:
77
+
78
+ $ bundle
79
+
80
+ Or install it yourself as:
81
+
82
+ $ gem install funkify
83
+
84
+ ## Contributing
85
+
86
+ 1. Fork it
87
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
88
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
89
+ 4. Push to the branch (`git push origin my-new-feature`)
90
+ 5. Create new Pull Request
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Set up and run tests"
4
+ task :default => [:test]
5
+
6
+ def run_specs paths
7
+ quiet = ENV['VERBOSE'] ? '' : '-q'
8
+ exec "bacon -Ispec -rubygems #{quiet} #{paths.join ' '}"
9
+ end
10
+
11
+ task :test do
12
+ run_specs Dir['spec/**/*_spec.rb'].shuffle!
13
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'funkify/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "funkify"
8
+ spec.version = Funkify::VERSION
9
+ spec.authors = ["John Mair"]
10
+ spec.email = ["jrmair@gmail.com"]
11
+ spec.description = "Haskell-style partial application and composition for Ruby methods"
12
+ spec.summary = "Haskell-style partial application and composition for Ruby methods"
13
+ spec.homepage = "https://github.com/banister/funkify"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency 'bacon', '~> 1.2'
23
+ end
@@ -0,0 +1,67 @@
1
+ require "funkify/version"
2
+
3
+ module Funkify
4
+ def self.included(klass)
5
+ klass.extend ClassMethods
6
+ end
7
+
8
+ class ::Proc
9
+ def +(other)
10
+ Funkify.compose(self, other)
11
+ end
12
+ end
13
+
14
+ module_function
15
+
16
+ def curry(obj, *args)
17
+ case obj
18
+ when Symbol
19
+ method(obj).to_proc.curry[*args]
20
+ else
21
+ obj.curry(*args)
22
+ end
23
+ end
24
+
25
+ def compose(*args)
26
+ head, *tail = args
27
+ head = _procify(head)
28
+ if args.size <= 2
29
+ ->(*xs) { head.(_procify(tail[0]).(*xs)) }
30
+ else
31
+ ->(*xs) { head.(compose(*tail)) }
32
+ end
33
+ end
34
+
35
+ def _procify(obj)
36
+ case obj
37
+ when Symbol
38
+ method(obj).to_proc
39
+ else
40
+ obj.to_proc
41
+ end
42
+ end
43
+
44
+ module ClassMethods
45
+ def auto_curry(*names)
46
+ if names.empty?
47
+ in_use = nil
48
+ define_singleton_method(:method_added) do |name|
49
+ return if in_use
50
+ in_use = true
51
+ auto_curry name
52
+ in_use = false
53
+ end
54
+ return
55
+ end
56
+
57
+ names.each do |name|
58
+ m = instance_method(name)
59
+ curried_method = nil
60
+ define_method(name) do |*args|
61
+ curried_method ||= m.bind(self).to_proc.curry
62
+ curried_method[*args]
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,3 @@
1
+ module Funkify
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe Funkify do
4
+ describe "auto_curry method" do
5
+ before do
6
+ @c = Class.new do
7
+ include Funkify
8
+
9
+ def alpha(x) x end
10
+ def beta(x) x end
11
+ auto_curry :beta
12
+
13
+ def epsilon(x) x end
14
+
15
+ def gamma(x) x end
16
+ def zeta(x) x end
17
+ auto_curry :gamma, :zeta
18
+
19
+ auto_curry
20
+
21
+ def harry(x) x end
22
+ def joseph(x) x end
23
+ end.new
24
+ end
25
+
26
+ it 'does not autocurry unselected methods' do
27
+ should.raise(ArgumentError) { @c.alpha }
28
+ end
29
+
30
+ it 'curries one method, when specified' do
31
+ @c.beta.is_a?(Proc).should == true
32
+ end
33
+
34
+ it 'does not curry methods after auto_curry used to curry a single method' do
35
+ should.raise(ArgumentError) { @c.epsilon }
36
+ end
37
+
38
+ it 'curries multiple methods when auto_curry given multiple args' do
39
+ @c.gamma.is_a?(Proc).should == true
40
+ @c.zeta.is_a?(Proc).should == true
41
+ end
42
+
43
+ it 'curries all methods after a call to auto_curry with no args' do
44
+ @c.harry.is_a?(Proc).should == true
45
+ @c.joseph.is_a?(Proc).should == true
46
+ end
47
+ end
48
+
49
+ describe "currying behaviour" do
50
+ before do
51
+ @c = Class.new do
52
+ include Funkify
53
+
54
+ auto_curry
55
+
56
+ def add(x, y, z)
57
+ x + y + z
58
+ end
59
+ end.new
60
+ end
61
+
62
+ it 'invokes methods normally when all parameters are provided' do
63
+ @c.add(1, 2, 3).should == 6
64
+ end
65
+
66
+ it 'returns a curried Proc when less than required args are given' do
67
+ @c.add.is_a?(Proc).should == true
68
+ @c.add(1).is_a?(Proc).should == true
69
+ @c.add(1, 2).is_a?(Proc).should == true
70
+ end
71
+
72
+ it 'allows curried procs to be completed when the full args are provided successively' do
73
+ @c.add(1).(2).(3).should == 6
74
+ @c.add(1).(2, 3).should == 6
75
+ @c.add(1, 2).(3).should == 6
76
+ end
77
+
78
+ it 'raises an exception when too many args passed to curried Proc' do
79
+ should.raise(ArgumentError) { @c.add(1, 2).(3, 4) }
80
+ end
81
+ end
82
+
83
+ describe "composition" do
84
+ before do
85
+ @c = Class.new do
86
+ include Funkify
87
+
88
+ auto_curry
89
+
90
+ def add(x, y)
91
+ x + y
92
+ end
93
+
94
+ def mult(x, y)
95
+ x * y
96
+ end
97
+
98
+ def negate(x)
99
+ -x
100
+ end
101
+
102
+ def plus_1(x)
103
+ x + 1
104
+ end
105
+ end.new
106
+ end
107
+
108
+ it 'returns a new Proc when composing methods' do
109
+ (@c.negate + @c.plus_1).is_a?(Proc).should == true
110
+ end
111
+
112
+ it 'invokes composed methods in the correct order (right-to-left)' do
113
+ (@c.negate + @c.plus_1).(5).should == -6
114
+ end
115
+
116
+ it 'can compose partially applied methods' do
117
+ (@c.add(5) + @c.mult(2)).(5).should == 15
118
+ end
119
+
120
+ it 'can compose multiple methods' do
121
+ (@c.negate + @c.add(5) + @c.mult(5)).(5).should == -30
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,3 @@
1
+ lib = File.expand_path('../../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'funkify'
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: funkify
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Mair
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bacon
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ description: Haskell-style partial application and composition for Ruby methods
56
+ email:
57
+ - jrmair@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - funkify.gemspec
68
+ - lib/funkify.rb
69
+ - lib/funkify/version.rb
70
+ - spec/funkify_spec.rb
71
+ - spec/spec_helper.rb
72
+ homepage: https://github.com/banister/funkify
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.0.3
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Haskell-style partial application and composition for Ruby methods
96
+ test_files:
97
+ - spec/funkify_spec.rb
98
+ - spec/spec_helper.rb
99
+ has_rdoc: