argible 0.1.0 → 0.1.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.
- data/README +29 -17
- data/lib/argible.rb +23 -60
- data/lib/argible/argument_resolver.rb +24 -0
- data/lib/argible/method_definition.rb +17 -0
- data/lib/argible/unknown_argument_error.rb +17 -0
- data/lib/argible/version.rb +8 -0
- data/spec/argible_spec.rb +16 -3
- metadata +6 -2
data/README
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
= ARGIBLE
|
|
1
|
+
= ARGIBLE: A dynamic method argument resolver for Ruby
|
|
2
2
|
|
|
3
3
|
by {Michael Ward}[http://m2ward.blogspot.com]
|
|
4
4
|
|
|
@@ -29,39 +29,51 @@ Have a look at the following Controller:
|
|
|
29
29
|
class FoobarController < ApplicationController
|
|
30
30
|
|
|
31
31
|
argible
|
|
32
|
-
def action_one(alpha)
|
|
32
|
+
def action_one(alpha, beta)
|
|
33
33
|
...
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
argible(:date => Date.method(:parse))
|
|
37
|
-
def action_two(date)
|
|
36
|
+
argible(:date => Date.method(:parse)) # process argument value for 'date' by passing it to Date#parse
|
|
37
|
+
def action_two(alpha, date)
|
|
38
38
|
...
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
argible(:date => lambda {|v| Date.parse(v) } )
|
|
41
|
+
argible(:date => lambda {|v| Date.parse(v) } ) # process argument value for 'date' by passing it to the Proc
|
|
42
42
|
def action_three(date)
|
|
43
43
|
...
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
argible(:
|
|
47
|
-
def action_four(
|
|
46
|
+
argible(:count => :to_i) # invoke the :to_i method on the resolved value for 'count'
|
|
47
|
+
def action_four(count)
|
|
48
|
+
...
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
argible(:the_name => :@first_name) # resolves the value for 'the_name' and sets it to the instance variable @first_name
|
|
52
|
+
def action_five
|
|
53
|
+
p @first_name # this instance variable has been set automatically by Argible
|
|
48
54
|
...
|
|
49
55
|
end
|
|
50
56
|
end
|
|
51
57
|
|
|
52
|
-
The first method *action_one
|
|
53
|
-
Argible will intercept the call and look up the request parameter value associated with
|
|
54
|
-
be called with
|
|
58
|
+
The first method, *action_one*, is annotated with the method +argible+. When this action method is executed
|
|
59
|
+
Argible will intercept the call and look up the request parameter value associated with the argument names +alpha+ and
|
|
60
|
+
+beta+. Then the action_one method will be called with these resolved values.
|
|
61
|
+
|
|
62
|
+
The second action, *action_two*, provides an example of how more complex processing can occur on argument
|
|
63
|
+
values. As in the first example the both arguments, +alpha+ and +date+, will be resolved against the request parameters.
|
|
64
|
+
However the resolved value for +date+ will then be passed to the <tt>Date#parse</tt> method. The returned value from
|
|
65
|
+
<tt>Date#parse</tt> method call will then be used as the argument value for +date+ when +action_two+ is called. **NOTE:** any
|
|
66
|
+
method can be used, Date#parse is simply used as an example.
|
|
55
67
|
|
|
56
|
-
The
|
|
57
|
-
the first example the argument *date* is resolved against the request parameters. This value will then be passed to the
|
|
58
|
-
Date#parse method. The result from Date#parse will then be used when *action_two* is called. **NOTE:** any method can
|
|
59
|
-
be used, Date#parse is simply used as an example.
|
|
68
|
+
The third example action, *action_three*, provides an example of using a Proc (lambda) as alternative to a named method.
|
|
60
69
|
|
|
61
|
-
The action *
|
|
70
|
+
The fourth example action, *action_four*, illustrates how you can call methods directly on the String value
|
|
71
|
+
returned from the request parameter. Assume request parameter hash was <tt>{'count' => "1985"}</tt> then the Integer value
|
|
72
|
+
+1985+ would be passed as the argument value for the argument named <tt>'count'</tt>.
|
|
62
73
|
|
|
63
|
-
The
|
|
64
|
-
the
|
|
74
|
+
The final example action, *action_five*, demonstrates how you can automatically set the value of an instance variable
|
|
75
|
+
without requiring a method argument of the same name. This example works because <tt>:my_name</tt> points to a symbol
|
|
76
|
+
begining with a '@' character. Without this '@' on the right hand side Argible would raise a Argible::UnknownArgumentError.
|
|
65
77
|
|
|
66
78
|
|
|
67
79
|
== LICENSE:
|
data/lib/argible.rb
CHANGED
|
@@ -1,41 +1,8 @@
|
|
|
1
1
|
require 'parse_tree'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
# defines information about the method being annotated
|
|
6
|
-
class MethodDefinition
|
|
7
|
-
attr_accessor :name, :options
|
|
8
|
-
attr_reader :arguments
|
|
9
|
-
|
|
10
|
-
def initialize(options)
|
|
11
|
-
@options = options
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def arguments=(arguments)
|
|
15
|
-
@arguments = arguments.select { |arg| arg.is_a?(Symbol) } # Only grab argument names
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class ArgumentResolver
|
|
20
|
-
class << self; attr_accessor :resolver; end
|
|
21
|
-
|
|
22
|
-
# By default resolves argument values from request parameters
|
|
23
|
-
@resolver = lambda do |object, argument_name|
|
|
24
|
-
return object.send(:params)[argument_name.to_s]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def self.resolve(object, argument_name)
|
|
28
|
-
return @resolver.call(object, argument_name)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
3
|
+
Dir[File.join(File.dirname(__FILE__), 'argible/**/*.rb')].sort.each { |lib| require lib }
|
|
31
4
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def initialize(class_name, method_name, key)
|
|
35
|
-
super("#{class_name}##{method_name}' does not include an argument named: #{key}" )
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
end
|
|
5
|
+
module Argible
|
|
39
6
|
|
|
40
7
|
def self.included(base)
|
|
41
8
|
base.extend(ClassMethods) # Adding class methods
|
|
@@ -51,11 +18,20 @@ module Argible
|
|
|
51
18
|
# ...
|
|
52
19
|
# end
|
|
53
20
|
#
|
|
21
|
+
# The +options+ Hash passed in can define custom processing to occur on a per argument basis. For example:
|
|
22
|
+
#
|
|
23
|
+
# argible(
|
|
24
|
+
# :one => :to_i, # call method 'to_i' on the argument value
|
|
25
|
+
# :two => lambda{ |value| return value }, # defines a Proc to run (resolved argumnet value passed to Proc)
|
|
26
|
+
# :three => method(:meth_name), # method to call (resolved argumnet value passed to Method)
|
|
27
|
+
# :four => :@four # argument value is set to the instance variable of name (i.e. @four)
|
|
28
|
+
# )
|
|
29
|
+
#
|
|
54
30
|
def argible(options = {})
|
|
55
31
|
@method_definition = MethodDefinition.new(options)
|
|
56
32
|
end
|
|
57
33
|
|
|
58
|
-
#
|
|
34
|
+
# This callback is used by Argible to determine which methods have been annotated with the argible() method.
|
|
59
35
|
def method_added(method_name)
|
|
60
36
|
|
|
61
37
|
unless @method_definition.nil?
|
|
@@ -82,18 +58,6 @@ module Argible
|
|
|
82
58
|
ParseTree.new.parse_tree_for_method(self, method_name)[2][1][1][1..-1]
|
|
83
59
|
end
|
|
84
60
|
|
|
85
|
-
# 1. Determine all arguments
|
|
86
|
-
# 1. Resolve argument values
|
|
87
|
-
# 2. Process arguments
|
|
88
|
-
# A. method to call on value
|
|
89
|
-
# B. pass to Proc
|
|
90
|
-
# B. pass to Method
|
|
91
|
-
#
|
|
92
|
-
# argible(
|
|
93
|
-
# :one => :to_i, # call method on argument value
|
|
94
|
-
# :two => lambda{ |value| return value }, # Proc to run
|
|
95
|
-
# :three => method(:alt_name) # method to call
|
|
96
|
-
#)
|
|
97
61
|
def build_alias_method(method_name)
|
|
98
62
|
original_method = "__argible_#{method_name}".to_sym
|
|
99
63
|
alias_method(original_method, method_name)
|
|
@@ -119,21 +83,20 @@ module Argible
|
|
|
119
83
|
|
|
120
84
|
private
|
|
121
85
|
|
|
122
|
-
# responsible for resolving argument value
|
|
123
|
-
def resolve_argument(name)
|
|
124
|
-
return params[name.to_s]
|
|
125
|
-
end
|
|
126
|
-
|
|
127
86
|
def process_argible_options(method_definition, arguments)
|
|
128
87
|
method_definition.options.each do |key, value|
|
|
129
88
|
unless arguments.include?(key) # only process key if it exists in the method argument list
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
89
|
+
if(value.is_a?(Symbol) and value.to_s =~ /^@/)
|
|
90
|
+
self.instance_variable_set(value, ArgumentResolver.resolve(self, key)) # set instance variable
|
|
91
|
+
else
|
|
92
|
+
raise UnknownArgumentError.new(self.class, method_definition.name, key)
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
if value.is_a?(Method) or value.is_a?(Proc)
|
|
96
|
+
arguments[key] = value.call(arguments[key])
|
|
97
|
+
elsif value.is_a?(Symbol) # method to be invoked on value
|
|
98
|
+
arguments[key] = arguments[key].send(value)
|
|
99
|
+
end
|
|
137
100
|
end
|
|
138
101
|
end
|
|
139
102
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Argible
|
|
2
|
+
|
|
3
|
+
# This class defines the method/proc that will be called to resolve an arguments value. By default this
|
|
4
|
+
# implementation will resolve an arguments value through RoR's request parameters. It is possible to plug-in an
|
|
5
|
+
# alternate resolver implementation like so (in the example all arguments values will resolve to the value 'foobar'):
|
|
6
|
+
#
|
|
7
|
+
# ::Argible::ArgumentResolver.resolver = lambda do |instance, argument_name|
|
|
8
|
+
# return "foobar"
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
class ArgumentResolver
|
|
12
|
+
class << self; attr_accessor :resolver; end
|
|
13
|
+
|
|
14
|
+
# By default resolves argument values from request parameters
|
|
15
|
+
@resolver = lambda do |object, argument_name|
|
|
16
|
+
return object.send(:params)[argument_name.to_s]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.resolve(object, argument_name)
|
|
20
|
+
return @resolver.call(object, argument_name)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Argible
|
|
2
|
+
|
|
3
|
+
# defines information about the method being annotated
|
|
4
|
+
class MethodDefinition # :nodoc:
|
|
5
|
+
attr_accessor :name, :options
|
|
6
|
+
attr_reader :arguments
|
|
7
|
+
|
|
8
|
+
def initialize(options)
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def arguments=(arguments)
|
|
13
|
+
@arguments = arguments.select { |arg| arg.is_a?(Symbol) } # Only grab argument names
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Argible
|
|
2
|
+
|
|
3
|
+
# As the name implies, this error occurs when an argible annotated method references an argument name that does NOT
|
|
4
|
+
# exist in the method signature.
|
|
5
|
+
#
|
|
6
|
+
# argible(:junk => :to_i)
|
|
7
|
+
# def my_method(arg1, arg2); end;
|
|
8
|
+
#
|
|
9
|
+
class UnknownArgumentError < ArgumentError
|
|
10
|
+
|
|
11
|
+
def initialize(class_name, method_name, key)
|
|
12
|
+
super("#{class_name}##{method_name}' does not include an argument named: #{key}" )
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
data/spec/argible_spec.rb
CHANGED
|
@@ -130,11 +130,24 @@ describe ::Argible, "proxied methods" do
|
|
|
130
130
|
foobar.echo.should == 1985
|
|
131
131
|
end
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
it "should be able to directly set instance variable (even without associated argument name)" do
|
|
134
|
+
|
|
135
|
+
class FooBar
|
|
136
|
+
|
|
137
|
+
argible(:name => :@name)
|
|
138
|
+
def change_name
|
|
139
|
+
return @name
|
|
140
|
+
end
|
|
141
|
+
end
|
|
134
142
|
|
|
143
|
+
foobar = FooBar.new
|
|
144
|
+
foobar.should_receive(:params).and_return('name' => "Regan")
|
|
135
145
|
|
|
136
|
-
|
|
137
|
-
|
|
146
|
+
foobar.change_name
|
|
147
|
+
foobar.instance_variable_get(:@name).should == "Regan" # instance variable set correctly
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
138
151
|
|
|
139
152
|
# TODO might want to handle argument default values (i.e. foo(bar=99, baz={}) )
|
|
140
153
|
|
metadata
CHANGED
|
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
|
3
3
|
specification_version: 1
|
|
4
4
|
name: argible
|
|
5
5
|
version: !ruby/object:Gem::Version
|
|
6
|
-
version: 0.1.
|
|
7
|
-
date: 2007-08-
|
|
6
|
+
version: 0.1.1
|
|
7
|
+
date: 2007-08-22 00:00:00 -05:00
|
|
8
8
|
summary: automatically resolves method argument values
|
|
9
9
|
require_paths:
|
|
10
10
|
- lib
|
|
@@ -29,6 +29,10 @@ post_install_message:
|
|
|
29
29
|
authors:
|
|
30
30
|
- Michael Ward
|
|
31
31
|
files:
|
|
32
|
+
- lib/argible/argument_resolver.rb
|
|
33
|
+
- lib/argible/method_definition.rb
|
|
34
|
+
- lib/argible/unknown_argument_error.rb
|
|
35
|
+
- lib/argible/version.rb
|
|
32
36
|
- lib/argible.rb
|
|
33
37
|
- spec/argible_spec.rb
|
|
34
38
|
- spec/spec_helper.rb
|