class ParseResource::Base
Constants
- HashWithIndifferentAccess
Attributes
Public Class Methods
Batch requests Sends multiple requests to /batch Set slice_size to send larger batches. Defaults to 20 to prevent timeouts. Parse doesn't support batches of over 20.
# File lib/parse_resource/base.rb, line 210 def self.batch_save(save_objects, slice_size = 20, method = nil) return true if save_objects.blank? load_settings base_uri = "https://api.parse.com/1/batch" app_id = @@settings['app_id'] master_key = @@settings['master_key'] res = RestClient::Resource.new(base_uri, app_id, master_key) # Batch saves seem to fail if they're too big. We'll slice it up into multiple posts if they are. save_objects.each_slice(slice_size) do |objects| # attributes_for_saving batch_json = { "requests" => [] } objects.each do |item| method ||= (item.new?) ? "POST" : "PUT" object_path = "/1/#{item.class.model_name_uri}" object_path = "#{object_path}/#{item.id}" if item.id json = { "method" => method, "path" => object_path } json["body"] = item.attributes_for_saving unless method == "DELETE" batch_json["requests"] << json end res.post(batch_json.to_json, :content_type => "application/json") do |resp, req, res, &block| response = JSON.parse(resp) rescue nil if resp.code == 400 puts resp return false end if response && response.is_a?(Array) && response.length == objects.length merge_all_attributes(objects, response) unless method == "DELETE" end end end true end
Similar to its ActiveRecord counterpart.
@param [Hash] options Added so that you can specify :class_name => '…'. It does nothing at all, but helps you write self-documenting code.
# File lib/parse_resource/base.rb, line 88 def self.belongs_to(parent, options = {}) field(parent) end
# File lib/parse_resource/base.rb, line 326 def self.chunk(attribute) Query.new(self).chunk(attribute) end
Replaced with a batch destroy_all
method. def self.destroy_all(all)
all.each do |object| object.destroy end
end
# File lib/parse_resource/base.rb, line 348 def self.class_attributes @class_attributes ||= {} end
Create a ParseResource::Base
object.
@param [Hash] attributes a `Hash` of attributes @return [ParseResource] an object that subclasses `ParseResource`. Or returns `false` if object fails to save.
# File lib/parse_resource/base.rb, line 334 def self.create(attributes = {}) attributes = HashWithIndifferentAccess.new(attributes) obj = new(attributes) obj.save obj end
# File lib/parse_resource/base.rb, line 268 def self.delete_all(o) raise StandardError.new("Parse Resource: delete_all doesn't exist. Did you mean destroy_all?") end
# File lib/parse_resource/base.rb, line 263 def self.destroy_all(objects=nil) objects ||= self.all batch_save(objects, 20, "DELETE") end
Explicitly adds a field to the model.
@param [Symbol] name the name of the field, eg `:author`. @param [Boolean] val the return value of the field. Only use this within the class.
# File lib/parse_resource/base.rb, line 60 def self.field(fname, val=nil) fname = fname.to_sym class_eval do define_method(fname) do get_attribute("#{fname}") end end unless self.respond_to? "#{fname}=" class_eval do define_method("#{fname}=") do |val| set_attribute("#{fname}", val) val end end end end
Add multiple fields in one line. Same as `#field`, but accepts multiple args.
@param [Array] *args an array of `Symbol`s, `eg :author, :body, :title`.
# File lib/parse_resource/base.rb, line 81 def self.fields(*args) args.each {|f| field(f)} end
Find a ParseResource::Base
object by ID
@param [String] id the ID of the Parse object you want to find. @return [ParseResource] an object that subclasses ParseResource
.
# File lib/parse_resource/base.rb, line 311 def self.find(id) raise RecordNotFound if id.blank? where(:objectId => id).first end
# File lib/parse_resource/base.rb, line 547 def self.included(base) base.extend(ClassMethods) end
Explicitly set Parse.com API keys.
@param [String] app_id the Application ID of your Parse database @param [String] master_key the Master Key of your Parse database
# File lib/parse_resource/base.rb, line 163 def self.load!(app_id, master_key) @@settings = {"app_id" => app_id, "master_key" => master_key} end
# File lib/parse_resource/base.rb, line 272 def self.load_settings @@settings ||= begin path = "config/parse_resource.yml" environment = defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : ENV["RACK_ENV"] YAML.load(ERB.new(File.new(path).read).result)[environment] end @@settings end
# File lib/parse_resource/base.rb, line 250 def self.merge_all_attributes(objects, response) i = 0 objects.each do |item| item.merge_attributes(response[i]["success"]) if response[i] && response[i]["success"] i += 1 end nil end
# File lib/parse_resource/base.rb, line 115 def self.method_missing(method_name, *args) method_name = method_name.to_s if method_name.start_with?("find_by_") attrib = method_name.gsub(/^find_by_/,"") finder_name = "find_all_by_#{attrib}" define_singleton_method(finder_name) do |target_value| where({attrib.to_sym => target_value}).first end send(finder_name, args[0]) elsif method_name.start_with?("find_all_by_") attrib = method_name.gsub(/^find_all_by_/,"") finder_name = "find_all_by_#{attrib}" define_singleton_method(finder_name) do |target_value| where({attrib.to_sym => target_value}).all end send(finder_name, args[0]) else super(method_name.to_sym, *args) end end
Gets the current class's Parse.com base_uri
# File lib/parse_resource/base.rb, line 183 def self.model_base_uri "https://api.parse.com/1/#{model_name_uri}" end
Gets the current class's model name for the URI
# File lib/parse_resource/base.rb, line 172 def self.model_name_uri if self.model_name == "User" "users" elsif self.model_name == "Installation" "installations" else "classes/#{self.model_name}" end end
Instantiates a ParseResource::Base
object
@params [Hash], [Boolean] a `Hash` of attributes and a `Boolean` that should be false only if the object already exists @return [ParseResource::Base] an object that subclasses `Parseresource::Base`
# File lib/parse_resource/base.rb, line 39 def initialize(attributes = {}, new=true) #attributes = HashWithIndifferentAccess.new(attributes) if new @unsaved_attributes = attributes @unsaved_attributes.stringify_keys! else @unsaved_attributes = {} end self.attributes = {} self.error_instances = [] self.attributes.merge!(attributes) self.attributes unless self.attributes.empty? create_setters_and_getters! end
Creates a RESTful resource sends requests to [base_uri]/
# File lib/parse_resource/base.rb, line 196 def self.resource load_settings #refactor to settings['app_id'] etc app_id = @@settings['app_id'] master_key = @@settings['master_key'] RestClient::Resource.new(self.model_base_uri, app_id, master_key) end
# File lib/parse_resource/base.rb, line 259 def self.save_all(objects) batch_save(objects) end
# File lib/parse_resource/base.rb, line 167 def self.settings load_settings end
# File lib/parse_resource/base.rb, line 99 def self.to_date_object(date) date = date.to_time if date.respond_to?(:to_time) {"__type" => "Date", "iso" => date.iso8601} if date && (date.is_a?(Date) || date.is_a?(DateTime) || date.is_a?(Time)) end
Creates a RESTful resource for file uploads sends requests to [base_uri]/files
# File lib/parse_resource/base.rb, line 285 def self.upload(file_instance, filename, options={}) load_settings base_uri = "https://api.parse.com/1/files" #refactor to settings['app_id'] etc app_id = @@settings['app_id'] master_key = @@settings['master_key'] options[:content_type] ||= 'image/jpg' # TODO: Guess mime type here. file_instance = File.new(file_instance, 'rb') if file_instance.is_a? String filename = filename.parameterize private_resource = RestClient::Resource.new "#{base_uri}/#{filename}", app_id, master_key private_resource.post(file_instance, options) do |resp, req, res, &block| return false if resp.code == 400 return JSON.parse(resp) rescue {"code" => 0, "error" => "unknown error"} end false end
Find a ParseResource::Base
object by chaining where method calls.
# File lib/parse_resource/base.rb, line 318 def self.where(*args) Query.new(self).where(*args) end
Public Instance Methods
provides access to @attributes for getting and setting
# File lib/parse_resource/base.rb, line 492 def attributes @attributes ||= self.class.class_attributes @attributes end
AKN 2012-06-18: Shouldn't this also be setting @unsaved_attributes?
# File lib/parse_resource/base.rb, line 498 def attributes=(n) @attributes = n @attributes end
# File lib/parse_resource/base.rb, line 451 def attributes_for_saving @unsaved_attributes = pointerize(@unsaved_attributes) put_attrs = @unsaved_attributes put_attrs.delete('objectId') put_attrs.delete('createdAt') put_attrs.delete('updatedAt') put_attrs end
# File lib/parse_resource/base.rb, line 487 def clean? !dirty? end
# File lib/parse_resource/base.rb, line 404 def create attrs = attributes_for_saving.to_json opts = {:content_type => "application/json"} result = self.resource.post(attrs, opts) do |resp, req, res, &block| return post_result(resp, req, res, &block) end end
Creates getter methods for model fields
# File lib/parse_resource/base.rb, line 142 def create_getters!(k,v) unless self.respond_to? "#{k}" self.class.send(:define_method, "#{k}") do get_attribute("#{k}") end end end
Creates setter methods for model fields
# File lib/parse_resource/base.rb, line 105 def create_setters!(k,v) unless self.respond_to? "#{k}=" self.class.send(:define_method, "#{k}=") do |val| set_attribute("#{k}", val) val end end end
# File lib/parse_resource/base.rb, line 150 def create_setters_and_getters! @attributes.each_pair do |k,v| create_setters!(k,v) create_getters!(k,v) end end
# File lib/parse_resource/base.rb, line 543 def created_at; get_attribute("createdAt"); end
# File lib/parse_resource/base.rb, line 464 def destroy if self.instance_resource.delete @attributes = {} @unsaved_attributes = {} return true end false end
# File lib/parse_resource/base.rb, line 483 def dirty? @unsaved_attributes.length > 0 end
# File lib/parse_resource/base.rb, line 503 def get_attribute(k) attrs = @unsaved_attributes[k.to_s] ? @unsaved_attributes : @attributes case attrs[k] when Hash klass_name = attrs[k]["className"] klass_name = "User" if klass_name == "_User" case attrs[k]["__type"] when "Pointer" result = klass_name.constantize.find(attrs[k]["objectId"]) when "Object" result = klass_name.constantize.new(attrs[k], false) when "Date" result = DateTime.parse(attrs[k]["iso"]).to_time_in_current_zone when "File" result = attrs[k]["url"] when "GeoPoint" result = ParseGeoPoint.new(attrs[k]) end #todo: support other types https://www.parse.com/docs/rest#objects-types else result = attrs["#{k}"] end result end
aliasing for idiomatic Ruby
# File lib/parse_resource/base.rb, line 540 def id; get_attribute("objectId") rescue nil; end
create RESTful resource for the specific Parse object sends requests to [base_uri]//[objectId]
# File lib/parse_resource/base.rb, line 371 def instance_resource self.class.resource["#{self.id}"] end
Merges in the return value of a save and resets the unsaved_attributes
# File lib/parse_resource/base.rb, line 426 def merge_attributes(results) @attributes.merge!(results) @attributes.merge!(@unsaved_attributes) @unsaved_attributes = {} create_setters_and_getters! @attributes end
Gets the current instance's parent class's Parse.com base_uri
# File lib/parse_resource/base.rb, line 188 def model_base_uri self.class.send(:model_base_uri) end
# File lib/parse_resource/base.rb, line 360 def new? !persisted? end
# File lib/parse_resource/base.rb, line 541 def objectId; get_attribute("objectId") rescue nil; end
# File lib/parse_resource/base.rb, line 352 def persisted? if id true else false end end
# File lib/parse_resource/base.rb, line 375 def pointerize(hash) new_hash = {} hash.each do |k, v| if v.respond_to?(:to_pointer) new_hash[k] = v.to_pointer elsif v.is_a?(Date) || v.is_a?(Time) || v.is_a?(DateTime) new_hash[k] = self.class.to_date_object(v) else new_hash[k] = v end end new_hash end
# File lib/parse_resource/base.rb, line 434 def post_result(resp, req, res, &block) if resp.code.to_s == "200" || resp.code.to_s == "201" merge_attributes(JSON.parse(resp)) return true else error_response = JSON.parse(resp) if error_response["error"] pe = ParseError.new(error_response["code"], error_response["error"]) else pe = ParseError.new(resp.code.to_s) end self.errors.add(pe.code.to_s.to_sym, pe.msg) self.error_instances << pe return false end end
# File lib/parse_resource/base.rb, line 473 def reload return false if new? fresh_object = self.class.find(id) @attributes.update(fresh_object.instance_variable_get('@attributes')) @unsaved_attributes = {} self end
delegate from Class method
# File lib/parse_resource/base.rb, line 365 def resource self.class.resource end
# File lib/parse_resource/base.rb, line 389 def save if valid? run_callbacks :save do if new? return create else return update end end else false end rescue false end
# File lib/parse_resource/base.rb, line 527 def set_attribute(k, v) if v.is_a?(Date) || v.is_a?(Time) || v.is_a?(DateTime) v = self.class.to_date_object(v) elsif v.respond_to?(:to_pointer) v = v.to_pointer end @unsaved_attributes[k.to_s] = v unless v == @attributes[k.to_s] # || @unsaved_attributes[k.to_s] @attributes[k.to_s] = v v end
# File lib/parse_resource/base.rb, line 92 def to_pointer klass_name = self.class.model_name klass_name = "_User" if klass_name == "User" klass_name = "_Installation" if klass_name == "Installation" {"__type" => "Pointer", "className" => klass_name.to_s, "objectId" => self.id} end
# File lib/parse_resource/base.rb, line 412 def update(attributes = {}) attributes = HashWithIndifferentAccess.new(attributes) @unsaved_attributes.merge!(attributes) put_attrs = attributes_for_saving.to_json opts = {:content_type => "application/json"} result = self.instance_resource.put(put_attrs, opts) do |resp, req, res, &block| return post_result(resp, req, res, &block) end end
# File lib/parse_resource/base.rb, line 460 def update_attributes(attributes = {}) self.update(attributes) end
# File lib/parse_resource/base.rb, line 545 def updated_at; get_attribute("updatedAt"); rescue nil; end