argible 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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(:value => :to_i)
47
- def action_four(value)
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* is annotated with the method *argible*. When the action_one action method is executed
53
- Argible will intercept the call and look up the request parameter value associated with *alpha*. Then action_one will
54
- be called with this resolved value.
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 second action *action_two* provides an example of how more complex processing can occur on argument values. As in
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 *action_three* provides an example of using a Proc (lambda) as alternative to a named method.
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 fourth example action *action_four* illustrates how you can call methods directly on the String value returned from
64
- the request parameter
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:
@@ -1,41 +1,8 @@
1
1
  require 'parse_tree'
2
2
 
3
- module Argible
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
- class UnknownArgumentError < ArgumentError
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
- # Callback to alert Argible of the method being annotated
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
- raise UnknownArgumentError.new(self.class, method_definition.name, key)
131
- end
132
-
133
- if value.is_a?(Method) or value.is_a?(Proc)
134
- arguments[key] = value.call(arguments[key])
135
- elsif value.is_a?(Symbol)
136
- arguments[key] = arguments[key].send(value)
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
@@ -0,0 +1,8 @@
1
+ module Argible # :nodoc:
2
+ module VERSION # :nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 1
6
+ STRING = [MAJOR, MINOR, TINY].join('.')
7
+ end
8
+ end
@@ -130,11 +130,24 @@ describe ::Argible, "proxied methods" do
130
130
  foobar.echo.should == 1985
131
131
  end
132
132
 
133
- end
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
- # TODO might look at supporting arguments:
137
- # :four => :@four, # set instance variable
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.0
7
- date: 2007-08-20 00:00:00 -05:00
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