namedarguments 0.0.1

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