Class: Cacofonix::Element

Inherits:
Object
  • Object
show all
Extended by:
Dry::Core::ClassAttributes
Includes:
ROXML
Defined in:
lib/cacofonix/core/element.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeElement

Returns a new instance of Element.



259
260
261
262
263
264
265
266
267
# File 'lib/cacofonix/core/element.rb', line 259

def initialize(*)
  if self.class.xml_array_accessors
    self.class.xml_array_accessors.each do |attr|
      send :"#{attr}=", []
    end
  end

  super
end

Class Method Details

.alias_accessor(new_accessor, old_accessor) ⇒ Object



244
245
246
247
# File 'lib/cacofonix/core/element.rb', line 244

def self.alias_accessor(new_accessor, old_accessor)
  alias_method(new_accessor, old_accessor)
  alias_method("#{new_accessor}=", "#{old_accessor}=")
end

.onix_boolean_flag(name, tag_name, options = {}) ⇒ Object

An accessor that treats an empty string as a true value – so that something like <NoContributor /> is recognised as “there is no contributor”.



60
61
62
63
64
65
66
67
# File 'lib/cacofonix/core/element.rb', line 60

def self.onix_boolean_flag(name, tag_name, options = {})
  options = options.merge(
    :from => tag_name,
    :to_xml => Cacofonix::Formatters.boolean
  )
  prep = lambda { |v| v ? true : false }
  xml_accessor(name, **options, &prep)
end

.onix_code_from_list(name, tag_name, options = {}) ⇒ Object

An accessor that maps a “code” string into an Cacofonix::Code object. A Code object can return the simple code (or “key”), or the value that is associated with it in ONIX code lists, and so on.

Required:

:list - integer referring to an ONIX Code List

Special options for Code instantiation are:

:length - how many digits to pad (default is taken from total list size)

As well as the normal accessor (x/x=), this will create a special accessor for the richer Code object (#x_code/#x_code=). For example:

class Foo < Cacofonix::Element
  onix_code_from_list(:message_type, "MessageType", :list => 1)
end

foo = Foo.new

foo.message_type = 1

foo.message_type
>> 1

foo.message_type_code
>> #<Cacofonix::Code:.......>

foo.message_type_code.key
>> 1

foo.message_type_code.to_s
>> "01"

foo.message_type_code.value
>> "Early notification"


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/cacofonix/core/element.rb', line 110

def self.onix_code_from_list(name, tag_name, options = {})
  unless list_number = options.delete(:list)
    raise Cacofonix::CodeListNotSpecified
  end
  code_opts = options.slice(:length, :enforce)
  options.delete(:enforce)
  prep = lambda { |value|
    Cacofonix::Code.new(list_number, value, code_opts)
  }
  options = options.merge(:from => tag_name)
  xml_accessor("#{name}_code", **options, &prep)

  define_method(name) do
    send("#{name}_code").key
  end

  define_method("#{name}=") do |val|
    val = prep.call(val)  unless val.kind_of?(Cacofonix::Code)
    send("#{name}_code=", val)
  end
end

.onix_codes_from_list(name, tag_name, options = {}, &blk) ⇒ Object

Like onix_code_from_list, but for an array of codes.

Required:

:list - integer referring to an ONIX Code List

One important caveat: when assigning to this accessor, you must pass in the complete array – if you assign an array that you later push or shift items into, you might get unexpected results.

Similar to onix_code_from_list, this creates a special accessor for the Code objects at (#x_codes/#x_codes=). For example:

class Bar < Cacofonix::Element
  onix_codes_from_list(:identifiers, "Identifier", :list => 5)
end

bar = Bar.new

bar.identifiers = [1, 5, 13]

bar.identifiers_codes.collect { |ids| ids.value }
>> ["Proprietary", "ISMN-10", "LLCN"]

If a block is given, each value is passed into it first - return an array of the actual values.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/cacofonix/core/element.rb', line 159

def self.onix_codes_from_list(name, tag_name, options = {}, &blk)
  unless list_number = options.delete(:list)
    raise Cacofonix::CodeListNotSpecified
  end
  code_opts = options.slice(:length, :enforce)
  options.delete(:enforce)
  prep = lambda { |values|
    if block_given?
      values = [values].flatten.collect { |v| blk.call(v) }
    end
    [values].flatten.collect do |data|
      Cacofonix::Code.new(list_number, data, code_opts)
    end
  }
  options = options.merge(:from => tag_name, :as => [])
  xml_accessor("#{name}_codes", **options, &prep)

  define_method(name) do
    codes = send("#{name}_codes")
    codes ? codes.collect { |cd| cd.key } : nil
  end

  # FIXME: Hmm, adding to array? what happens with push, etc?
  define_method("#{name}=") do |vals|
    vals = [vals].flatten.collect { |v|
      v.kind_of?(Cacofonix::Code) ? v : prep.call(v)
    }.flatten
    send("#{name}_codes=", vals)
  end
end

.onix_composite(name, klass, options = {}) ⇒ Object

An accessor to an array of element instances.

Options:

:from - defaults to the class name, but you can override this.
:singular - accessor is not an array, just a single object.


18
19
20
21
22
# File 'lib/cacofonix/core/element.rb', line 18

def self.onix_composite(name, klass, options = {})
  options[:as] = options.delete(:singular) ? klass : [klass]
  options[:from] ||= klass.to_s.split("::").last
  xml_accessor(name, **options)
end

.onix_date_accessor(name, tag_name, options = {}) ⇒ Object

An accessor that treats the input/output as a date.

Options: none yet.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/cacofonix/core/element.rb', line 28

def self.onix_date_accessor(name, tag_name, options = {})
  options = options.merge(
    :from => tag_name,
    :to_xml => Cacofonix::Formatters.yyyymmdd
  )
  if options[:as].kind_of?(Array)
    prep = lambda { |vs|
      [vs].flatten.collect { |v| Date.parse(v) rescue nil }
    }
  else
    prep = lambda { |v| Date.parse(v) rescue nil }
  end
  xml_accessor(name, **options, &prep)
end

.onix_space_separated_list(name, tag_name, options = {}) ⇒ Object

An accessor that treats the input as a space-separated list, and creates an array for it.



46
47
48
49
50
51
52
53
# File 'lib/cacofonix/core/element.rb', line 46

def self.onix_space_separated_list(name, tag_name, options = {})
  options = options.merge(
    :from => tag_name,
    :to_xml => Cacofonix::Formatters.space_separated
  )
  prep = lambda { |v| v ? v.split : [] }
  xml_accessor(name, **options, &prep)
end

.onix_spaced_codes_from_list(name, tag_name, options) ⇒ Object

Sugar for a common case – country or territory codes.



193
194
195
196
# File 'lib/cacofonix/core/element.rb', line 193

def self.onix_spaced_codes_from_list(name, tag_name, options)
  options[:to_xml] ||= Cacofonix::Formatters.space_separated
  onix_codes_from_list(name, tag_name, options) { |v| v ? v.split : [] }
end

.xml_accessor(attr, *args, **options) ⇒ Object



250
251
252
253
254
255
256
# File 'lib/cacofonix/core/element.rb', line 250

def self.xml_accessor(attr, *args, **options)
  if options[:as] && options[:as].kind_of?(Array)
    xml_array_accessors(Array(xml_array_accessors) + [attr])
  end

  super
end

Instance Method Details

#fetch(composite_symbol, mthd, query) ⇒ Object

Query a composite array within this element, looking for an attribute (‘mthd’) that has a value equal to ‘query’.

The idea is that you can shorten this:

product.websites.detect { |ws| ws.website_role == 1 }

To this:

product.fetch(:websites, :website_role, 1)

Note: query may be an array of values, will return the first composite that matches one of them. So this:

product.websites.detect { |ws| ws.website_role == 1 } ||
  product.websites.detect { |ws| ws.website_role == 2 }

becomes this:

product.fetch(:websites, :website_role, [1, 2])


220
221
222
223
224
225
226
227
228
# File 'lib/cacofonix/core/element.rb', line 220

def fetch(composite_symbol, mthd, query)
  result = nil
  [query].flatten.each do |matcher|
    break  if result = send(composite_symbol).detect do |comp|
      comp.send(mthd) == matcher
    end
  end
  result
end

#fetch_all(composite_symbol, mthd, query) ⇒ Object

Queries a composite array like #fetch, but returns all composites that have a match.



233
234
235
236
237
238
239
240
241
# File 'lib/cacofonix/core/element.rb', line 233

def fetch_all(composite_symbol, mthd, query)
  [query].flatten.inject([]) do |acc, matcher|
    comps = send(composite_symbol).select do |comp|
      comp.send(mthd) == matcher
    end
    acc += comps  if comps.any?
    acc
  end
end