parse-stack 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +77 -0
- data/LICENSE +20 -0
- data/README.md +1281 -0
- data/Rakefile +12 -0
- data/bin/console +20 -0
- data/bin/server +10 -0
- data/bin/setup +7 -0
- data/lib/parse/api/all.rb +13 -0
- data/lib/parse/api/analytics.rb +16 -0
- data/lib/parse/api/apps.rb +37 -0
- data/lib/parse/api/batch.rb +148 -0
- data/lib/parse/api/cloud_functions.rb +18 -0
- data/lib/parse/api/config.rb +22 -0
- data/lib/parse/api/files.rb +21 -0
- data/lib/parse/api/hooks.rb +68 -0
- data/lib/parse/api/objects.rb +77 -0
- data/lib/parse/api/push.rb +16 -0
- data/lib/parse/api/schemas.rb +25 -0
- data/lib/parse/api/sessions.rb +11 -0
- data/lib/parse/api/users.rb +43 -0
- data/lib/parse/client.rb +225 -0
- data/lib/parse/client/authentication.rb +59 -0
- data/lib/parse/client/body_builder.rb +69 -0
- data/lib/parse/client/caching.rb +103 -0
- data/lib/parse/client/protocol.rb +15 -0
- data/lib/parse/client/request.rb +43 -0
- data/lib/parse/client/response.rb +116 -0
- data/lib/parse/model/acl.rb +182 -0
- data/lib/parse/model/associations/belongs_to.rb +121 -0
- data/lib/parse/model/associations/collection_proxy.rb +202 -0
- data/lib/parse/model/associations/has_many.rb +218 -0
- data/lib/parse/model/associations/pointer_collection_proxy.rb +71 -0
- data/lib/parse/model/associations/relation_collection_proxy.rb +134 -0
- data/lib/parse/model/bytes.rb +50 -0
- data/lib/parse/model/core/actions.rb +499 -0
- data/lib/parse/model/core/properties.rb +377 -0
- data/lib/parse/model/core/querying.rb +100 -0
- data/lib/parse/model/core/schema.rb +92 -0
- data/lib/parse/model/date.rb +50 -0
- data/lib/parse/model/file.rb +127 -0
- data/lib/parse/model/geopoint.rb +98 -0
- data/lib/parse/model/model.rb +120 -0
- data/lib/parse/model/object.rb +347 -0
- data/lib/parse/model/pointer.rb +106 -0
- data/lib/parse/model/push.rb +99 -0
- data/lib/parse/query.rb +378 -0
- data/lib/parse/query/constraint.rb +130 -0
- data/lib/parse/query/constraints.rb +176 -0
- data/lib/parse/query/operation.rb +66 -0
- data/lib/parse/query/ordering.rb +49 -0
- data/lib/parse/stack.rb +11 -0
- data/lib/parse/stack/version.rb +5 -0
- data/lib/parse/webhooks.rb +228 -0
- data/lib/parse/webhooks/payload.rb +115 -0
- data/lib/parse/webhooks/registration.rb +139 -0
- data/parse-stack.gemspec +45 -0
- metadata +340 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative "properties"
|
2
|
+
# This class adds methods to Parse::Objects in order to create a JSON Parse schema
|
3
|
+
# in order to support table creation and table alterations.
|
4
|
+
module Parse
|
5
|
+
module Schema
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
# returns the schema of the defined class in the Parse JSON format.
|
14
|
+
def schema
|
15
|
+
sch = { className: parse_class, fields: {} }
|
16
|
+
#first go through all the attributes
|
17
|
+
attributes.each do |k,v|
|
18
|
+
# don't include the base Parse fields
|
19
|
+
next if Parse::Properties::BASE.include?(k)
|
20
|
+
next if v.nil?
|
21
|
+
result = { type: v.to_s.camelize }
|
22
|
+
# if it is a basic column property, find the right datatype
|
23
|
+
case v
|
24
|
+
when :integer, :float
|
25
|
+
result[:type] = "Number".freeze
|
26
|
+
when :geopoint, :geo_point
|
27
|
+
result[:type] = "GeoPoint".freeze
|
28
|
+
when :pointer
|
29
|
+
result = { type: "Pointer".freeze, targetClass: references[k] }
|
30
|
+
when :acl
|
31
|
+
result[:type] = "ACL".freeze
|
32
|
+
else
|
33
|
+
result[:type] = v.to_s.camelize
|
34
|
+
end
|
35
|
+
|
36
|
+
sch[:fields][k] = result
|
37
|
+
|
38
|
+
end
|
39
|
+
#then add all the relational column attributes
|
40
|
+
relations.each do |k,v|
|
41
|
+
sch[:fields][k] = { type: "Relation".freeze, targetClass: relations[k] }
|
42
|
+
end
|
43
|
+
sch
|
44
|
+
end
|
45
|
+
|
46
|
+
# updates the remote schema using Parse::Client
|
47
|
+
def update_schema(schema_updates = nil)
|
48
|
+
schema_updates ||= schema
|
49
|
+
client.update_schema parse_class, schema_updates
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_schema
|
53
|
+
client.create_schema parse_class, schema
|
54
|
+
end
|
55
|
+
|
56
|
+
# fetches the current schema of this table.
|
57
|
+
def fetch_schema
|
58
|
+
client.schema parse_class
|
59
|
+
end
|
60
|
+
|
61
|
+
# A class method for non-destructive auto upgrading a remote schema based on the properties
|
62
|
+
# and relations you have defined. If the table doesn't exist, we create the schema
|
63
|
+
# from scratch - otherwise we fetched the current schema, calculate the differences
|
64
|
+
# and add the missing columns. WE DO NOT REMOVE any columns.
|
65
|
+
def auto_upgrade!
|
66
|
+
response = fetch_schema
|
67
|
+
if response.success?
|
68
|
+
#let's figure out the diff fields
|
69
|
+
remote_fields = response.result["fields"]
|
70
|
+
current_schema = schema
|
71
|
+
current_schema[:fields] = current_schema[:fields].reduce({}) do |h,(k,v)|
|
72
|
+
#if the field does not exist in Parse, then add it to the update list
|
73
|
+
h[k] = v if remote_fields[k.to_s].nil?
|
74
|
+
h
|
75
|
+
end
|
76
|
+
return if current_schema[:fields].empty?
|
77
|
+
return update_schema( current_schema )
|
78
|
+
else
|
79
|
+
return create_schema
|
80
|
+
end
|
81
|
+
#fetch_schema.success? ? update_schema : create_schema
|
82
|
+
end
|
83
|
+
#def diff(h2);self.dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| self.has_key?(k) }); end;
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
def schema
|
88
|
+
self.class.schema
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'active_model'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/inflector'
|
5
|
+
require 'active_support/core_ext/object'
|
6
|
+
require 'active_model_serializers'
|
7
|
+
require_relative 'model'
|
8
|
+
|
9
|
+
# Parse has a specific date format. One of the supported types is a date string in
|
10
|
+
# ISO 8601 format (including milliseconds). The other is a hash object that contains
|
11
|
+
# the similar information. When sending data to Parse, we need to use the hash object,
|
12
|
+
# but when receiving data from Parse, we may either get the string version or the hash version.
|
13
|
+
# To make things easier to use in ruby, th Parse::Date class inherits from the DateTime class.
|
14
|
+
# This will allow us to use all the great ActiveSupport methods for date (ex. 3.days.ago) while
|
15
|
+
# providing our own encoding for sending to Parse.
|
16
|
+
module Parse
|
17
|
+
class Date < ::DateTime
|
18
|
+
include ::ActiveModel::Model
|
19
|
+
include ::ActiveModel::Serializers::JSON
|
20
|
+
def self.parse_class; Parse::Model::TYPE_DATE; end;
|
21
|
+
def parse_class; self.class.parse_class; end;
|
22
|
+
alias_method :__type, :parse_class
|
23
|
+
|
24
|
+
# called when encoding to JSON.
|
25
|
+
def attributes
|
26
|
+
{ __type: :string, iso: :string }.freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
# this method is defined because it is used by JSON encoding
|
30
|
+
def iso
|
31
|
+
to_time.utc.iso8601(3) #include milliseconds
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# To enable conversion of other date class objects, we will add a mixin to turn
|
38
|
+
# Time and DateTime objects to Parse::Date objects
|
39
|
+
class Time
|
40
|
+
def parse_date
|
41
|
+
Parse::Date.parse self.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
class DateTime
|
47
|
+
def parse_date
|
48
|
+
Parse::Date.parse self.to_s
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext/object'
|
3
|
+
require_relative "model"
|
4
|
+
require 'open-uri'
|
5
|
+
# Parse File objects are the only non Parse::Object subclass that has a save method.
|
6
|
+
# In general, a Parse File needs content and a specific mime-type to be created. When it is saved, it
|
7
|
+
# is sent to AWS/S3 to be saved (by Parse), and the result is a new URL pointing to that file.
|
8
|
+
# This however is return as a type of File pointer object (hash format)
|
9
|
+
# It only has two fields, the absolute URL of the file and the basename of the file.
|
10
|
+
# The contents and mime_type are only present when creating a new file locally and are not
|
11
|
+
# stored as part of the parse pointer.
|
12
|
+
module Parse
|
13
|
+
|
14
|
+
class File < Model
|
15
|
+
|
16
|
+
attr_accessor :name, :url
|
17
|
+
attr_accessor :contents, :mime_type
|
18
|
+
def self.parse_class; TYPE_FILE; end;
|
19
|
+
def parse_class; self.class.parse_class; end;
|
20
|
+
alias_method :__type, :parse_class
|
21
|
+
FIELD_NAME = "name".freeze
|
22
|
+
FIELD_URL = "url".freeze
|
23
|
+
|
24
|
+
# The initializer to create a new file supports different inputs.
|
25
|
+
# If the first paramter is a string which starts with 'http', we then download
|
26
|
+
# the content of the file (and use the detected mime-type) to set the content and mime_type fields.
|
27
|
+
# If the first parameter is a hash, we assume it might be the Parse File hash format which contains url and name fields only.
|
28
|
+
# If the first paramter is a Parse::File, then we copy fields over
|
29
|
+
# Otherwise, creating a new file requires a name, the actual contents (usually from a File.open("local.jpg").read ) and the mime-type
|
30
|
+
def initialize(name, contents = nil, mime_type = "application/octet-stream".freeze)
|
31
|
+
|
32
|
+
if name.is_a?(String) && name.start_with?("http".freeze) #could be url string
|
33
|
+
file = open( name )
|
34
|
+
@contents = file.read
|
35
|
+
@name = File.basename file.base_uri.to_s
|
36
|
+
@mime_type = file.content_type
|
37
|
+
elsif name.is_a?(Hash)
|
38
|
+
self.attributes = name
|
39
|
+
elsif name.is_a?(::File)
|
40
|
+
@contents = contents || name.read
|
41
|
+
@name = File.basename name.to_path
|
42
|
+
elsif name.is_a?(File)
|
43
|
+
@name = name.name
|
44
|
+
@url = name.url
|
45
|
+
else
|
46
|
+
@name = name
|
47
|
+
@contents = contents
|
48
|
+
end
|
49
|
+
if @name.blank?
|
50
|
+
raise "Invalid Parse::File initialization with name '#{@name}'"
|
51
|
+
end
|
52
|
+
|
53
|
+
@mime_type ||= mime_type
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
# This creates a new Parse File Object with from a URL, saves it and returns it
|
58
|
+
def self.create(url)
|
59
|
+
url = url.url if url.is_a?(Parse::File)
|
60
|
+
file = self.new(url)
|
61
|
+
file.save
|
62
|
+
file
|
63
|
+
end
|
64
|
+
|
65
|
+
# A File object is considered saved if the basename of the URL and the name parameters are equal and
|
66
|
+
# the name of the file begins with 'tfss'
|
67
|
+
def saved?
|
68
|
+
@url.present? && @name.present? && @name == File.basename(@url) && @name.start_with?("tfss".freeze)
|
69
|
+
end
|
70
|
+
|
71
|
+
def attributes
|
72
|
+
{ __type: :string, name: :string, url: :string }.freeze
|
73
|
+
end
|
74
|
+
|
75
|
+
def ==(u)
|
76
|
+
return false unless u.is_a?(self.class)
|
77
|
+
@url == u.url
|
78
|
+
end
|
79
|
+
|
80
|
+
def attributes=(h)
|
81
|
+
if h.is_a?(String)
|
82
|
+
@url = h
|
83
|
+
@name = File.basename(h)
|
84
|
+
elsif h.is_a?(Hash)
|
85
|
+
@url = h[FIELD_URL] || h[:url] || @url
|
86
|
+
@name = h[FIELD_NAME] || h[:name] || @name
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# This is a proxy to the ruby ::File.basename method
|
91
|
+
def self.basename(file_name, suffix = nil)
|
92
|
+
if suffix.nil?
|
93
|
+
::File.basename(file_name)
|
94
|
+
else
|
95
|
+
::File.basename(file_name, suffix)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# save (create) the file if it has all the proper fields. You cannot update Parse Files.
|
100
|
+
def save
|
101
|
+
unless saved? || @contents.nil? || @name.nil?
|
102
|
+
response = client.create_file(@name, @contents, @mime_type)
|
103
|
+
unless response.error?
|
104
|
+
result = response.result
|
105
|
+
@name = result[FIELD_NAME] || File.basename(result[FIELD_URL])
|
106
|
+
@url = result[FIELD_URL]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
saved?
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
class Hash
|
118
|
+
# {"name"=>"tfss-cat.jpg", "url"=>"http://files.parsetfss.com/bcf638bb-3db0-4042-b846-7840b345b0d6/tfss-cat.jpg"}
|
119
|
+
# This is a helper method that determines whether a hash looks like a Parse::File hash
|
120
|
+
def parse_file?
|
121
|
+
url = self[Parse::File::FIELD_URL]
|
122
|
+
name = self[Parse::File::FIELD_NAME]
|
123
|
+
(count == 2 || self["__type".freeze] == Parse::File.parse_class) &&
|
124
|
+
url.present? && name.present? &&
|
125
|
+
name == ::File.basename(url) && name.start_with?("tfss".freeze)
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
require_relative "model"
|
3
|
+
|
4
|
+
module Parse
|
5
|
+
|
6
|
+
# A basic geo location object in Parse. It represents a location on a map through a
|
7
|
+
# latitude and longitue.
|
8
|
+
class GeoPoint < Model
|
9
|
+
|
10
|
+
attr_accessor :latitude, :longitude
|
11
|
+
FIELD_LAT = "latitude".freeze
|
12
|
+
FIELD_LNG = "longitude".freeze
|
13
|
+
alias_method :lat, :latitude
|
14
|
+
alias_method :lng, :longitude
|
15
|
+
def self.parse_class; TYPE_GEOPOINT; end;
|
16
|
+
def parse_class; self.class.parse_class; end;
|
17
|
+
alias_method :__type, :parse_class
|
18
|
+
# TODO: Validate the ranges of the geo point for valid lat and lng numbers.
|
19
|
+
# To create a GeoPoint, you can either pass a hash (ex. {latitude: 32, longitue: -117})
|
20
|
+
# or an array (ex. [32,-117]) as the first parameter.
|
21
|
+
# You may also pass a GeoPoint object or both a lat/lng pair (Ex. GeoPoint.new(32, -117) )
|
22
|
+
def initialize(latitude = nil, longitude = nil)
|
23
|
+
@latitude = @longitude = 0.0
|
24
|
+
if latitude.is_a?(Hash) || latitude.is_a?(Array)
|
25
|
+
self.attributes = latitude
|
26
|
+
elsif latitude.is_a?(Numeric) && longitude.is_a?(Numeric)
|
27
|
+
@latitude = latitude
|
28
|
+
@longitude = longitude
|
29
|
+
elsif latitude.is_a?(GeoPoint)
|
30
|
+
@latitude = latitude.latitude
|
31
|
+
@longitude = latitude.longitude
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def attributes
|
36
|
+
{ __type: :string, latitude: :float, longitude: :float }.freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
def max_miles(m)
|
40
|
+
[@latitude,@longitude,m]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Setting lat and lng for an GeoPoint can be done using a hash with the attributes set
|
44
|
+
# or with an array of two items where the first is the lat and the second is the lng (ex. [32.22,-118.81])
|
45
|
+
def attributes=(h)
|
46
|
+
if h.is_a?(Hash)
|
47
|
+
h.symbolize_keys!
|
48
|
+
@latitude = h[:latitude].to_f || h[:lat].to_f || @latitude
|
49
|
+
@longitude = h[:longitude].to_f || h[:lng].to_f || @longitude
|
50
|
+
elsif h.is_a?(Array) && h.count == 2
|
51
|
+
@latitude = h.first.to_f
|
52
|
+
@longitude = h.last.to_f
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def ==(g)
|
57
|
+
return false unless g.is_a?(GeoPoint)
|
58
|
+
@latitude == g.latitude && @longitude == g.longitude
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_a
|
62
|
+
[@latitude,@longitude]
|
63
|
+
end
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
"#<GeoPoint [#{@latitude},#{@longitude}]>"
|
67
|
+
end
|
68
|
+
|
69
|
+
# either GeoPoint, array or lat,lng
|
70
|
+
def distance_in_miles(geopoint,lng = nil)
|
71
|
+
distance_in_km(geopoint, lng) * 0.621371
|
72
|
+
end
|
73
|
+
|
74
|
+
def distance_in_km(geopoint,lng = nil)
|
75
|
+
unless geopoint.is_a?(Parse::GeoPoint)
|
76
|
+
geopoint = Parse::GeoPoint.new(geopoint, lng)
|
77
|
+
end
|
78
|
+
|
79
|
+
dtor = Math::PI/180
|
80
|
+
r = 6378.14
|
81
|
+
r_lat1 = self.latitude * dtor
|
82
|
+
r_lng1 = self.longitude * dtor
|
83
|
+
r_lat2 = geopoint.latitude * dtor
|
84
|
+
r_lng2 = geopoint.longitude * dtor
|
85
|
+
|
86
|
+
delta_lat = r_lat1 - r_lat2
|
87
|
+
delta_lng = r_lng1 - r_lng2
|
88
|
+
|
89
|
+
a = (Math::sin(delta_lat/2.0) ** 2).to_f + (Math::cos(r_lat1) * Math::cos(r_lat2) * ( Math::sin(delta_lng/2.0) ** 2 ) )
|
90
|
+
c = 2.0 * Math::atan2(Math::sqrt(a), Math::sqrt(1.0-a))
|
91
|
+
d = r * c
|
92
|
+
d
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require 'active_support/core_ext/object'
|
5
|
+
require 'active_model_serializers'
|
6
|
+
require_relative '../client'
|
7
|
+
|
8
|
+
# This is the base model for all Parse object-type classes.
|
9
|
+
|
10
|
+
module Parse
|
11
|
+
|
12
|
+
class Model
|
13
|
+
|
14
|
+
include Client::Connectable # allows easy default Parse::Client access
|
15
|
+
include ::ActiveModel::Model
|
16
|
+
include ::ActiveModel::Serializers::JSON # support for JSON Serializers
|
17
|
+
include ::ActiveModel::Dirty # adds dirty tracking support
|
18
|
+
include ::ActiveModel::Conversion
|
19
|
+
extend ::ActiveModel::Callbacks # callback support on save, update, delete, etc.
|
20
|
+
extend ::ActiveModel::Naming # provides the methods for getting class names from Model classes
|
21
|
+
|
22
|
+
# General Parse constants
|
23
|
+
KEY_CLASS_NAME = 'className'.freeze
|
24
|
+
KEY_OBJECT_ID = 'objectId'.freeze
|
25
|
+
KEY_CREATED_AT = 'createdAt'.freeze
|
26
|
+
KEY_UPDATED_AT = 'updatedAt'.freeze
|
27
|
+
CLASS_USER = '_User'.freeze
|
28
|
+
CLASS_INSTALLATION = '_Installation'.freeze
|
29
|
+
TYPE_FILE = "File".freeze
|
30
|
+
TYPE_GEOPOINT = "GeoPoint".freeze
|
31
|
+
TYPE_OBJECT = "Object".freeze
|
32
|
+
TYPE_DATE = "Date".freeze
|
33
|
+
TYPE_BYTES = "Bytes".freeze
|
34
|
+
TYPE_POINTER = "Pointer".freeze
|
35
|
+
TYPE_RELATION = "Relation".freeze
|
36
|
+
TYPE_FIELD = "__type".freeze
|
37
|
+
|
38
|
+
# To support being able to have different ruby class names from the 'table'
|
39
|
+
# names used in Parse, we will need to have a dynamic lookup system where
|
40
|
+
# when a parse class name received, we go through all of our subclasses to determine
|
41
|
+
# which Parse::Object subclass is responsible for handling this Parse table class.
|
42
|
+
# we use @@model_cache to cache the results of the algorithm since we do this frequently
|
43
|
+
# when encoding and decoding objects.
|
44
|
+
@@model_cache = {}
|
45
|
+
def self.autosave_on_create
|
46
|
+
@@autosave_on_create ||= false
|
47
|
+
end
|
48
|
+
def self.autosave_on_create=(bool)
|
49
|
+
@@autosave_on_create = bool
|
50
|
+
end
|
51
|
+
|
52
|
+
class << self
|
53
|
+
|
54
|
+
def raise_on_save_failure
|
55
|
+
@global_raise_on_save_failure ||= false
|
56
|
+
end
|
57
|
+
def raise_on_save_failure=(bool)
|
58
|
+
@global_raise_on_save_failure = bool
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# class method to find the responsible ruby Parse::Object subclass that handles
|
64
|
+
# the provided parse class (str).
|
65
|
+
def self.find_class(str)
|
66
|
+
return Parse::File if str == TYPE_FILE.freeze
|
67
|
+
return Parse::GeoPoint if str == TYPE_GEOPOINT.freeze
|
68
|
+
return Parse::Date if str == TYPE_DATE.freeze
|
69
|
+
# return Parse::User if str == "User".freeze
|
70
|
+
# return Parse::Installation if str == "Installation".freeze
|
71
|
+
|
72
|
+
str = str.to_s
|
73
|
+
# Basically go through all Parse::Object subclasses and see who is has a parse_class
|
74
|
+
# set to this string. We will cache the results for future use.
|
75
|
+
@@model_cache[str] ||= Parse::Object.descendants.find do |f|
|
76
|
+
f.parse_class == str || f.parse_class == "_#{str}"
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
class String
|
87
|
+
# short helper method to provide lower-first-camelcase
|
88
|
+
def columnize
|
89
|
+
return "objectId" if self == "id"
|
90
|
+
camelize(:lower)
|
91
|
+
end;
|
92
|
+
|
93
|
+
#users for properties: ex. :users -> "_User" or :songs -> Song
|
94
|
+
def to_parse_class
|
95
|
+
final_class = self.singularize.camelize
|
96
|
+
klass = Parse::Model.find_class(final_class) || Parse::Model.find_class(self)
|
97
|
+
#handles the case that a class has a custom parse table
|
98
|
+
final_class = klass.parse_class if klass.present?
|
99
|
+
final_class
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Symbol
|
104
|
+
# for compatibility
|
105
|
+
def columnize
|
106
|
+
to_s.columnize.to_sym
|
107
|
+
end
|
108
|
+
|
109
|
+
def singularize
|
110
|
+
to_s.singularize
|
111
|
+
end
|
112
|
+
|
113
|
+
def camelize
|
114
|
+
to_s.camelize
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_parse_class
|
118
|
+
to_s.to_parse_class
|
119
|
+
end
|
120
|
+
end
|