Skip to content

Commit

Permalink
Fix thread safety issues
Browse files Browse the repository at this point in the history
closes #3
  • Loading branch information
corny committed Mar 18, 2024
1 parent 0bdc056 commit 5513ceb
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 35 deletions.
8 changes: 4 additions & 4 deletions lib/xrechnung.rb
Original file line number Diff line number Diff line change
Expand Up @@ -270,18 +270,18 @@ def to_xml(indent: 2, target: "")
xml.cbc :TaxCurrencyCode, tax_currency_code
xml.cbc :BuyerReference, buyer_reference

unless members[:invoice_period][:optional] && invoice_period.nil?
unless self.class.members[:invoice_period].optional && invoice_period.nil?
invoice_period&.to_xml(xml)
end

xml.cac :OrderReference do
xml.cbc :ID, purchase_order_reference
unless members[:sales_order_reference][:optional] && sales_order_reference.nil?
unless self.class.members[:sales_order_reference].optional && sales_order_reference.nil?
xml.cbc :SalesOrderID, sales_order_reference
end
end

unless members[:billing_reference][:optional] && billing_reference.nil?
unless self.class.members[:billing_reference].optional && billing_reference.nil?
xml.cac :BillingReference do
billing_reference&.to_xml(xml)
end
Expand All @@ -303,7 +303,7 @@ def to_xml(indent: 2, target: "")
accounting_customer_party&.to_xml(xml)
end

unless members[:tax_representative_party][:optional] && tax_representative_party.nil?
unless self.class.members[:tax_representative_party].optional && tax_representative_party.nil?
xml.cac :TaxRepresentativeParty do
tax_representative_party&.to_xml(xml)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/xrechnung/invoice_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def to_xml(xml)
xml.cbc :InvoicedQuantity, invoiced_quantity.amount_to_s, unitCode: invoiced_quantity.unit_code
xml.cbc :LineExtensionAmount, *line_extension_amount.xml_args

unless members[:invoice_period][:optional] && invoice_period.nil?
unless self.class.members[:invoice_period].optional && invoice_period.nil?
invoice_period&.to_xml(xml)
end
item&.to_xml(xml)
Expand Down
6 changes: 3 additions & 3 deletions lib/xrechnung/legal_monetary_total.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ class LegalMonetaryTotal

# noinspection RubyResolve
def to_xml(xml)
members.each do |member, _options|
next if self[member].nil?
self.class.members.each_key do |name|
next if self[name].nil?

xml.cbc :"#{member.to_s.split("_").map(&:capitalize).join}", *self[member].xml_args
xml.cbc :"#{name.to_s.split("_").map(&:capitalize).join}", *self[name].xml_args
end
xml.target!
end
Expand Down
45 changes: 18 additions & 27 deletions lib/xrechnung/member_container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,54 @@ def self.included(base)
end

def initialize(**kwargs)
self.class.after_initialize.each do |block|
instance_eval(&block)
# initialize default values
self.class.members.each do |name, member|
self[name] = member.default.dup unless member.default.nil?
end

kwargs.each do |k, v|
self[k] = v
end
end

def members
self.class.instance_variable_get :@members
end

def [](key)
send(key)
end

def []=(key, value)
send(members[key].fetch(:setter_name), value)
send("#{key}=", value)
end

Member = Struct.new(:type, :default, :optional, :transform_value, keyword_init: true)

module ClassMethods
def members
@members
end

# @param [String] member_name
# @param [Array<Class>, Class] type
# @param [Object] default
# @param [TrueClass, FalseClass] optional When true, omits tag rather than rendering an empty tag on nil
# @param [Proc] transform_value A Proc which is called with the input value to perform type conversion.
def member(member_name, type: nil, default: nil, optional: false, transform_value: nil)
def member(member_name, **kwargs)
attr_reader member_name
setter_name = :"#{member_name}="
@members[member_name] = { optional: optional, setter_name: setter_name }
kwargs[:default].freeze

if default
after_initialize do
send(setter_name, default)
end
end
setter_name = :"#{member_name}="
member = Member.new(**kwargs)
@members[member_name] = member

define_method setter_name do |in_value|
in_value = transform_value.call(in_value) if transform_value
in_value = member.transform_value.call(in_value) if member.transform_value

if type && !in_value.nil? && Array(type).none? { |t| in_value.is_a?(t) }
raise ArgumentError, "expected #{type} for :#{member_name}, got: #{in_value.class}"
if member.type && !in_value.nil? && Array(member.type).none? { |t| in_value.is_a?(t) }
raise ArgumentError, "expected #{member.type} for :#{member_name}, got: #{in_value.class}"
end

instance_variable_set :"@#{member_name}", in_value
end
end

def after_initialize(&block)
@after_initialize_blocks ||= []
if block
@after_initialize_blocks << block
else
@after_initialize_blocks
end
end
end
end
end
9 changes: 9 additions & 0 deletions spec/xrechnung_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,13 @@
it "sets defaults" do
expect(doc.to_xml).to include "<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>"
end

it "thread safe initializer" do
doc1 = ::Xrechnung::Document.new
doc2 = ::Xrechnung::Document.new

doc1.invoice_lines << Xrechnung::InvoiceLine.new

expect(doc2.invoice_lines).to be_empty # Fails - doc1 and doc2 have the same invoice lines
end
end

0 comments on commit 5513ceb

Please sign in to comment.