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 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