named_parameters 0.1.0
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/LICENSE +13 -0
- data/README +76 -0
- data/spec/named_parameters_spec.rb +94 -0
- data/src/named_parameters.rb +128 -0
- metadata +77 -0
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2012 Marvin Ede
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
= Named Parameters
|
2
|
+
Makes your methods callable with named parameters
|
3
|
+
|
4
|
+
== Get it
|
5
|
+
Surprisingly it is this easy:
|
6
|
+
|
7
|
+
gem install named_parameters
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
== Use it
|
12
|
+
= First, include NamedParameters:
|
13
|
+
class AnyClass
|
14
|
+
include NamedParameters
|
15
|
+
end
|
16
|
+
|
17
|
+
= Second, call the macro:
|
18
|
+
|
19
|
+
|
20
|
+
class AnyClass
|
21
|
+
include NamedParameters
|
22
|
+
|
23
|
+
named_parameters
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
= Last, but not least, define your method:
|
28
|
+
class AnyClass
|
29
|
+
include NamedParameters
|
30
|
+
|
31
|
+
named_parameters
|
32
|
+
def some_method(a,b,c)
|
33
|
+
#... do something
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
== Call it
|
38
|
+
any_object = AnyClass.new
|
39
|
+
any_object.some_method(a: "here comes the 'a' parameter",
|
40
|
+
c: "this is for c"
|
41
|
+
b: 42)
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
---
|
46
|
+
== Optional parameters
|
47
|
+
You may also define optional parameters with defaults values:
|
48
|
+
class AnyClass
|
49
|
+
named_parameters(a: "default a", b: "default b")
|
50
|
+
def some_method(a,b)
|
51
|
+
[a,b]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
The call is just the same:
|
55
|
+
any_object.some_method(a: "actual a") # => ["actual a", "default b"]
|
56
|
+
any_object.some_method(b: "actual b") # => ["default a", "actual b"]
|
57
|
+
any_object.some_method # => ["default a", "default b"]
|
58
|
+
|
59
|
+
== Optional and mandatory parameters
|
60
|
+
You can have both optional and mandatory parameters for the same method.
|
61
|
+
All paramaters with no default value are mandatory:
|
62
|
+
|
63
|
+
class AnyClass
|
64
|
+
named_parameters(a: "default a")
|
65
|
+
def some_method(a,b,c)
|
66
|
+
[a,b,c]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
any_object.some_method(b: "given",
|
71
|
+
c: nil) # => ["default a", "given", nil]
|
72
|
+
|
73
|
+
any_object.some_method(b: "given",
|
74
|
+
a: "overwritten") # => Error: Mandatory parameter 'c' not given
|
75
|
+
|
76
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require_relative "../src/named_parameters"
|
2
|
+
|
3
|
+
class Book
|
4
|
+
include NamedParameters
|
5
|
+
|
6
|
+
attr_reader :author, :title, :year
|
7
|
+
|
8
|
+
named_parameters
|
9
|
+
|
10
|
+
def initialize author, title, year
|
11
|
+
@author = author
|
12
|
+
@title = title
|
13
|
+
@year = year
|
14
|
+
end
|
15
|
+
|
16
|
+
named_parameters
|
17
|
+
|
18
|
+
def foo bar, baz
|
19
|
+
[bar, baz]
|
20
|
+
end
|
21
|
+
|
22
|
+
named_parameters(foo: "foo", bar: "bar")
|
23
|
+
|
24
|
+
def method_with_defaults_only foo, bar
|
25
|
+
[foo, bar]
|
26
|
+
end
|
27
|
+
|
28
|
+
named_parameters(optional: "optional")
|
29
|
+
|
30
|
+
def method_with_defaults_and_mandatory optional, mandatory
|
31
|
+
[optional, mandatory]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe Book do
|
37
|
+
|
38
|
+
attr_reader :book
|
39
|
+
before :each do
|
40
|
+
@book = Book.new(title: "Enders Game",
|
41
|
+
author: "Orson Scott Card",
|
42
|
+
year: 1992)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
it "should be a book" do
|
47
|
+
book.should be_instance_of Book
|
48
|
+
end
|
49
|
+
|
50
|
+
it "attributes should be readable" do
|
51
|
+
book.title.should == "Enders Game"
|
52
|
+
book.author.should == "Orson Scott Card"
|
53
|
+
book.year.should == 1992
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should accept a second method defined" do
|
57
|
+
book.foo(bar: "bar",
|
58
|
+
baz: "baz").should == ["bar", "baz"]
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should raise an error on false parameters" do
|
62
|
+
-> { book.foo(bar: "bar") }.should raise_error(RuntimeError)
|
63
|
+
-> { book.foo(baz: "baz") }.should raise_error(RuntimeError)
|
64
|
+
-> { book.foo(bar: "bar",
|
65
|
+
baz: "baz",
|
66
|
+
other: "other") }.should raise_error(RuntimeError)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should accept a method with only optional parameters" do
|
70
|
+
#book.method_with_defaults_only.should == ["foo", "bar"]
|
71
|
+
book.method_with_defaults_only(foo: "FOO").should == ["FOO", "bar"]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should not accept inconsistent method definition" do
|
75
|
+
code = -> do
|
76
|
+
class Book
|
77
|
+
some_defaults(a: "a", b: "b")
|
78
|
+
|
79
|
+
def a_method a, c
|
80
|
+
#... do something
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
code.should raise_error(RuntimeError)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should accept a method with mixed parameters" do
|
88
|
+
book.method_with_defaults_and_mandatory(mandatory: "given").should == ["optional", "given"]
|
89
|
+
-> { book.method_with_defaults_and_mandatory(
|
90
|
+
optional: "only optional is given") }.should raise_error(RuntimeError)
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Kernel #:nodoc:
|
2
|
+
# @return [Class] the eigenclass / singletonclass / metaclass of self
|
3
|
+
def eigenclass
|
4
|
+
class << self
|
5
|
+
self
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
#
|
11
|
+
# Include this module to enable the named_parameters macro
|
12
|
+
#
|
13
|
+
module NamedParameters
|
14
|
+
|
15
|
+
# :nodoc:
|
16
|
+
# Makes all methods of this module to class or module methods of include
|
17
|
+
def self.included base
|
18
|
+
base.extend(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
#noinspection RubyArgCount
|
22
|
+
self.class.class_eval do
|
23
|
+
alias :method_added_before_named_parameters :method_added #:nodoc:
|
24
|
+
private :method_added_before_named_parameters
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# @param defaults [Hash] Containing defaults values for optional parameters.
|
29
|
+
# Parameters that are not listet here are mandatory.
|
30
|
+
def named_parameters defaults = { }
|
31
|
+
defaults.empty? ? all_required : some_defaults(defaults)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# [Symbol] is in [:nothing, :all_required, :defaults]
|
37
|
+
@@next_method_modifier = :nothing
|
38
|
+
|
39
|
+
def all_required
|
40
|
+
@@next_method_modifier = :all_required
|
41
|
+
end
|
42
|
+
|
43
|
+
def some_defaults defaults
|
44
|
+
@@next_method_modifier = :defaults
|
45
|
+
@@defaults = defaults
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# @param method_name [Symbol]
|
50
|
+
def method_added(method_name)
|
51
|
+
if @@next_method_modifier == :all_required
|
52
|
+
@@next_method_modifier = :nothing
|
53
|
+
redefine_all_required_method(method_name)
|
54
|
+
elsif @@next_method_modifier == :defaults
|
55
|
+
@@next_method_modifier = :nothing
|
56
|
+
redefine_defaults_method(method_name, @@defaults)
|
57
|
+
@@defaults = nil
|
58
|
+
end
|
59
|
+
method_added_before_named_parameters method_name
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param method_name [Symbol]
|
64
|
+
# @return [Object] Not important
|
65
|
+
def redefine_all_required_method(method_name)
|
66
|
+
method = instance_method method_name
|
67
|
+
parameter_definition = method.parameters.collect { |_, b| b }
|
68
|
+
old_name = (method_name.to_s+"_before_named_parameters").to_sym
|
69
|
+
alias_method old_name, method_name
|
70
|
+
module_eval do
|
71
|
+
define_method method_name do |options|
|
72
|
+
missing = parameter_definition-(options.keys)
|
73
|
+
unless missing.empty?
|
74
|
+
raise RuntimeError.new("Missing arguments: #{missing}")
|
75
|
+
end
|
76
|
+
unexpected = options.keys - parameter_definition
|
77
|
+
unless unexpected.empty?
|
78
|
+
raise RuntimeError.new("Unexpected arguments: #{unexpected}")
|
79
|
+
end
|
80
|
+
parameters = []
|
81
|
+
parameter_definition.each { |parameter| parameters << options[parameter] }
|
82
|
+
eval <<-STRING
|
83
|
+
obj = self
|
84
|
+
method.bind(obj).call *parameters
|
85
|
+
STRING
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param method_name [Symbol]
|
91
|
+
# @param defaults [Hash]
|
92
|
+
# @return [Object] Not important
|
93
|
+
def redefine_defaults_method(method_name, defaults)
|
94
|
+
method = instance_method method_name
|
95
|
+
parameter_definition = method.parameters.collect { |_, b| b }
|
96
|
+
|
97
|
+
defaults.keys.each do |default_key|
|
98
|
+
raise RuntimeError.new("A default value for #{default_key} has been defined,
|
99
|
+
but it is not a parameter of #{method_name}") unless parameter_definition.include? default_key
|
100
|
+
end
|
101
|
+
|
102
|
+
old_name = (method_name.to_s+"_before_named_parameters").to_sym
|
103
|
+
alias_method old_name, method_name
|
104
|
+
module_eval do
|
105
|
+
define_method method_name do |options = { }|
|
106
|
+
unexpected = options.keys - parameter_definition
|
107
|
+
unless unexpected.empty?
|
108
|
+
raise RuntimeError.new("Unexpected arguments: #{unexpected}")
|
109
|
+
end
|
110
|
+
|
111
|
+
parameters = []
|
112
|
+
mandatory = parameter_definition-(defaults.keys)
|
113
|
+
parameter_definition.each do |parameter|
|
114
|
+
if mandatory.member? parameter
|
115
|
+
parameters << options.fetch(parameter) { raise RuntimeError.new("Mandatory parameter #{parameter} not
|
116
|
+
given") }
|
117
|
+
else
|
118
|
+
parameters << options.fetch(parameter, defaults[parameter])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
eval <<-STRING
|
122
|
+
obj = self
|
123
|
+
method.bind(obj).call *parameters
|
124
|
+
STRING
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: named_parameters
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Marvin Ede
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &25860360 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.9.0
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 3.0.0
|
25
|
+
type: :development
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: *25860360
|
28
|
+
description: ! 'By including the NamedParameters module into your class/module the
|
29
|
+
''named_parameters'' macro is
|
30
|
+
|
31
|
+
available. Optional parameters with default values can be as easily defined as mandatory
|
32
|
+
parameters. Your actual
|
33
|
+
|
34
|
+
method definition does not change at all'
|
35
|
+
email: marvinede@gmx.net
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files:
|
39
|
+
- README
|
40
|
+
- LICENSE
|
41
|
+
files:
|
42
|
+
- src/named_parameters.rb
|
43
|
+
- README
|
44
|
+
- LICENSE
|
45
|
+
- spec/named_parameters_spec.rb
|
46
|
+
homepage: http://rubygems.org/gems/named_parameters
|
47
|
+
licenses:
|
48
|
+
- apache 2.0
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --title
|
52
|
+
- Named Parameters
|
53
|
+
- --main
|
54
|
+
- README
|
55
|
+
- --line_numbers
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.9.0
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 1.8.17
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Makes your methods callable with named parameters
|
76
|
+
test_files:
|
77
|
+
- spec/named_parameters_spec.rb
|