curryer 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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +43 -0
- data/Rakefile +1 -0
- data/curryer.gemspec +23 -0
- data/doctest_helper.rb +11 -0
- data/lib/curryer.rb +100 -0
- data/lib/curryer/version.rb +3 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f816271ae0ce4b1eb55f721b662461027ea4a630
|
4
|
+
data.tar.gz: ecc75dfcdedebc1c45002b810aca77bbc718f88d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a43f9d75ba1b13b46b61f4c73dec7e8cf0bfa88577d7d7a2c82727f186349ef9c97bde0f48d8ad1d1e66e32ea007b43583a8dfd8ee7301e154cead7001d01fe3
|
7
|
+
data.tar.gz: a64132ec3f39e863164bfa19d1409e524362a78f63486322f04f7bba635c41c799a0b7ace927ecd5dda7e7bbaa03ff76eb4aeff7b824fc2f7652a909f7b9219e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Andrew O'Brien
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Curryer
|
2
|
+
|
3
|
+
The Delegator that curries!
|
4
|
+
|
5
|
+
There are often times where you have a module or class with many methods that
|
6
|
+
share the same initial arguments and you'd like to just set them once. Curryer
|
7
|
+
provides a method that allows you to generate an object that saves those first
|
8
|
+
couple arguments as its own state, while exposing the target's methods (with a
|
9
|
+
lower arity).
|
10
|
+
|
11
|
+
In functional programming, this is called "currying" or "partial evaluation".
|
12
|
+
Ruby supports this on the Proc level with Proc#curry. Currying is an important
|
13
|
+
concept and indeed serves as the theoretical basis for how computation works.
|
14
|
+
In fact, object-oriented programming is actually nothing by the combination of
|
15
|
+
structures/records/tuples and partially evaluated functions (see the second
|
16
|
+
example in lib/curryer if you don't believe me).
|
17
|
+
|
18
|
+
## Running tests
|
19
|
+
|
20
|
+
This library uses Ruby-Doctest. After running `bundle install`, you can run
|
21
|
+
tests using `bundle exec rubydoctest lib/*.rb`.
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Add this line to your application's Gemfile:
|
26
|
+
|
27
|
+
gem 'curryer'
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install curryer
|
36
|
+
|
37
|
+
## Contributing
|
38
|
+
|
39
|
+
1. Fork it
|
40
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
41
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
42
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
43
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/curryer.gemspec
ADDED
@@ -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 'curryer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "curryer"
|
8
|
+
spec.version = Curryer::VERSION
|
9
|
+
spec.authors = ["Andrew O'Brien"]
|
10
|
+
spec.email = ["obrien.andrew@gmail.com"]
|
11
|
+
spec.description = %q{Currying + Delegation = Curryer}
|
12
|
+
spec.summary = %q{Allows you to easily wrap a target object with common arguments.}
|
13
|
+
spec.homepage = "https://github.com/AndrewO/curryer"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/doctest_helper.rb
ADDED
data/lib/curryer.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require "curryer/version"
|
2
|
+
require "delegate"
|
3
|
+
|
4
|
+
module Curryer
|
5
|
+
# @param target [Object] Object whose methods will be wrapped in curried.
|
6
|
+
# versions
|
7
|
+
# @param *args [Array] The first n arguments of every wrapped method.
|
8
|
+
# @returns Curried A delegator with a set of arguments that will be used in
|
9
|
+
# every method call to its delegate.
|
10
|
+
#
|
11
|
+
# doctest_require: "../doctest_helper"
|
12
|
+
# doctest: Wrap an instantiated object.
|
13
|
+
# >> foo = Foo.new
|
14
|
+
# >> curried = Curryer.for_all(foo, 1, 2)
|
15
|
+
# >> curried.do_stuff(3)
|
16
|
+
# => 0
|
17
|
+
# >> curried.do_stuff(1)
|
18
|
+
# => 2
|
19
|
+
#
|
20
|
+
# doctest: Wrap a module with state.
|
21
|
+
# >> person = Curryer.for_all(FakeOOP, "Andrew", "O'Brien")
|
22
|
+
# >> person.full_name
|
23
|
+
# => "Andrew O'Brien"
|
24
|
+
#
|
25
|
+
# doctest: Raises ArgumentError when too many things are passed in.
|
26
|
+
# >> person = Curryer.for_all(FakeOOP, "Andrew", "O'Brien")
|
27
|
+
# >> begin; person.full_name("Jr."); rescue => e; end
|
28
|
+
# >> e.class
|
29
|
+
# => ArgumentError
|
30
|
+
#
|
31
|
+
# Look, ma! Object Oriented Programming is just degenerate Functional
|
32
|
+
# Programming!
|
33
|
+
def self.for_all(target, *args)
|
34
|
+
Curried.new(target, args)
|
35
|
+
end
|
36
|
+
|
37
|
+
# DO NOT USE. Not implemented.
|
38
|
+
#
|
39
|
+
# If this is actually needed, it opens up all sorts of questions like:
|
40
|
+
#
|
41
|
+
# 1. Should we have a CurriedClass (like DelegateClass) that defined methods
|
42
|
+
# instead of using method_missing?
|
43
|
+
# 2. Should we make a mixin that allows a compile time specification of
|
44
|
+
# currying (think Forwardable instead of Delegate)?
|
45
|
+
#
|
46
|
+
# These are some interesting routes to go down, are probably pretty easy to
|
47
|
+
# implement, and are valid use cases. I just don't have them right now. If
|
48
|
+
# you do, let me know.
|
49
|
+
def self.for_methods(object, methods, *args)
|
50
|
+
raise "Not implemented. Do you actually need this? " +
|
51
|
+
"Or should the caller switch between the un-curried object and its curried version?"
|
52
|
+
end
|
53
|
+
|
54
|
+
class Curried < Delegator
|
55
|
+
def initialize(target, args)
|
56
|
+
@target, @args = target, args
|
57
|
+
reset_cache!
|
58
|
+
end
|
59
|
+
|
60
|
+
def __get_obj__
|
61
|
+
@target
|
62
|
+
end
|
63
|
+
|
64
|
+
def __set_obj__(new_target)
|
65
|
+
reset_cache!
|
66
|
+
@target = new_target
|
67
|
+
end
|
68
|
+
|
69
|
+
# See STLIB's delegate.rb. This is mostly just an adaption of what the
|
70
|
+
# base Delegate#method_missing is doing.
|
71
|
+
def method_missing(m, *local_args, &b)
|
72
|
+
target = self.__get_obj__
|
73
|
+
|
74
|
+
# Get the method, and put a partially-evaluated version of it in the cache
|
75
|
+
# so that we don't have to re-curry future calls. Then call it with the
|
76
|
+
# local args.
|
77
|
+
if target.respond_to?(m)
|
78
|
+
result = @cache[m] ||= target.method(m).to_proc.curry[*@args]
|
79
|
+
if local_args.empty?
|
80
|
+
result
|
81
|
+
else
|
82
|
+
if result.respond_to?(:call)
|
83
|
+
result.call(*local_args)
|
84
|
+
else
|
85
|
+
raise ArgumentError, \
|
86
|
+
"Curried result #{result.inspect} is not callable but arguments #{local_args} were still given. " +
|
87
|
+
"You're trying to call the method with more arguments than it takes."
|
88
|
+
end
|
89
|
+
end
|
90
|
+
else
|
91
|
+
super(m, *local_args, &b)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def reset_cache!
|
97
|
+
@cache = {}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: curryer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew O'Brien
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-21 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
|
+
description: Currying + Delegation = Curryer
|
42
|
+
email:
|
43
|
+
- obrien.andrew@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- curryer.gemspec
|
54
|
+
- doctest_helper.rb
|
55
|
+
- lib/curryer.rb
|
56
|
+
- lib/curryer/version.rb
|
57
|
+
homepage: https://github.com/AndrewO/curryer
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.2.1
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Allows you to easily wrap a target object with common arguments.
|
81
|
+
test_files: []
|