Class: ParseResource::Base

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Callbacks, ActiveModel::Naming
Includes:
ActiveModel::AttributeMethods, ActiveModel::Conversion, ActiveModel::Validations
Defined in:
lib/base.rb

Direct Known Subclasses

ParseUser

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

HashWithIndifferentAccess =
ActiveSupport::HashWithIndifferentAccess

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}, new = true) ⇒ ParseResource::Base

Instantiates a ParseResource::Base object



34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/base.rb', line 34

def initialize(attributes = {}, new=true)
  #attributes = HashWithIndifferentAccess.new(attributes)
  
  if new
    @unsaved_attributes = attributes
  else
    @unsaved_attributes = {}
  end
  self.attributes = {}
  self.attributes.merge!(attributes)
  self.attributes unless self.attributes.empty?
  create_setters!
end

Class Method Details

.allArray

Find all ParseResource::Base objects for that model.

Returns:

  • (Array)

    an ‘Array` of objects that subclass `ParseResource`.



233
234
235
# File 'lib/base.rb', line 233

def all
  Query.new(self).all
end

.belongs_to(parent) ⇒ Object



72
73
74
# File 'lib/base.rb', line 72

def self.belongs_to(parent)
  field(parent)
end

.class_attributesObject



264
265
266
# File 'lib/base.rb', line 264

def class_attributes
  @class_attributes ||= {}
end

.countObject

Add this at the end of a method chain to get the count of objects, instead of an Array of objects



225
226
227
228
# File 'lib/base.rb', line 225

def count
  #https://www.parse.com/docs/rest#queries-counting
  Query.new(self).count(1)
end

.create(attributes = {}) ⇒ ParseResource

Create a ParseResource::Base object.

Parameters:

  • attributes (Hash) (defaults to: {})

    a ‘Hash` of attributes

Returns:

  • (ParseResource)

    an object that subclasses ‘ParseResource`. Or returns `false` if object fails to save.



253
254
255
256
# File 'lib/base.rb', line 253

def create(attributes = {})
  attributes = HashWithIndifferentAccess.new(attributes)
  new(attributes).save
end

.destroy_allObject



258
259
260
261
262
# File 'lib/base.rb', line 258

def destroy_all
  all.each do |object|
    object.destroy
  end
end

.field(name, val = nil) ⇒ Object

Explicitly adds a field to the model.

Parameters:

  • name (Symbol)

    the name of the field, eg ‘:author`.

  • val (Boolean) (defaults to: nil)

    the return value of the field. Only use this within the class.



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/base.rb', line 52

def self.field(name, val=nil)
  class_eval do
    define_method(name) do
      @attributes[name] ? @attributes[name] : @unsaved_attributes[name]
    end
    define_method("#{name}=") do |val|
      @attributes[name] = val
      @unsaved_attributes[name] = val
      val
    end
  end
end

.fields(*args) ⇒ Object

Add multiple fields in one line. Same as ‘#field`, but accepts multiple args.

Parameters:

  • *args (Array)

    an array of ‘Symbol`s, `eg :author, :body, :title`.



68
69
70
# File 'lib/base.rb', line 68

def self.fields(*args)
  args.each {|f| field(f)}
end

.find(id) ⇒ ParseResource

Find a ParseResource::Base object by ID

Parameters:

  • id (String)

    the ID of the Parse object you want to find.

Returns:



207
208
209
# File 'lib/base.rb', line 207

def find(id)
  where(:objectId => id).first
end

.firstObject

Find the first object. Fairly random, not based on any specific condition.



239
240
241
# File 'lib/base.rb', line 239

def first
  Query.new(self).limit(1).first
end

.has_many(children) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/base.rb', line 125

def has_many(children)
  parent_klass_name = model_name
  lowercase_parent_klass_name = parent_klass_name.downcase
  parent_klass = model_name.constantize
  child_klass_name = children.to_s.singularize.camelize
  child_klass = child_klass_name.constantize
  
  if parent_klass_name == "User"
    parent_klass_name = "_User"
  end
  
  @@parent_klass_name = parent_klass_name
  #@@parent_instance = self
  
  send(:define_method, children) do
    @@parent_id = self.id
    @@parent_instance = self
    
    
    singleton = child_klass.where(@@parent_klass_name.downcase => @@parent_instance.to_pointer).all

    class << singleton
      def <<(child)
        if @@parent_instance.respond_to?(:to_pointer)
          child.send("#{@@parent_klass_name.downcase}=", @@parent_instance.to_pointer)
          child.save
        end
        super(child)
      end
    end
              
    singleton
  end
  
end

.include_object(parent) ⇒ Object

Include the attributes of a parent ojbect in the results Similar to ActiveRecord eager loading



220
221
222
# File 'lib/base.rb', line 220

def include_object(parent)
  Query.new(self).include_object(parent)
end

.included(base) ⇒ Object



398
399
400
# File 'lib/base.rb', line 398

def self.included(base)
  base.extend(ClassMethods)
end

.limit(n) ⇒ Object

Limits the number of objects returned



245
246
247
# File 'lib/base.rb', line 245

def limit(n)
  Query.new(self).limit(n)
end

.load!(app_id, master_key) ⇒ Object

Explicitly set Parse.com API keys.

Parameters:

  • app_id (String)

    the Application ID of your Parse database

  • master_key (String)

    the Master Key of your Parse database



167
168
169
# File 'lib/base.rb', line 167

def load!(app_id, master_key)
  @@settings = {"app_id" => app_id, "master_key" => master_key}
end

.resourceObject

Creates a RESTful resource sends requests to [base_uri]/



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/base.rb', line 184

def resource
  if @@settings.nil?
    path = "config/parse_resource.yml"
    environment = defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : ENV["RACK_ENV"]
    @@settings = YAML.load(ERB.new(File.new(path).read).result)[environment]
  end

  if model_name == "User" #https://parse.com/docs/rest#users-signup
    base_uri = "https://api.parse.com/1/users"
  else
    base_uri = "https://api.parse.com/1/classes/#{model_name}"
  end

  #refactor to settings['app_id'] etc
  app_id     = @@settings['app_id']
  master_key = @@settings['master_key']
  RestClient::Resource.new(base_uri, app_id, master_key)
end

.settingsObject



171
172
173
174
175
176
177
178
179
# File 'lib/base.rb', line 171

def settings
  if @@settings.nil?
    path = "config/parse_resource.yml"
    #environment = defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : ENV["RACK_ENV"]
    environment = ENV["RACK_ENV"]
    @@settings = YAML.load(ERB.new(File.new(path).read).result)[environment]
  end
  @@settings
end

.where(*args) ⇒ Object

Find a ParseResource::Base object by chaining #where method calls.



213
214
215
# File 'lib/base.rb', line 213

def where(*args)
  Query.new(self).where(*args)
end

Instance Method Details

#attributesObject

provides access to @attributes for getting and setting



381
382
383
384
# File 'lib/base.rb', line 381

def attributes
  @attributes ||= self.class.class_attributes
  @attributes
end

#attributes=(n) ⇒ Object



386
387
388
389
# File 'lib/base.rb', line 386

def attributes=(n)
  @attributes = n
  @attributes
end

#createObject



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/base.rb', line 293

def create
  opts = {:content_type => "application/json"}
  attrs = @unsaved_attributes.to_json
  result = self.resource.post(attrs, opts) do |resp, req, res, &block|
    
    case resp.code 
    when 400
      
      # https://www.parse.com/docs/ios/api/Classes/PFConstants.html
      error_response = JSON.parse(resp)
      pe = ParseError.new(error_response["code"]).to_array
      self.errors.add(pe[0], pe[1])
    
    else
      @attributes.merge!(JSON.parse(resp))
      @attributes.merge!(@unsaved_attributes)
      attributes = HashWithIndifferentAccess.new(attributes)
      @unsaved_attributes = {}
      create_setters!
    end
    
    self
  end

  result
end

#create_setters!Object

Creates getter and setter methods for model fields



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/base.rb', line 85

def create_setters!
  @attributes.each_pair do |k,v|
    
    self.class.send(:define_method, "#{k}=") do |val|
      if k.is_a?(Symbol)
        k = k.to_s
      end
      
      val = val.to_pointer if val.respond_to?(:to_pointer)

      @attributes[k.to_s] = val
      @unsaved_attributes[k.to_s] = val
      
      val
    end
    
    self.class.send(:define_method, "#{k}") do
              
      case @attributes[k]
      when Hash
        
        case @attributes[k]["__type"]
        when "Pointer"
          klass_name = @attributes[k]["className"]
          klass_name = "User" if klass_name == "_User"
          result = klass_name.constantize.find(@attributes[k]["objectId"])
        end #todo: support Dates and other types https://www.parse.com/docs/rest#objects-types
        
      else
        result =  @attributes[k]
      end
      
      result
    end
    
  end
end

#created_atObject



394
# File 'lib/base.rb', line 394

def created_at; self.createdAt; end

#destroyObject



373
374
375
376
377
378
# File 'lib/base.rb', line 373

def destroy
  self.instance_resource.delete
  @attributes = {}
  @unsaved_attributes = {}
  nil
end

#idObject

aliasing for idiomatic Ruby



392
# File 'lib/base.rb', line 392

def id; self.objectId rescue nil; end

#instance_resourceObject

create RESTful resource for the specific Parse object sends requests to [base_uri]//[objectId]



289
290
291
# File 'lib/base.rb', line 289

def instance_resource
  self.class.resource["#{self.id}"]
end

#new?Boolean

Returns:

  • (Boolean)


278
279
280
# File 'lib/base.rb', line 278

def new?
  !persisted?
end

#persisted?Boolean

Returns:

  • (Boolean)


270
271
272
273
274
275
276
# File 'lib/base.rb', line 270

def persisted?
  if id
    true
  else
    false
  end
end

#resourceObject

delegate from Class method



283
284
285
# File 'lib/base.rb', line 283

def resource
  self.class.resource
end

#saveObject



320
321
322
323
324
325
326
327
328
329
# File 'lib/base.rb', line 320

def save
  if valid?
    run_callbacks :save do
      new? ? create : update
    end
  else
    false
  end
  rescue false
end

#to_pointerObject



77
78
79
80
81
# File 'lib/base.rb', line 77

def to_pointer
  klass_name = self.class.model_name
  klass_name = "_User" if klass_name == "User"
  {"__type" => "Pointer", "className" => klass_name, "objectId" => self.id}
end

#update(attributes = {}) ⇒ Object



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/base.rb', line 331

def update(attributes = {})
  
  attributes = HashWithIndifferentAccess.new(attributes)
    
  @unsaved_attributes.merge!(attributes)

  put_attrs = @unsaved_attributes
  put_attrs.delete('objectId')
  put_attrs.delete('createdAt')
  put_attrs.delete('updatedAt')
  put_attrs = put_attrs.to_json
  
  opts = {:content_type => "application/json"}
  result = self.instance_resource.put(put_attrs, opts) do |resp, req, res, &block|

    case resp.code
    when 400
      
      # https://www.parse.com/docs/ios/api/Classes/PFConstants.html
      error_response = JSON.parse(resp)
      pe = ParseError.new(error_response["code"], error_response["error"]).to_array
      self.errors.add(pe[0], pe[1])
      
    else

      @attributes.merge!(JSON.parse(resp))
      @attributes.merge!(@unsaved_attributes)
      @unsaved_attributes = {}
      create_setters!

      self
    end
    
    result
  end
 
end

#update_attributes(attributes = {}) ⇒ Object



369
370
371
# File 'lib/base.rb', line 369

def update_attributes(attributes = {})
  self.update(attributes)
end

#updated_atObject



396
# File 'lib/base.rb', line 396

def updated_at; self.updatedAt rescue nil; end