Module: MotionModel::Model::PublicClassMethods

Defined in:
lib/motion_model/model/model.rb,
lib/motion_model/model/persistence.rb

Instance Method Summary collapse

Instance Method Details

#allObject

Returns query result as an array



236
237
238
# File 'lib/motion_model/model/model.rb', line 236

def all
  @collection
end

#belongs_to(relation) ⇒ Object

Use at class level, as follows

class Assignee
  include MotionModel::Model

  columns :assignee_name, :department
  belongs_to :task

Allows code like this:

Assignee.find(:assignee_name).like('smith').first.task


138
139
140
141
# File 'lib/motion_model/model/model.rb', line 138

def belongs_to(relation)
  add_field relation, :belongs_to
  add_field generate_belongs_to_id(relation), :belongs_to_id    # a relation is singular.
end

#bulk_update(&block) ⇒ Object

Use to do bulk insertion, updating, or deleting without making repeated calls to a delegate. E.g., when syncing with an external data source.



60
61
62
63
64
# File 'lib/motion_model/model/model.rb', line 60

def bulk_update(&block)
  @_issue_notifications = false
  class_eval &block
  @_issue_notifications = true
end

#column?(column) ⇒ Boolean

Returns true if a column exists on this model, otherwise false.

Returns:

  • (Boolean)


144
145
146
# File 'lib/motion_model/model/model.rb', line 144

def column?(column)
  respond_to?(column)
end

#columns(*fields) ⇒ Object

Macro to define names and types of columns. It can be used in one of two forms:

Pass a hash, and you define columns with types. E.g.,

columns :name => :string, :age => :integer

Pass a hash of hashes and you can specify defaults such as:

columns :name => {:type => :string, :default => 'Joe Bob'}, :age => :integer

Pass an array, and you create column names, all of which have type :string.

columns :name, :age, :hobby


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/motion_model/model/model.rb', line 81

def columns(*fields)
  return @_columns.map{|c| c.name} if fields.empty?

  case fields.first
  when Hash
    column_from_hash fields
  when String, Symbol
    column_from_string_or_sym fields
  else
    raise ArgumentError.new("arguments to `columns' must be a symbol, a hash, or a hash of hashes -- was #{fields.first}.")
  end

  unless self.respond_to?(:id)
    add_field(:id, :integer)
  end
end

#create(options = {}) ⇒ Object

Creates an object and saves it. E.g.:

@bob = Person.create(:name => 'Bob', :hobby => 'Bird Watching')

returns the object created or false.



163
164
165
166
167
# File 'lib/motion_model/model/model.rb', line 163

def create(options = {})
  row = self.new(options)
  row.save
  row
end

#default(column) ⇒ Object

returns default value for this column or nil.



154
155
156
# File 'lib/motion_model/model/model.rb', line 154

def default(column)
  column_named(column).default || nil
end

#delete_allObject

Deletes all rows in the model – no hooks are called and deletes are not cascading so this does not affected related data.



177
178
179
180
181
182
183
184
185
186
# File 'lib/motion_model/model/model.rb', line 177

def delete_all
  # Do each delete so any on_delete and
  # cascades are called, then empty the
  # collection and compact the array.
  bulk_update do
    @collection.each{|item| item.delete}
  end
  @collection = []
  @_next_id = 1
end

#deserialize_from_file(file_name = nil) ⇒ Object

Returns the unarchived object if successful, otherwise false

Note that subsequent calls to serialize/deserialize methods will remember the file name, so they may omit that argument.

Raises a MotionModel::PersistFileFailureError on failure.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/motion_model/model/persistence.rb', line 12

def deserialize_from_file(file_name = nil)
  @file_name = file_name if file_name
  
  if File.exist? documents_file(@file_name)
    error_ptr = Pointer.new(:object)

    data = NSData.dataWithContentsOfFile(documents_file(@file_name), options:NSDataReadingMappedIfSafe, error:error_ptr)
    
    if data.nil?
      error = error_ptr[0]
      raise MotionModel::PersistFileFailureError.new "Error when reading the data: #{error}"
    else
      bulk_update do
        collection = NSKeyedUnarchiver.unarchiveObjectWithData(data)
      end
      return self
    end
  else
    return false
  end
end

#destroy_allObject

Destroys all rows in the model – before_delete and after_delete hooks are called and deletes are not cascading if declared with :delete => destroy in the has_many macro.



191
192
193
194
195
196
197
198
199
# File 'lib/motion_model/model/model.rb', line 191

def destroy_all
  ids = self.all.map{|item| item.id}
  bulk_update do
    ids.each do |item|
      find(item).destroy
    end
  end
  # Note collection is not emptied, and next_id is not reset.
end

#documents_file(file_name) ⇒ Object



57
58
59
60
# File 'lib/motion_model/model/persistence.rb', line 57

def documents_file(file_name)
  file_path = File.join NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true), file_name
  file_path
end

#each(&block) ⇒ Object

Raises:

  • (ArgumentError)


244
245
246
247
# File 'lib/motion_model/model/model.rb', line 244

def each(&block)
  raise ArgumentError.new("each requires a block") unless block_given?
  @collection.each{|item| yield item}
end

#empty?Boolean

Returns:

  • (Boolean)


249
250
251
# File 'lib/motion_model/model/model.rb', line 249

def empty?
  @collection.empty?
end

#find(*args, &block) ⇒ Object Also known as: where

Finds row(s) within the data store. E.g.,

@post = Post.find(1)  # find a specific row by ID

or…

@posts = Post.find(:author).eq('bob').all


208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/motion_model/model/model.rb', line 208

def find(*args, &block)
  if block_given?
    matches = @collection.collect do |item|
      item if yield(item)
    end.compact
    return FinderQuery.new(matches)
  end

  unless args[0].is_a?(Symbol) || args[0].is_a?(String)
    target_id = args[0].to_i
    return @collection.select{|element| element.id == target_id}.first
  end

  FinderQuery.new(args[0].to_sym, @collection)
end

#firstObject

Retrieves first row of query



226
227
228
# File 'lib/motion_model/model/model.rb', line 226

def first
  @collection.first
end

#generate_belongs_to_id(relation) ⇒ Object



123
124
125
# File 'lib/motion_model/model/model.rb', line 123

def generate_belongs_to_id(relation)
  (relation.to_s.singularize.underscore + '_id').to_sym
end

#has_many(relation, options = {}) ⇒ Object

Use at class level, as follows:

class Task
  include MotionModel::Model

  columns  :name, :details, :assignees
  has_many :assignees

Note that :assignees must be declared as a virtual attribute on the model before you can has_many on it.

This enables code like:

Task.find(:due_date).gt(Time.now).first.assignees

to get the people assigned to first task that is due after right now.

This must be used with a belongs_to macro in the related model class if you want to be able to access the inverse relation.

Raises:

  • (ArgumentError)


118
119
120
121
# File 'lib/motion_model/model/model.rb', line 118

def has_many(relation, options = {})
  raise ArgumentError.new("arguments to has_many must be a symbol or string.") unless [Symbol, String].include? relation.class
  add_field relation, :has_many, options        # Relation must be plural
end

#lastObject

Retrieves last row of query



231
232
233
# File 'lib/motion_model/model/model.rb', line 231

def last
  @collection.last
end

#lengthObject Also known as: count



169
170
171
# File 'lib/motion_model/model/model.rb', line 169

def length
  @collection.length
end

#order(field_name = nil, &block) ⇒ Object



240
241
242
# File 'lib/motion_model/model/model.rb', line 240

def order(field_name = nil, &block)
  FinderQuery.new(@collection).order(field_name, &block)
end

#serialize_to_file(file_name = nil) ⇒ Object

Serializes data to a persistent store (file, in this terminology). Serialization is synchronous, so this will pause your run loop until complete.

file_name is the name of the persistent store you want to use. If you omit this, it will use the last remembered file name.

Raises a MotionModel::PersistFileError on failure.



42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/motion_model/model/persistence.rb', line 42

def serialize_to_file(file_name = nil)
  @file_name = file_name if file_name
  error_ptr = Pointer.new(:object)
  
  data = NSKeyedArchiver.archivedDataWithRootObject @collection
  unless data.writeToFile(documents_file(@file_name), options: NSDataWritingAtomic, error: error_ptr)
    # De-reference the pointer.
    error = error_ptr[0]

    # Now we can use the `error' object.
    raise MotionModel::PersistFileError.new "Error when writing data: #{error}"
  end
end

#type(column) ⇒ Object

Returns type of this column.



149
150
151
# File 'lib/motion_model/model/model.rb', line 149

def type(column)
  column_named(column).type || nil
end