named_arguments 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 1.0.0 / 2007-06-03
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
data/Manifest.txt ADDED
@@ -0,0 +1,10 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/named_arguments.rb
6
+ lib/named_arguments/class_settings_mixin.rb
7
+ lib/named_arguments/hash_extended_tools.rb
8
+ lib/named_arguments/method_extensions.rb
9
+ test/unit/named_arguments_test.rb
10
+ test/test_helper.rb
data/README.txt ADDED
@@ -0,0 +1,48 @@
1
+ namedarguments
2
+ by FIX (your name)
3
+ FIX (url)
4
+
5
+ == DESCRIPTION:
6
+
7
+ FIX (describe your package)
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * FIX (list of features or problems)
12
+
13
+ == SYNOPSIS:
14
+
15
+ FIX (code sample of usage)
16
+
17
+ == REQUIREMENTS:
18
+
19
+ * FIX (list of requirements)
20
+
21
+ == INSTALL:
22
+
23
+ * FIX (sudo gem install, anything else)
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2007 FIX
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/named_arguments.rb'
6
+
7
+ Hoe.new('named_arguments', NamedArguments::VERSION) do |p|
8
+ p.rubyforge_name = 'named_arguments'
9
+ # p.author = 'FIX'
10
+ # p.email = 'FIX'
11
+ # p.summary = 'FIX'
12
+ # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
13
+ # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
15
+ end
16
+
17
+ # vim: syntax=Ruby
@@ -0,0 +1,258 @@
1
+ require File.dirname(__FILE__) + '/named_arguments/method_extensions'
2
+ require File.dirname(__FILE__) + '/named_arguments/hash_extended_tools'
3
+ require File.dirname(__FILE__) + '/named_arguments/class_settings_mixin'
4
+
5
+ # Adds the following features to a class:
6
+ #
7
+ # * Pass a hash to new and matching attributes
8
+ # will be set.
9
+ # * Set default values for arguments (attribute_defaults)
10
+ # * Require arguments (required_fields)
11
+ # * Require kind_of? tests for arguments (required_kind_of)
12
+ # * Change the type of the object passed in (type_converter)
13
+ #
14
+ # =Sample
15
+ #
16
+ # class Snark
17
+ # include NamedArguments
18
+ #
19
+ # attr_accessor :color, :size, :name
20
+ #
21
+ # attribute_defaults :color => 'blue', :size => lambda {|s| some_method_call(s)}
22
+ # required_fields :color, :name
23
+ # required_respond_to :name => :to_s
24
+ # required_kind_of? :size => Fixnum
25
+ # type_converter :size => Fixnum
26
+ # end
27
+ #
28
+ # = See also
29
+ #
30
+ # See ClassMethods for more methods:
31
+ #
32
+ # * ClassMethods#type_converter
33
+ # * ClassMethods#option_attr
34
+ module NamedArguments
35
+ VERSION = '0.0.4'
36
+
37
+ def self.included target # :nodoc:
38
+ target.class_eval do
39
+ include HashExtendedTools
40
+ include ClassSettingsMixin
41
+ extend ClassMethods
42
+ create_class_settings_method :required_fields
43
+ create_class_settings_method :required_kind_of
44
+ create_class_settings_method :required_respond_to
45
+ create_class_settings_method :attribute_defaults
46
+ end
47
+ end
48
+
49
+ # Requires that object.snark.kind_of? String be true
50
+ # on the call to initialize.
51
+ #
52
+ # Throws a NamedArgumentException if that test fails.
53
+ #
54
+ # required_kind_of :snark => String, :boojum => Fixnum
55
+ def required_kind_of
56
+ # Dummy for rdoc
57
+ end
58
+
59
+ # Requires that the given arguments be present on the
60
+ # call to new.
61
+ #
62
+ # required_fields [:snark, :boojum]
63
+ #
64
+ # or
65
+ #
66
+ # required_fields :snark
67
+ def required_fields
68
+ # Dummy for rdoc
69
+ end
70
+
71
+ # Requires that the given objects respond to the
72
+ # method call.
73
+ #
74
+ # required_respond_to :snark => 'hunt'
75
+ #
76
+ # Raises a NamedArgumentException on failure.
77
+ def required_respond_to
78
+ # Dummy for rdoc
79
+ end
80
+
81
+ # Set defaults for the given attributes.
82
+ #
83
+ # Values can be:
84
+ #
85
+ # * A class. The #new method is called with no arguments.
86
+ # * A Proc. The proc is called with one argument.
87
+ # * []. A new array is created.
88
+ # * {}. A new hash is created.
89
+ #
90
+ # attribute_defaults :snark => lambda {|s| String.new s.to_s}
91
+ # attribute_defaults :snark => [], :boojum => ObjectTypeFromSomewhereElse
92
+ # attribute_defaults :snark => {}
93
+ def attribute_defaults
94
+ # Dummy for rdoc
95
+ end
96
+
97
+ # Set the attributes for this object. Normally called
98
+ # by initialize.
99
+
100
+ def attributes_set(args) # :nodoc:
101
+ set_default_attributes(args)
102
+
103
+ args_plus_defaults = (attribute_defaults? || {}).merge(args)
104
+ check_required_field args_plus_defaults
105
+ check_required_kind
106
+ check_required_respond_to
107
+ end
108
+
109
+ def set_default_attributes(args) # :nodoc:
110
+ defaults = {}
111
+ (attribute_defaults? || {}).each_pair do |k, v|
112
+ if Class === v
113
+ result = v.new
114
+ elsif Proc === v
115
+ result = v.call self
116
+ elsif v.class == Array and v.empty?
117
+ result = Array.new
118
+ elsif v.class == Hash and v.empty?
119
+ result = Hash.new
120
+ else
121
+ result = v
122
+ end
123
+ defaults[k] = result
124
+ end
125
+ args = defaults.merge args
126
+ args.each_pair do
127
+ |k, v|
128
+ m = "#{k}="
129
+ send m, v if respond_to? m
130
+ end
131
+ end
132
+
133
+ # An array of field names. When an object is created
134
+ # all of these attributes must be passed. (They can be
135
+ # set to nil)
136
+ #
137
+ # Note that this can only be called as a class method.
138
+
139
+ # A hash of :fieldname => klass pairs that specify the
140
+ # required class of each of the attributes.
141
+
142
+ # Checks to make sure all the required fields
143
+ # are passed in.
144
+ #
145
+ # See also: required_fields
146
+
147
+ def check_required_field(fields_set) # :nodoc:
148
+ (required_fields? || []).each do |f|
149
+ raise ParameterRequired, "Must set parameter: " + f.to_s unless fields_set.has_key? f.to_sym
150
+ end
151
+ end
152
+
153
+ # Checks to make sure all the fields specifed in required_kind_of
154
+ # have the right kind of objects.
155
+ #
156
+ # See also:
157
+ # * #required_kind_of
158
+ # * #set_default_attributes
159
+
160
+ def check_required_kind # :nodoc:
161
+ (required_kind_of? || {}).each_pair do |k, v|
162
+ raise WrongClass.new("Wrong class: #{k.to_s}; should have been #{v.to_s}, object is #{self.send(k).inspect}") unless v === self.send(k)
163
+ end
164
+ end
165
+
166
+ def check_required_respond_to # :nodoc:
167
+ (required_respond_to? || {}).each_pair do |k, v|
168
+ raise MustRespondTo.new("#{k} must respond to #{v}; the object is #{self.send(k).inspect}") unless self.send(k).respond_to?(v)
169
+ end
170
+ end
171
+
172
+ # For every key/value pair in +args+, set the
173
+ # value of the attribute +key+ to +value+.
174
+ #
175
+ # class Snark
176
+ # include NamedArguments
177
+ # attr_accessor :boojum
178
+ # end
179
+ #
180
+ # s = Snark.new :boojum => 7
181
+ def initialize args = {}
182
+ if kind_of? ActiveRecord::Base
183
+ super
184
+ else
185
+ super()
186
+ end
187
+ attributes_set args
188
+ end
189
+
190
+ def option_attr_get k # :nodoc:
191
+ option_attr_storage[k]
192
+ end
193
+
194
+ def option_attr_set k, v # :nodoc:
195
+ option_attr_storage[k] = v
196
+ end
197
+
198
+ def option_attr_storage # :nodoc:
199
+ self.options ||= {}
200
+ end
201
+
202
+ def option_attrs_keys # :nodoc:
203
+ option_attr_storage.keys
204
+ end
205
+
206
+ def option_attrs_keys_set # :nodoc:
207
+ option_attrs_keys.select do |k|
208
+ option_attr_storage.include? k
209
+ end
210
+ end
211
+
212
+ # Class methods (methods of the class
213
+ # object itself) provided when
214
+ # NamedArguments in included.
215
+ module ClassMethods
216
+ def option_attr *array_of_names
217
+ array_of_names.each { |n|
218
+ define_method n, lambda {
219
+ option_attr_get n
220
+ }
221
+ define_method "#{n}=", lambda { |v|
222
+ option_attr_set n, v
223
+ }
224
+ }
225
+ end
226
+
227
+ def type_converter field, new_klass
228
+ alias_name = ('attribute_setter_for_' + field.to_s).to_sym
229
+ setter_method_name = (field.to_s + '=').to_sym
230
+ alias_method alias_name, setter_method_name
231
+ send :define_method, setter_method_name do |rhs|
232
+ if Integer === new_klass
233
+ v = rhs.to_i
234
+ elsif new_klass == String
235
+ v = rhs.to_s
236
+ elsif new_klass == Symbol
237
+ v = rhs.to_sym
238
+ elsif new_klass == :boolean
239
+ v = !!rhs
240
+ elsif Proc === new_klass
241
+ v = new_klass.call v
242
+ else
243
+ v = new_klass.new rhs
244
+ end
245
+ self.send alias_name, v
246
+ end
247
+ end
248
+ end
249
+
250
+ class Error < RuntimeError; end
251
+ class ParameterRequired < Error; end
252
+ class WrongClass < Error; end
253
+ class MustRespondTo < Error; end
254
+ end
255
+
256
+ # If Rails isn't around, fake ActiveRecord
257
+ module ActiveRecord; end;
258
+ class ActiveRecord::Base; end
@@ -0,0 +1,122 @@
1
+ module NamedArguments
2
+ module ClassSettingsMixin
3
+ def self.included target
4
+ target.class_eval do
5
+ include NamedArguments::MethodExtensions
6
+ extend ClassMethods
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+ include NamedArguments::MethodExtensions
12
+
13
+ def self.included target
14
+ target.class_eval do
15
+ include NamedArguments::MethodExtensions
16
+ end
17
+ end
18
+
19
+ # Allow you to define
20
+ # methods that set a class value hash
21
+ # and an accessor for that hash.
22
+ #
23
+ # Example:
24
+ #
25
+ # create_class_settings_method :settings
26
+ #
27
+ # creates:
28
+ #
29
+ # settings:: A class method that allows
30
+ # you to set variables
31
+ # settings?:: The current value of those variables
32
+ #
33
+ # class BlueCar
34
+ # create_class_settings_method :settings
35
+ # create_class_settings_method :has_these
36
+ #
37
+ # settings :color => :blue, :another_settting => 10
38
+ # settings :painted => true
39
+ # has_these :doors, :windows
40
+ # has_these :wheels
41
+ # end
42
+ #
43
+ # class Convertable < BlueCar
44
+ # has_these :poptop
45
+ # end
46
+ #
47
+ # BlueCar.color
48
+ # => :blue
49
+ #
50
+ # BlueCar.new.settings?
51
+ # => {:color => :blue, :painted => true, :another_settting => 10}
52
+ def create_class_settings_method name
53
+ # Build the class methods first
54
+ l = class_setting_lambda name
55
+ define_method_with_context name, &l
56
+
57
+ # Allow #name? to be called as an instance method
58
+ # and default its return value to nil.
59
+ # This will be replaced on any call to the
60
+ # setter.
61
+ value_name = value_field_identifier(name)
62
+ instance_method = lambda {nil}
63
+ define_method value_name, instance_method
64
+ end
65
+
66
+ protected
67
+
68
+ # Returns a lambda that will create
69
+ # two methods:
70
+ #
71
+ # A singleton method on the current object
72
+ # that takes multiple parameters and
73
+ # stores their values. This allows
74
+ # you to define methods on the class
75
+ # that will save their parameters.
76
+ # An instance method that returns those
77
+ # saved parameters
78
+ def class_setting_lambda name #:nodoc:
79
+ value_field_id = value_field_identifier name
80
+ result = lambda do
81
+ # Note that this lambda is called
82
+ # either with a single argument
83
+ # containing a hash, or an array
84
+ # of arguments.
85
+ |*args|
86
+
87
+ if args.first.kind_of? Hash
88
+ args = args.first
89
+ end
90
+
91
+ begin
92
+ current_value = self.send value_field_id
93
+ case current_value
94
+ when Array then val = merge_arrays_of_symbols current_value, args
95
+ when Hash then val = current_value.merge args
96
+ else
97
+ raise RuntimeError.new("Attempting to merge two different kinds of data")
98
+ end
99
+ rescue NoMethodError
100
+ val = args
101
+ end
102
+
103
+ # Define the class method
104
+ define_method_with_value value_field_id, val
105
+
106
+ # Define the instance method
107
+ define_method value_field_id, lambda {val}
108
+ end
109
+ result
110
+ end
111
+
112
+ def value_field_identifier name #:nodoc:
113
+ return "#{name}?"
114
+ end
115
+
116
+ def merge_arrays_of_symbols a, b #:nodoc:
117
+ result = a.map(&:to_sym) + b.map(&:to_sym)
118
+ result.uniq
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,94 @@
1
+ module NamedArguments
2
+ # Provides several hash utilities.
3
+ #
4
+ # Note that you need to extend your hash with this module:
5
+ #
6
+ # hash = {}
7
+ # hash.extend HashExtendedTools
8
+ # hash = hash.exclude :foo, :bar
9
+ #
10
+ # Or create a new class:
11
+ #
12
+ # class HashWithExtendedTools < Hash
13
+ # include HashExtendedTools
14
+ # end
15
+ module HashExtendedTools
16
+ # Change keys in a hash.
17
+ #
18
+ # Pass in a hash of:
19
+ #
20
+ # old_key => new_key
21
+ #
22
+ # Any keys matching +old_key+ will be
23
+ # deleted and a new entry created with
24
+ # the same value and the new key.
25
+ def switch_keys args = {}
26
+ args.each_pair do
27
+ |old_key, new_key|
28
+ if self.has_key?(old_key)
29
+ self[new_key] = self[old_key]
30
+ delete(old_key)
31
+ end
32
+ end
33
+ self
34
+ end
35
+
36
+ # Return a new hash not including
37
+ # keys that are contained in
38
+ # +keys_to_exclude+.
39
+ #
40
+ # Keys that match entries in
41
+ # +keys_to_exclude+ are deleted if
42
+ # either they match as string or a
43
+ # symbol (created with to_sym).
44
+ def exclude *keys_to_exclude
45
+ result = self.dup
46
+ keys_to_exclude.each do |k|
47
+ result.delete k.to_s
48
+ result.delete k.to_sym
49
+ end
50
+ result
51
+ end
52
+
53
+ # Given an array of keys,
54
+ # return a hash containing
55
+ # the key/value pairs
56
+ # for the matching keys.
57
+ #
58
+ # Values that are nil are not
59
+ # returned.
60
+ def slice *slice_keys
61
+ result = {}
62
+ slice_keys.each do |k|
63
+ result[k] = self[k] unless self[k].nil?
64
+ end
65
+ result
66
+ end
67
+
68
+ # Return the given attributes as a hash containing
69
+ # attribute => value pairs.
70
+ #
71
+ # obj.a = 10
72
+ # obj.b = 20
73
+ # attributes_as_hash(:a, :b)
74
+ # => {:a => 10, :b => 20}
75
+ def attributes_as_hash *attrs
76
+ result = {}
77
+ attrs.each do |a|
78
+ v = self.send a
79
+ result[a] = v unless v.nil?
80
+ end
81
+ result
82
+ end
83
+
84
+ def symbol_keys_as_strings
85
+ sym_keys = self.keys.select {|k| k.kind_of? Symbol}
86
+ new_keys = {}
87
+ sym_keys.each {|k| new_keys[k] = k.to_s}
88
+ result = dup
89
+ result.extend HashExtendedTools
90
+ result.switch_keys new_keys
91
+ result
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,51 @@
1
+ module NamedArguments
2
+ # Adds two methods to a class:
3
+ #
4
+ # define_method_with_context
5
+ # define_method_with_value
6
+ module MethodExtensions
7
+ def self.included target
8
+ target.class_eval do
9
+ include ExtendedDefines
10
+ extend ExtendedDefines
11
+ end
12
+ end
13
+
14
+ module ExtendedDefines
15
+ # Create a method out of a
16
+ # name and a lambda.
17
+ #
18
+ # Example:
19
+ #
20
+ # my_lambda = lambda {13}
21
+ # define_method_with_context :return_13, my_lambda
22
+ #
23
+ # assert 13 == self.return_13
24
+ def define_method_with_context method_name, &block
25
+ sclass = class << self; self end
26
+ sclass.send(:define_method, method_name, block)
27
+ sclass.send(:public, method_name)
28
+ end
29
+
30
+ # Create a method out of a
31
+ # value and a name
32
+ # The method will return the value.
33
+ #
34
+ # Example:
35
+ #
36
+ # define_method_with_value :return_14, 14
37
+ #
38
+ # assert 14 == self.return_14
39
+ def define_method_with_value name, value
40
+ define_method_with_context name do
41
+ value
42
+ end
43
+ end
44
+
45
+ def define_method_noop name
46
+ define_method_with_context name do
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/named_arguments'
@@ -0,0 +1,59 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require 'test/unit'
3
+
4
+ class NamedArgumentsTest < Test::Unit::TestCase
5
+ class TestClassAttributeDefaults
6
+ include NamedArguments
7
+ attr_accessor :foo, :bar
8
+ attribute_defaults :foo => 3
9
+ attribute_defaults :bar => 'bar'
10
+ end
11
+
12
+ class TestClassRequiredFields
13
+ include NamedArguments
14
+ required_fields :foo, :bar
15
+ attr_accessor :foo, :bar
16
+ end
17
+
18
+ class TestClassTypeConverter
19
+ include NamedArguments
20
+ attr_accessor :foo
21
+ type_converter :foo, String
22
+ end
23
+
24
+ # attribute_defaults :color => 'blue', :size => lambda {|s| some_method_call(s)}
25
+ # required_fields :color, :name
26
+ # required_respond_to :name => :to_s
27
+ # required_kind_of? :size => Fixnum
28
+ # type_converter :size => Fixnum
29
+ def test_attribute_defaults
30
+ o = TestClassAttributeDefaults.new
31
+ assert_equal 3, o.foo
32
+ assert_equal 'bar', o.bar
33
+ o = TestClassAttributeDefaults.new :bar => 7
34
+ assert_equal 7, o.bar
35
+ end
36
+
37
+ def test_required_fields
38
+ assert_raises NamedArguments::ParameterRequired do
39
+ o = TestClassRequiredFields.new
40
+ end
41
+ end
42
+
43
+ def test_required_respond_to
44
+ end
45
+
46
+ def type_converter
47
+ o = TestClassTypeConverter.new :foo => 3
48
+ assert_equal '3', o.foo
49
+
50
+ o = TestClassTypeConverter.new :foo => 3
51
+ assert_equal '3', o.foo
52
+ end
53
+
54
+ def test_includes
55
+ t = TestClassTypeConverter.new
56
+ t.define_method_with_value :test_method, 7
57
+ assert_equal 7, t.test_method
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: named_arguments
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.4
7
+ date: 2007-06-03 00:00:00 -07:00
8
+ summary: The author was too lazy to write a summary
9
+ require_paths:
10
+ - lib
11
+ email: ryand-ruby@zenspider.com
12
+ homepage: http://www.zenspider.com/ZSS/Products/named_arguments/
13
+ rubyforge_project: named_arguments
14
+ description: The author was too lazy to write a description
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Ryan Davis
31
+ files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - Rakefile
36
+ - lib/named_arguments.rb
37
+ - lib/named_arguments/class_settings_mixin.rb
38
+ - lib/named_arguments/hash_extended_tools.rb
39
+ - lib/named_arguments/method_extensions.rb
40
+ - test/unit/named_arguments_test.rb
41
+ - test/test_helper.rb
42
+ test_files:
43
+ - test/test_helper.rb
44
+ rdoc_options:
45
+ - --main
46
+ - README.txt
47
+ extra_rdoc_files:
48
+ - History.txt
49
+ - Manifest.txt
50
+ - README.txt
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ requirements: []
56
+
57
+ dependencies:
58
+ - !ruby/object:Gem::Dependency
59
+ name: hoe
60
+ version_requirement:
61
+ version_requirements: !ruby/object:Gem::Version::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 1.2.1
66
+ version: