Skip to content

Commit

Permalink
Merge pull request #5 from PikachuEXE/performance/object-shape-friendly
Browse files Browse the repository at this point in the history
Make code "Object Shape friendly"
  • Loading branch information
PikachuEXE authored Oct 11, 2023
2 parents 24a26f3 + fa465cf commit 66e7e1f
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 9 deletions.
25 changes: 17 additions & 8 deletions lib/contracted_value/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,17 @@ def extract_value(hash)
attr_reader :default_value

def raise_error_if_inputs_invalid
raise_error_if_name_invalid
raise_error_if_refrigeration_mode_invalid
raise_error_if_default_value_invalid
end

def raise_error_if_name_invalid
return if name.is_a?(Symbol)

raise NotImplementedError, "Internal error: name is not a symbol (#{name.class.name})"
end

def raise_error_if_refrigeration_mode_invalid
return if RefrigerationMode::Enum.all.include?(refrigeration_mode)

Expand Down Expand Up @@ -231,6 +238,8 @@ def initialize(input_attr_values = {})
)
end

@attr_values = {}

self.class.send(:attribute_set).each_attribute do |attribute|
attr_value = attribute.extract_value(input_attr_values_hash)

Expand All @@ -253,8 +262,8 @@ def initialize(input_attr_values = {})

# Using symbol since attribute names are limited in number
# An alternative would be using frozen string
instance_variable_set(
:"@#{attribute.name}",
@attr_values.store(
attribute.name.to_sym,
sometimes_frozen_attr_value,
)
end
Expand All @@ -265,10 +274,7 @@ def initialize(input_attr_values = {})
# rubocop:enable Metrics/CyclomaticComplexity

def to_h
self.class.send(:attribute_set).
each_attribute.each_with_object({}) do |attribute, hash|
hash[attribute.name] = instance_variable_get(:"@#{attribute.name}")
end
@attr_values.clone
end

# == Class interface == #
Expand All @@ -288,16 +294,19 @@ def attribute(
refrigeration_mode: RefrigerationMode::Enum::DEEP,
default_value: Private::ATTR_DEFAULT_VALUE_ABSENT_VAL
)
# Using symbol since attribute names are limited in number
# An alternative would be using frozen string
name_in_sym = name.to_sym

attr = Attribute.new(
name: name,
name: name_in_sym,
contract: contract,
refrigeration_mode: refrigeration_mode,
default_value: default_value,
)
@attribute_set = @attribute_set.add(attr)

attr_reader(name)
define_method(name_in_sym) { @attr_values[name_in_sym] }
end

# @api private
Expand Down
24 changes: 23 additions & 1 deletion spec/contracted_value/value_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@
end
}.to raise_error(::ContractedValue::Errors::DuplicateAttributeDeclaration)
end

example "does not raise error when declaring 1 attribute with string name" do
expect {
value_class.class_eval do
attribute("attribute_1")
end
}.to_not raise_error
end

example "does not raise error when declaring 1 attribute with number name" do
expect {
value_class.class_eval do
attribute(1)
end
}.to raise_error(::NoMethodError, /undefined method `to_sym'/)
end
end


Expand Down Expand Up @@ -93,13 +109,19 @@

it "does not raise error when input is a value" do
aggregate_failures do
new_val = nil
expect {
value_class.new(
new_val = value_class.new(
value_class.new(
default_inputs,
),
)
}.to_not raise_error
if new_val
default_inputs.each_pair do |attr_name, attr_val|
expect(new_val.public_send(attr_name)).to eq(attr_val)
end
end
end
end

Expand Down

0 comments on commit 66e7e1f

Please sign in to comment.