Class ArsModels::Entry
In: lib/ars_models/entry.rb
Parent: BaseWithContext

Ruby wrapper class for the java ArsEntry object. Instances of Entry represent a single Ars record.

Methods

Attributes

ars_entry  [RW]  TODO: Document ArsModels::Entry#ars_entry attribute
context  [RW]  TODO: Document ArsModels::Entry#context attribute
field_values  [RW]  TODO: Document ArsModels::Entry#field_values attribute
form  [RW]  TODO: Document ArsModels::Entry#form attribute
id  [RW]  TODO: Document ArsModels::Entry#id attribute
messages  [RW]  TODO: Document ArsModels::Entry#messages attribute
modified_attributes  [RW]  TODO: Document ArsModels::Entry#modified_attributes attribute

Public Class methods

TODO: Document ArsModels::Entry.create!

[Source]

    # File lib/ars_models/entry.rb, line 34
34:     def self.create!(options={})
35:       # FIXME: This should not be save! directly

36:       save!(options)
37:     end

Delete Operations

Delete operates with two different selection approaches:

Delete All: This will attempt to delete all the records matching the passed conditions and return an array including Entry objects for successfully deleted records and Exceptions::ModelException objects for any records that encountered an error when being deleted.

Delete By ID: This can either be a specific id (‘000000000000001’), a list of ids (‘000000000000001’, ‘000000000000005’, ‘000000000000006’), or an array of ids ([‘000000000000001’, ‘000000000000005’, ‘000000000000006’]). If a single id is passed, the single deleted matching record will be returned (or an Exceptions::ModelException will be raised if an error was encountered). If multiple ids are passed, an array including Entry objects for successfully deleted records and Exceptions::ModelException objects for any records that encountered an error when being deleted will be returned.

Multiple Deletion Results

Anytime delete is called with the :all operation, or passed multiple IDs to delete, additional helper methods are available for the resulting array. The successes method will return a hash of results index keys mapped to the corresponding successfuly deleted entry objects, and the failures method will return a hash of results index keys mapped to the corresponding unsucessfuly entry deletion exceptions.

Delete Options

Required Delete Options

  • :context - The Context instance that the delete call should be executed as.
  • :form - The Form instance representing the Ars schema the delete call is querying.

Optional Delete Options

  • :conditions - An ars qualification fragment a format like: [’? = "NEW"’, 7], [’? = "NEW"’, :Status], or [’? = "NEW"’, ‘Status’], where the first entry is the qualification string substituting question marks for each of the field identifiers and the remaining array items are the field identifiers in order of appearence. As a shortcut, a hash of field identifiers to values can be passed in. For example, {"Status" => "New", "Group Type" => "View"} equates to [’? = "New" AND ? = "View"’, "Status", "Group Type"]. If any of the values contain a question mark, it should be escaped (IE ’\?’ or "\?"). This can be done via @string.gsub(/\?/, ’\?’).
  • :fields - :all or an array of field identifiers (string for name, symbol for label, number for id) indicating which field values should be included in the results. Set this parameter to an empty array for more efficient deletes that return only the ID of the record. Defaults to :all.

Examples

  # Preamble
  context = Context.new(:username => 'Demo', :password => '', :server => '127.0.0.1', :port => '0')
  group_form = Form.find('Group')

  # Delete all non-system groups
  results = Entry.delete(:all, :context => context, :form => group_form, :conditions => ["? > 7", 'Group ID'])

  # Display the results
  puts "Successes: "
  results.successes.each do {|results_index, entry| puts "  #{entry.id}"}
  puts "Failures: "
  results.failures.each do {|results_index, exception| puts "  #{exception.to_s}"}

  # Delete a single group entry (Administrators) by id
  Entry.delete(000000000000001, :context => context, :form => group_form)
  # Delete multiple group entries (Administrators, Public) by id
  results = Entry.delete(000000000000001, 000000000000006, :context => context, :form => group_form)
  results = Entry.delete([000000000000001,000000000000006], :context => context, :form => group_form)

  # Display the results
  puts "Successes: "
  results.successes.each do {|results_index, entry| puts "  #{entry.id}"}
  puts "Failures: "
  results.failures.each do {|results_index, exception| puts "  #{exception.to_s}"}

[Source]

     # File lib/ars_models/entry.rb, line 117
117:     def self.delete!(*args)
118:       # TODO: Abstraction - Clean up and document the delete logic chain

119:       # TODO: Feature Completion - Test paged deletions? (ArsModels::Entry.delete)

120:       # TODO: Feature Completion - include_attachments? (ArsModels::Entry.delete)

121:       # TODO: Feature Completion - Messages triggered by deletes

122: 
123:       # Pop the last argument if it is an options hash

124:       options = args.last.is_a?(::Hash) ? args.pop.clone : {}
125: 
126:       # Default the context if this class was created with a context instance

127:       options[:context] ||= context if context
128: 
129:       # Validate that the options are valid

130:       validate_options(options,
131:         :required => [:context, :form],
132:         :optional => [:conditions, :fields]
133:       )
134: 
135:       # Build the array of requested field ids

136:       field_ids = build_field_ids(options.delete(:fields), options[:form])
137: 
138:       # Call the appropriate find method

139:       if args.first == :all
140:         # Build the qualification information

141:         options[:qualification], options[:qualification_field_ids] = build_qualification(options.delete(:conditions), options[:form])
142: 
143:         # Make the JAPI models call to find all of the matching results

144:         ars_find_results = ArsEntry.findAll(
145:           options[:context].ars_context,
146:           options[:form].ars_form,
147:           options[:qualification],
148:           options[:qualification_field_ids].to_java(:Long),
149:           field_ids.nil? ? nil : field_ids.to_java(:Long)
150:         )
151: 
152:         # Declare the requried result objects

153:         results, successes, failures = [], {}, {}
154: 
155:         # Collect the results, converting them to Entry or Exception objects

156:         ars_find_results.collect do |ars_result|
157:           begin
158:             # Call the japi delete method

159:             ars_result.delete(options[:context].ars_context)
160:             # Build a new Entry from the japi object

161:             result = new(ars_result, :context => options[:context], :form => options[:form])
162:             # Add it to the successes hash

163:             successes[results.length] = result
164:             # Add it to the results array

165:             results << result
166:           rescue StandardError => exception
167:             # Process the exception

168:             exception = Exceptions::InternalError.process(exception)
169:             # Add the exception to the failures hash

170:             failures[results.length] = exception
171:             # Add the exception to the results array

172:             results << exception
173:           end
174:         end
175:         
176:         # Add the successes method to the results array

177:         results_metaclass = class << results; self; end
178:         results_metaclass.instance_eval do
179:           define_method(:successes) {successes}
180:           define_method(:failures) {failures}
181:         end
182: 
183:         # Return the results

184:         results
185:       # If this is no an :all, :first, :last, :rand, or :single search

186:       else
187:         # Build the list of ids

188:         ids = args.flatten.collect {|record| record.is_a?(ArsModels::Entry) ? record.id : record}
189: 
190:         # If there were no entries, return an empty array

191:         if args.length == 0
192:           raise ArgumentError.new('Missing arguments specifying records to delete.')
193:         # If there was one entry, try to delete it

194:         elsif args.length == 1 && !args.first.is_a?(::Array)
195:           begin
196:             # Call the japi delete method

197:             ars_result = ArsEntry.delete(
198:               options[:context].ars_context,
199:               options[:form].ars_form,
200:               ids.first,
201:               field_ids.nil? ? nil : field_ids.to_java('Long''Long')
202:             )
203:             # Build a new Entry from the japi object

204:             result = new(ars_result, :context => options[:context], :form => options[:form])
205:           rescue StandardError => exception
206:             # Process the exception

207:             raise Exceptions::InternalError.process(exception)
208:           end
209:         # If multiple entries were deleted

210:         else
211:           # Declare the requried result objects

212:           results, successes, failures = [], {}, {}
213: 
214:           # Collect the results, converting them to Entry or Exception objects

215:           ids.collect do |id|
216:             begin
217:               # Call the japi delete method

218:               ars_result = ArsEntry.delete(
219:                 options[:context].ars_context,
220:                 options[:form].ars_form,
221:                 id,
222:                 field_ids.nil? ? nil : field_ids.to_java('Long''Long')
223:               )
224:               # Build a new Entry from the japi object

225:               result = new(ars_result, :context => options[:context], :form => options[:form])
226:               # Add it to the successes hash

227:               successes[results.length] = result
228:               # Add it to the results array

229:               results << result
230:             rescue StandardError => exception
231:               # Process the exception

232:               exception = Exceptions::InternalError.process(exception)
233:               # Add the exception to the failures hash

234:               failures[results.length] = exception
235:               # Add the exception to the results array

236:               results << exception
237:             end
238:           end
239: 
240:           # Add the successes method to the results array

241:           results_metaclass = class << results; self; end
242:           results_metaclass.instance_eval do
243:             define_method(:successes) {successes}
244:             define_method(:failures) {failures}
245:           end
246: 
247:           # Return the results

248:           results
249:         end
250:       end
251:     end

Find Operations

Find operates with four different retrieval approaches:

Find All: This will return all the records matched by the options used. If no records are found, an empty array is returned.

Find By ID: This can either be a specific id (‘000000000000001’), a list of ids (‘000000000000001’, ‘000000000000005’, ‘000000000000006’), or an array of ids ([‘000000000000001’, ‘000000000000005’, ‘000000000000006’]). If a single id is passed, the single matched record will be returned, or nil if it is not found. If multiple ids are passed, an array of records will be returned. Entry ids not found will result in a nil object.

Find By Position: There are three options for finding an entry by position.

  • :first - Returns the first record matched by the options used. If no records are matched, nil is returned.
  • :last - Returns the last record matched by the options used. If no records are matched, nil is returned.
  • :rand - Returns a random record that matches the options used. If no records are matched, nil is returned.

Find Single: This will return a single record matched by the options used. If a matching record is not found, nil is returned. If more than one record is found an exception is raised. Find single is most frequently used to retrieve a record by GUID or other non-id unique identifier.

Find Options

Required Find Options

  • :context - The Context instance that the find call should be executed as.
  • :form - The Form instance representing the Ars schema the find call is querying.

Optional Find Options

  • :conditions - An ars qualification fragment a format like: [’? = "NEW"’, 7], [’? = "NEW"’, :Status], or [’? = "NEW"’, ‘Status’], where the first entry is the qualification string substituting question marks for each of the field identifiers and the remaining array items are the field identifiers in order of appearence. As a shortcut, a hash of field identifiers to values can be passed in. For example, {"Status" => "New", "Group Type" => "View"} equates to [’? = "New" AND ? = "View"’, "Status", "Group Type"]. If any of the values contain a question mark, it should be escaped (IE ’\?’ or "\?"). This can be done via @string.gsub(/\?/, ’\?’).
  • :fields - :all or an array of field identifiers (string for name, symbol for label, number for id) indicating which field values should be included in the results. Defaults to :all.
  • :limit - A number representing the maximum number of entries returned. This defaults to the max entry list size set for Remedy.
  • :order - An array of field identifiers (string for name, symbol for label, number for id) indicating the sort order from most to least significant. To reverse the sort order for a field, replace the identifier with a single element hash that contains the field identifier as the key, and :desc, ‘desc’, or ‘DESC’ as the value. Numerical field identifiers can be set to reverse sorting by using their negative. Defaults to the Form‘s default sort order.
  • :page - Used when finding all or finding by position, +:page+ indicates the page number, indexed starting at 1, to restrict the search to.

Pagination

When the find all operation is used, the resulting array is patched to include pagination meta-data. The following method calls are available for the resulting array:

  • :total_entries - The total number of matching results.
  • :total_pages - The total number of pages containing matching results.
  • :current_page - The current page number, indexed starting at 1, for the results.
  • :previous_page - The previous page number, indexed starting at 1, for the results. If the current page is also the first, this will return nil.
  • :next_page - The next page number, indexed starting at 1, for the results. If the current page is also the last, this will return nil.

Examples

  # Preamble
  context = Context.new(:username => 'Demo', :password => '', :server => '127.0.0.1', :port => '0')
  group_form = Form.find('Group')

  # Find all system groups
  Entry.find(:all, :context => context, :form => group_form, :conditions => ["? <= 7", 'Group ID'])

  # Find single group entry (Administrators) by id
  Entry.find(000000000000001, :context => context, :form => group_form)
  # Find multiple group entries (Administrators, Public) by id
  Entry.find(000000000000001, 000000000000006, :context => context, :form => group_form)
  Entry.find([000000000000001,000000000000006], :context => context, :form => group_form)

  # Find the first group alphabetically
  Entry.find(:first, :context => context, :form => group_form, :order => ['Group Name'])
  # Find the most recently created group
  Entry.find(:last, :context => context, :form => group_form, :order => ['Create Date'])
  # Find a random group
  Entry.find(:rand, :context => context, :form => group_form)

  # Find single group entry with the provided unique identifier
  Entry.find(:single, :context => context, :form => group_form, :conditions => {'Unique Identifier' => "AG0c29df58dc1rCgRQAGMuAAY_zD"})

[Source]

     # File lib/ars_models/entry.rb, line 358
358:     def self.find(*args)
359:       # TODO: Abstraction - Clean up and document the find logic chain

360:       # TODO: Feature Completion - Test paged deletions? (ArsModels::Entry.find)

361:       # TODO: Feature Completion - include_attachments? (ArsModels::Entry.find)

362: 
363:       begin
364:         # Pop the last argument if it is an options hash

365:         options = args.last.is_a?(::Hash) ? args.pop.clone : {}
366: 
367:         # Default the context if this class was created with a context instance

368:         options[:context] ||= context if context
369:         
370:         # Validate that the options are valid

371:         validate_options(options,
372:           :required => [:context, :form],
373:           :optional => [
374:             :conditions,
375:             :field_ids,
376:             :fields,
377:             :include_attachments,
378:             :limit,
379:             :order,
380:             :page
381:           ]
382:         )
383: 
384:         # Default shared options

385:         options[:include_attachments] ||= false
386: 
387:         # Build the array of requested field ids

388:         options[:field_ids] = build_field_ids(options.delete(:fields), options[:form])
389: 
390:         # Call the appropriate find method

391:         if [:all, :first, :last, :rand, :single].include?(args.first)
392:           # Default order

393:           options[:order] = build_field_ids(options.delete(:order), options[:form]) || options[:form].sort_field_ids
394: 
395:           # Build the qualification information

396:           options[:qualification], options[:qualification_field_ids] = build_qualification(options.delete(:conditions), options[:form])
397: 
398:           # Call the corresponding find

399:           case args.first
400:           when :all then
401:             # Validate the options

402:             validate_options(options,
403:               :required => [
404:                 :context, :form, :field_ids, :include_attachments, :qualification, :qualification_field_ids
405:               ],
406:               :optional => [:limit, :order, :page]
407:             )
408: 
409:             # Compute the chunk index (which is indexed by 0, where :page is indexed by 1)

410:             chunk_index = options[:page] ? (options[:page] - 1) : 0
411: 
412:             # Get the JAPI results

413:             find_result = ArsEntry.findAllWithCount(
414:               options[:context].ars_context,
415:               options[:form].ars_form,
416:               options[:qualification],
417:               options[:qualification_field_ids].to_java('Long'),
418:               options[:field_ids].nil? ? nil : options[:field_ids].to_java('Long'),
419:               options[:order].to_java('Long'),
420:               options[:limit],
421:               chunk_index,
422:               options[:include_attachments]
423:             )
424: 
425:             # Parse the results to get the entries and the count

426:             ars_entries = find_result[0]
427:             count = find_result[1]
428: 
429:             # Convert the JAPI entries into ArsModels entries

430:             result = ars_entries.collect do |ars_entry|
431:               new(ars_entry, :context => options[:context], :form => options[:form])
432:             end
433: 
434:             # TODO: Abstraction - Convert this to an include / extend module call so that the results array can be checked via is_a?(ArsModels::Pagination)

435:             # Add the pagination helper methods

436:             result_metaclass = class << result; self; end
437:             result_metaclass.instance_eval do
438:               define_method(:total_entries) {count}
439:               define_method(:total_pages)   {options[:limit] == 0 ? 1 : (count.to_f / options[:limit]).ceil}
440:               define_method(:current_page)  {chunk_index}
441:               define_method(:previous_page) {chunk_index == 0 ? nil : chunk_index}
442:               define_method(:next_page)     {chunk_index+1 == total_pages ? nil : chunk_index+2}
443:             end
444: 
445:             # Return the result set

446:             result
447:           when :first, :last then
448:             # TODO: Feature Completion - Implement first/last for paging (ArsModels::Entry.find)

449: 
450:             # Validate the options

451:             validate_options(options,
452:               :required => [:context, :form],
453:               :optional => [:field_ids, :include_attachments, :order, :qualification, :qualification_field_ids]
454:             )
455: 
456:             # Reverse the order if looking for the last

457:             options[:order].collect! {|id| -id} if args.first == :last
458: 
459:             # Make the JAPI models call

460:             entries = ArsEntry.find_all(
461:               options[:context].ars_context,
462:               options[:form].ars_form,
463:               options[:qualification],
464:               options[:qualification_field_ids].to_java('Long'),
465:               options[:field_ids].nil? ? nil : options[:field_ids].to_java('Long'),
466:               options[:order].to_java('Long'),
467:               1 # Set the limit to 1

468:             ).to_a
469: 
470:             # Generate the result

471:             case entries.length
472:               when 0 then nil
473:               when 1 then new(entries.first, :context => options[:context], :form => options[:form])
474:               else raise 'Multiple entries returned for find by position call.'
475:             end
476:           when :rand then
477:             # TODO: Feature Completion - ArsModels::Entry.find(:rand)

478:             raise 'Not Implemented'
479:           when :single then
480:             # Validate the options

481:             validate_options(options,
482:               :required => [:context, :form],
483:               :optional => [
484:                 :field_ids, :include_attachments, :order, :qualification, :qualification_field_ids
485:               ]
486:             )
487: 
488:             # Get the JAPI results

489:             find_results = ArsEntry.find_all(
490:               options[:context].ars_context,
491:               options[:form].ars_form,
492:               options[:qualification],
493:               options[:qualification_field_ids].to_java('Long'),
494:               options[:field_ids].nil? ? nil : options[:field_ids].to_java('Long')
495:             )
496: 
497:             # Generate the result

498:             case find_results.length
499:               when 0 then nil
500:               when 1 then new(find_results[0], :context => options[:context], :form => options[:form])
501:               else raise "More than one result found by 'single' search"
502:             end
503:           end
504:         # If this is no an :all, :first, :last, :rand, or :single search

505:         else
506:           # Validate the options

507:           validate_options(options,
508:             :required => [
509:               :context,
510:               :form,
511:               :field_ids,
512:               :include_attachments
513:             ]
514:           )
515: 
516:           # Convert the list of arguments to an array of Java Strings

517:           ids = args.to_java(:string)
518: 
519:           # Get the JAPI results

520:           find_results = ArsEntry.find_all(
521:             options[:context].ars_context,
522:             options[:form].ars_form,
523:             ids,
524:             options[:field_ids].nil? ? nil : options[:field_ids].to_java('Long')
525:           )
526: 
527:           # Convert the JAPI entries into ArsModels entries

528:           results = find_results.collect do |ars_entry|
529:             ars_entry ? new(ars_entry, :context => options[:context], :form => options[:form]) : nil
530:           end
531: 
532:           # If there was only one entry requested, return the single result,

533:           # otherwise return the result array

534:           ids.length == 1 && find_results.length == 1 ? results.first : results
535:         end
536:       rescue NativeException => exception
537:         raise Exceptions::ModelException.new(exception.cause)
538:       end
539:     end

TODO: Document ArsModels::Entry initializer

[Source]

    # File lib/ars_models/entry.rb, line 25
25:     def initialize(*args)
26:       super(*args)
27:     end

TODO: Document ArsModels::Entry.update!

[Source]

     # File lib/ars_models/entry.rb, line 542
542:     def self.update!(*args)
543:       # FIXME: This should probably not be save! directly

544:       # TODO: Enhancement - Implement update!(:single)

545:       save!(*args)
546:     end

Public Instance methods

'[]'(attribute)

Alias for get_field_value

'[]='(attribute, value)

Alias for set_field_value

TODO: Document ArsModels::Form#delete!

[Source]

     # File lib/ars_models/entry.rb, line 580
580:     def delete!(options={})
581:       # Check for bad options

582:       validate_options(options, :optional => [:context, :fields])
583: 
584:       # Define the default options

585:       default_options = {}
586:       default_options[:context] = context if context
587:       default_options[:form] = form
588:       default_options[:fields] = field_value_ids
589: 
590:       # Delete this instance

591:       self.class.delete!(self.id, default_options.merge(options))
592:     end

TODO: Document ArsModels::Form#field_value_ids

[Source]

     # File lib/ars_models/entry.rb, line 628
628:     def field_value_ids
629:       @ars_entry.field_value_ids.to_a
630:     end

TODO: Document ArsModels::Entry#get_field_value

[Source]

     # File lib/ars_models/entry.rb, line 553
553:     def get_field_value(attribute)
554:       raise 'Unable to process field value without form definition' unless form
555:       field = form.field_for(attribute)
556:       field_id = field.id
557:       raise "Unable to retrieve field id for key '#{attribute.inspect}' (#{attribute.class.name})." unless field_id
558:       begin
559:         value = @ars_entry.get_field_value(field_id)
560:       rescue StandardError => error
561:         raise Exceptions::InternalError.process(error)
562:       end
563:       FieldValue.to_ruby(value, :field_id => field_id, :form => form, :entry => self) if value
564:     end

TODO: Document ArsModels::Form#save!

[Source]

     # File lib/ars_models/entry.rb, line 610
610:     def save!(options={})
611:       # Check for bad options

612:       validate_options(options, :optional => [:context, :field_values, :fields, :field_ids, :form])
613: 
614:       # Raise an exception if the form specified is not the correct form

615:       raise "Conflicting forms" if (form && options[:form]) && form.name != options[:form].name
616: 
617:       # Define the default options

618:       default_options = {}
619:       default_options[:context] = context if context
620:       default_options[:form] = form
621:       default_options[:fields] = field_value_ids
622: 
623:       # Delete this instance

624:       self.class.save!(self, default_options.merge(options))
625:     end

TODO: Document ArsModels::Entry#set_field_value

[Source]

     # File lib/ars_models/entry.rb, line 568
568:     def set_field_value(attribute, value)
569:       raise 'Unable to process field value without form definition' unless form
570:       field = form.field_for(attribute)
571:       raise "Unable to retrieve field id for key '#{attribute.inspect}' (#{attribute.class.name})." unless field.id
572:       new_value = FieldValue.to_java(field, value, @ars_entry.get_field_value(field.id))
573:       @ars_entry.set_field_value(field.id, new_value)
574:       @modified_attributes.nil? ? @modified_attributes = {field.id => value} : @modified_attributes[field.id] = value
575:       self
576:     end

TODO: Document ArsModels::Form#to_xml

[Source]

     # File lib/ars_models/entry.rb, line 657
657:     def to_xml
658:       # Call the java method

659:       @ars_entry.to_xml_string
660:     end

TODO: Document ArsModels::Form#update_attributes!

[Source]

     # File lib/ars_models/entry.rb, line 595
595:     def update_attributes!(attributes, options={})
596:       # Check for bad options

597:       validate_options(options, :optional => [:context, :fields])
598: 
599:       # Define the default options

600:       default_options = {}
601:       default_options[:context] = context if context
602:       default_options[:form] = form
603:       default_options[:fields] = field_value_ids
604: 
605:       # Save the update

606:       self.class.save!(self, default_options.merge(options).merge(:field_values => attributes))
607:     end

[Validate]