named_arguments 0.0.4

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