diff --git a/lib/values.rb b/lib/values.rb index c762350..3c8af8a 100644 --- a/lib/values.rb +++ b/lib/values.rb @@ -23,17 +23,28 @@ def self.new(*fields, &block) Class.new do attr_reader(:hash, *fields) - define_method(:initialize) do |*values| - raise ArgumentError.new("wrong number of arguments, #{values.size} for #{fields.size}") if fields.size != values.size + # Unroll the fields into a series of assignment Ruby statements that can + # be used inside of the initializer for the new class. This was introduced + # in PR#56 as a performance optimization -- it ensures that this iteration + # happens once per class, instead of happening once per instance of the + # class. + instance_var_assignments = Array.new(fields.length) do |idx| + "@#{fields[idx]} = values[#{idx}]" + end.join("\n") + + class_eval <<-RUBY + def initialize(*values) + if #{fields.size} != values.size + raise ArgumentError.new("wrong number of arguments, \#{values.size} for #{fields.size}") + end - fields.zip(values) do |field, value| - instance_variable_set(:"@#{field}", value) - end + #{instance_var_assignments} - @hash = self.class.hash ^ values.hash + @hash = self.class.hash ^ values.hash - freeze - end + freeze + end + RUBY const_set :VALUE_ATTRS, fields