parse-ruby-client 0.0.1

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.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.4"
12
+ gem "rcov", ">= 0"
13
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,20 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.4)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rake (0.9.2.2)
10
+ rcov (0.9.11)
11
+ shoulda (2.11.3)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ bundler (~> 1.0.0)
18
+ jeweler (~> 1.6.4)
19
+ rcov
20
+ shoulda
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Alan deLevie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,110 @@
1
+ The original creator of parse-ruby-client, [aalpern](http://github.com/aalpern), has decided to stop work on the project. I'm going to give the project new life, first by maintaining the project as a gem, and second by eventually making it power [parse_resource](http://github.com/adelevie/parse_resource) under the hood.
2
+
3
+ # Ruby Client for parse.com REST API
4
+
5
+ This file implements a simple Ruby client library for using Parse's REST API.
6
+ Rather than try to implement an ActiveRecord-compatible library, it tries to
7
+ match the structure of the iOS and Android SDKs from Parse.
8
+
9
+ So far it supports simple GET, PUT, and POST operations on objects. Enough
10
+ to read & write simple data.
11
+
12
+ ## Dependencies
13
+
14
+ This currently depends on the gems 'json' and 'patron' for JSON support and HTTP, respectively.
15
+
16
+ # Getting Started
17
+
18
+ ---
19
+
20
+ To get started, load the parse.rb file and call Parse.init to initialize the client object with
21
+ your application ID and API key values, which you can obtain from the parse.com dashboard.
22
+
23
+ ```ruby
24
+ Parse.init :application_id => "<your_app_id>",
25
+ :api_key => "<your_api_key>"
26
+ ```
27
+
28
+ ## Creating and Saving Objects
29
+
30
+ Create an instance of ```Parse::Object``` with your class name supplied as a string, set some keys, and call ```save()```.
31
+
32
+ ```ruby
33
+ game_score = Parse::Object.new "GameScore"
34
+ game_score["score"] = 1337
35
+ game_score["playerName"] = "Sean Plott"
36
+ game_score["cheatMode"] = false
37
+ game_score.save
38
+ ```
39
+
40
+ Alternatively, you can initialize the object's initial values with a hash.
41
+
42
+ ```ruby
43
+ game_score = Parse::Object.new "GameScore", {
44
+ "score" => 1337, "playerName" => "Sean Plott", "cheatMode" => false
45
+ }
46
+ ```
47
+
48
+ Or if you prefer, you can use symbols for the hash keys - they will be converted to strings
49
+ by ```Parse::Object.initialize()```.
50
+
51
+ ```ruby
52
+ game_score = Parse::Object.new "GameScore", {
53
+ :score => 1337, :playerName => "Sean Plott", :cheatMode => false
54
+ }
55
+ ```
56
+
57
+ ## Retrieving Objects
58
+
59
+ Individual objects can be retrieved with a single call to ```Parse.get()``` supplying the class and object id.
60
+
61
+ ```ruby
62
+ game_score = Parse.get "GameScore", "xWMyZ4YEGZ"
63
+ ```
64
+
65
+ All the objects in a given class can be retrieved by omitting the object ID. Use caution if you have lots
66
+ and lots of objects of that class though.
67
+
68
+ ```ruby
69
+ all_scores = Parse.get "GameScore"
70
+ ```
71
+
72
+ ## Queries
73
+
74
+ Queries are supported by the ```Parse::Query``` class.
75
+
76
+ ```ruby
77
+ # Create some simple objects to query
78
+ (1..100).each { |i|
79
+ score = Parse::Object.new "GameScore"
80
+ score["score"] = i
81
+ score.save
82
+ }
83
+
84
+ # Retrieve all scores between 10 & 20 inclusive
85
+ Parse::Query.new("GameScore") \
86
+ .greater_eq("score", 10) \
87
+ .less_eq("score", 20) \
88
+ .get
89
+
90
+ # Retrieve a set of specific scores
91
+ Parse::Query.new("GameScore") \
92
+ .value_in("score", [10, 20, 30, 40]) \
93
+ .get
94
+
95
+ ```
96
+
97
+ # TODO
98
+
99
+ - Add some form of debug logging
100
+ - ~~Support for Date, Pointer, and Bytes API [data types](https://www.parse.com/docs/rest#objects-types)~~
101
+ - Users
102
+ - ACLs
103
+ - Login
104
+
105
+
106
+ # Resources
107
+
108
+ - parse.com [REST API documentation](https://parse.com/docs/rest)
109
+ - [parse_resource](https://github.com/adelevie/parse_resource) , an ActiveRecord-compatible wrapper
110
+ for the API for seamless integration into Rails.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "parse-ruby-client"
18
+ gem.homepage = "http://github.com/adelevie/parse-ruby-client"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A simple Ruby client for the parse.com REST API}
21
+ gem.description = %Q{A simple Ruby client for the parse.com REST API}
22
+ gem.email = "adelevie@gmail.com"
23
+ gem.authors = ["Alan deLevie", "Adam Alpern"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "parse-ruby-client #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/example.rb ADDED
@@ -0,0 +1,30 @@
1
+ Parse.init :application_id => "your_application_id",
2
+ :api_key => "your_api_key"
3
+
4
+ profile = Parse::Object.new "Profile"
5
+ profile["first_name"] = "John"
6
+ profile["last_name"] = "Doe"
7
+ profile["username"] = "jdoe"
8
+ profile["email_address"] = "jdoe@fubar.com"
9
+ profile["birthday"] = Parse::Date.new "1980-12-25"
10
+ profile.save
11
+
12
+ profile.increment "login_count"
13
+
14
+ # Queries
15
+ cls = "GameScore"
16
+ (1..100).each { |i|
17
+ score = Parse::Object.new cls
18
+ score["score"] = i
19
+ score.save
20
+ }
21
+
22
+ Parse::Query.new(cls) \
23
+ .greater_eq("score", 10) \
24
+ .less_eq("score", 20) \
25
+ .get
26
+
27
+ Parse::Query.new(cls) \
28
+ .value_in("score", [10, 20, 30, 40]) \
29
+ .get
30
+
data/lib/parse.rb ADDED
@@ -0,0 +1,19 @@
1
+ ## ----------------------------------------------------------------------
2
+ ##
3
+ ## Ruby client for parse.com
4
+ ## A quick library for playing with parse.com's REST API for object storage.
5
+ ## See https://parse.com/docs/rest for full documentation on the API.
6
+ ##
7
+ ## ----------------------------------------------------------------------
8
+
9
+ require 'json'
10
+ require 'patron'
11
+ require 'date'
12
+ require 'cgi'
13
+
14
+ cwd = Pathname(__FILE__).dirname
15
+ $:.unshift(cwd.to_s) unless $:.include?(cwd.to_s) || $:.include?(cwd.expand_path.to_s)
16
+
17
+ require 'parse/object'
18
+ require 'parse/query'
19
+ require 'parse/datatypes'
@@ -0,0 +1,103 @@
1
+ require 'parse/protocol'
2
+ require 'parse/error'
3
+ require 'parse/util'
4
+
5
+ module Parse
6
+
7
+ # A class which encapsulates the HTTPS communication with the Parse
8
+ # API server. Currently uses the Patron library for low-level HTTP
9
+ # communication.
10
+ class Client
11
+ attr_accessor :host
12
+ attr_accessor :application_id
13
+ attr_accessor :api_key
14
+ attr_accessor :session
15
+
16
+ def initialize(data = {})
17
+ @host = data[:host] || Protocol::HOST
18
+ @application_id = data[:application_id]
19
+ @api_key = data[:api_key]
20
+ @session = Patron::Session.new
21
+
22
+ @session.base_url = "https://#{host}"
23
+ @session.headers["Content-Type"] = "application/json"
24
+ @session.headers["Accept"] = "application/json"
25
+ @session.headers["User-Agent"] = "Parse for Ruby, 0.0"
26
+ @session.headers[Protocol::HEADER_API_KEY] = @api_key
27
+ @session.headers[Protocol::HEADER_APP_ID] = @application_id
28
+ end
29
+
30
+ # Perform an HTTP request for the given uri and method
31
+ # with common basic response handling. Will raise a
32
+ # ParseProtocolError if the response has an error status code,
33
+ # and will return the parsed JSON body on success, if there is one.
34
+ def request(uri, method = :get, body = nil, query = nil)
35
+ options = {}
36
+ if body
37
+ options[:data] = body
38
+ end
39
+ if query
40
+ options[:query] = query
41
+ end
42
+
43
+ response = @session.request(method, uri, {}, options)
44
+ if response.status >= 400
45
+ raise ParseProtocolError, response
46
+ else
47
+ if response.body
48
+ return JSON.parse response.body
49
+ end
50
+ end
51
+ end
52
+
53
+ def get(uri)
54
+ request(uri)
55
+ end
56
+
57
+ def post(uri, body)
58
+ request(uri, :post, body)
59
+ end
60
+
61
+ def put(uri, body)
62
+ request(uri, :put, body)
63
+ end
64
+
65
+ def delete(uri)
66
+ request(uri, :delete)
67
+ end
68
+
69
+ end
70
+
71
+
72
+ # Module methods
73
+ # ------------------------------------------------------------
74
+
75
+ # A singleton client for use by methods in Object.
76
+ # Always use Parse.client to retrieve the client object.
77
+ @@client = nil
78
+
79
+ # Initialize the singleton instance of Client which is used
80
+ # by all API methods. Parse.init must be called before saving
81
+ # or retrieving any objects.
82
+ def Parse.init(data)
83
+ @@client = Client.new(data)
84
+ end
85
+
86
+ def Parse.client
87
+ if !@@client
88
+ raise ParseError, "API not initialized"
89
+ end
90
+ @@client
91
+ end
92
+
93
+ # Perform a simple retrieval of a simple object, or all objects of a
94
+ # given class. If object_id is supplied, a single object will be
95
+ # retrieved. If object_id is not supplied, then all objects of the
96
+ # given class will be retrieved and returned in an Array.
97
+ def Parse.get(class_name, object_id = nil)
98
+ data = Parse.client.get( Protocol.class_uri(class_name, object_id) )
99
+ Parse.parse_json class_name, data
100
+ end
101
+
102
+ end
103
+
@@ -0,0 +1,74 @@
1
+ require 'base64'
2
+
3
+ module Parse
4
+
5
+ # Pointer
6
+ # ------------------------------------------------------------
7
+
8
+ class Pointer
9
+ attr_accessor :parse_object_id
10
+ attr_accessor :class_name
11
+
12
+ def initialize(data)
13
+ @class_name = data[Protocol::KEY_CLASS_NAME]
14
+ @parse_object_id = data[Protocol::KEY_OBJECT_ID]
15
+ end
16
+
17
+ def to_json(*a)
18
+ {
19
+ Protocol::KEY_TYPE => Protocol::TYPE_POINTER,
20
+ Protocol::KEY_CLASS_NAME => @class_name,
21
+ Protocol::KEY_OBJECT_ID => @parse_object_id
22
+ }.to_json(*a)
23
+ end
24
+
25
+ # Retrieve the Parse object referenced by this pointer.
26
+ def get
27
+ Parse.get @class_name, @parse_object_id
28
+ end
29
+ end
30
+
31
+ # Date
32
+ # ------------------------------------------------------------
33
+
34
+ class Date
35
+ attr_accessor :value
36
+
37
+ def initialize(data)
38
+ if data.is_a? DateTime
39
+ @value = data
40
+ elsif data.is_a? Hash
41
+ @value = DateTime.parse data["iso"]
42
+ elsif data.is_a? String
43
+ @value = DateTime.parse data
44
+ end
45
+ end
46
+
47
+ def to_json(*a)
48
+ {
49
+ Protocol::KEY_TYPE => Protocol::TYPE_DATE,
50
+ "iso" => value.iso8601
51
+ }.to_json(*a)
52
+ end
53
+ end
54
+
55
+ # Bytes
56
+ # ------------------------------------------------------------
57
+
58
+ class Bytes
59
+ attr_accessor :value
60
+
61
+ def initialize(data)
62
+ bytes = data["base64"]
63
+ value = Base64.decode(bytes)
64
+ end
65
+
66
+ def to_json(*a)
67
+ {
68
+ Protocol::KEY_TYPE => Protocol::TYPE_BYTES,
69
+ "base64" => Base64.encode(@value)
70
+ }.to_json(*a)
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,27 @@
1
+ module Parse
2
+
3
+ # Base exception class for errors thrown by the Parse
4
+ # client library. ParseError will be raised by any
5
+ # network operation if Parse.init() has not been called.
6
+ class ParseError < Exception
7
+ end
8
+
9
+ # An exception class raised when the REST API returns an error.
10
+ # The error code and message will be parsed out of the HTTP response,
11
+ # which is also included in the response attribute.
12
+ class ParseProtocolError < ParseError
13
+ attr_accessor :code
14
+ attr_accessor :error
15
+ attr_accessor :response
16
+
17
+ def initialize(response)
18
+ @response = response
19
+ if response.body
20
+ data = JSON.parse response.body
21
+ @code = data["code"]
22
+ @message = data["error"]
23
+ end
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,119 @@
1
+ require 'parse/protocol'
2
+ require 'parse/client'
3
+ require 'parse/error'
4
+
5
+ module Parse
6
+
7
+ # Represents an individual Parse API object.
8
+ class Object < Hash
9
+ attr_reader :parse_object_id
10
+ attr_reader :class_name
11
+ attr_reader :created_at
12
+ attr_reader :updated_at
13
+
14
+ def initialize(class_name, data = nil)
15
+ @class_name = class_name
16
+ if data
17
+ parse data
18
+ end
19
+ end
20
+
21
+ def uri
22
+ Protocol.class_uri @class_name, @parse_object_id
23
+ end
24
+
25
+ def pointer
26
+ Parse::Pointer.new self
27
+ end
28
+
29
+ # Merge a hash parsed from the JSON representation into
30
+ # this instance. This will extract the reserved fields,
31
+ # merge the hash keys, and then insure that the reserved
32
+ # fields do not occur in the underlying hash storage.
33
+ def parse(data)
34
+ if !data
35
+ return
36
+ end
37
+
38
+ @parse_object_id ||= data[Protocol::KEY_OBJECT_ID]
39
+
40
+ if data.has_key? Protocol::KEY_CREATED_AT
41
+ @created_at = DateTime.parse data[Protocol::KEY_CREATED_AT]
42
+ end
43
+
44
+ if data.has_key? Protocol::KEY_UPDATED_AT
45
+ @updated_at = DateTime.parse data[Protocol::KEY_UPDATED_AT]
46
+ end
47
+
48
+ data.each { |k,v|
49
+ if k.is_a? Symbol
50
+ k = k.to_s
51
+ end
52
+ if !Protocol::RESERVED_KEYS.include? k
53
+ self[k] = v
54
+ end
55
+ }
56
+ end
57
+ private :parse
58
+
59
+ # Write the current state of the local object to the API.
60
+ # If the object has never been saved before, this will create
61
+ # a new object, otherwise it will update the existing stored object.
62
+ def save
63
+ method = @parse_object_id ? :put : :post
64
+ body = self.to_json
65
+
66
+ data = Parse.client.request(self.uri, method, body)
67
+ if data
68
+ parse data
69
+ end
70
+ self
71
+ end
72
+
73
+ # Update the fields of the local Parse object with the current
74
+ # values from the API.
75
+ def refresh
76
+ if @parse_object_id
77
+ data = Parse.client.get self.uri
78
+ if data
79
+ parse data
80
+ end
81
+ end
82
+ self
83
+ end
84
+
85
+ # Delete the remote Parse API object.
86
+ def parse_delete
87
+ if @parse_object_id
88
+ response = Parse.client.delete self.uri
89
+ end
90
+ nil
91
+ end
92
+
93
+ # Increment the given field by an amount, which defaults to 1.
94
+ def increment(field, amount = 1)
95
+ value = (self[field] || 0) + amount
96
+ self[field] = value
97
+ if !@parse_object_id
98
+ # TODO - warn that the object must be stored first
99
+ return nil
100
+ end
101
+
102
+ if amount != 0
103
+ op = amount > 0 ? Protocol::OP_INCREMENT : Protocol::OP_DECREMENT
104
+ body = "{\"#{field}\": {\"#{Protocol::KEY_OP}\": \"#{op}\", \"#{Protocol::KEY_AMOUNT}\" : #{amount.abs}}}"
105
+ data = Parse.client.request( self.uri, :put, body)
106
+ parse data
107
+ end
108
+ self
109
+ end
110
+
111
+ # Decrement the given field by an amount, which defaults to 1.
112
+ # A synonym for increment(field, -amount).
113
+ def decrement(field, amount = 1)
114
+ increment field, -amount
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,122 @@
1
+ module Parse
2
+ # A module which encapsulates the specifics of Parse's REST API.
3
+ module Protocol
4
+
5
+ # Basics
6
+ # ----------------------------------------
7
+
8
+ # The default hostname for communication with the Parse API.
9
+ HOST = "api.parse.com"
10
+
11
+ # The version of the REST API implemented by this module.
12
+ VERSION = 1
13
+
14
+ # HTTP Headers
15
+ # ----------------------------------------
16
+
17
+ # The HTTP header used for passing your application ID to the
18
+ # Parse API.
19
+ HEADER_APP_ID = "X-Parse-Application-Id"
20
+
21
+ # The HTTP header used for passing your API key to the
22
+ # Parse API.
23
+ HEADER_API_KEY = "X-Parse-REST-API-Key"
24
+
25
+ # JSON Keys
26
+ # ----------------------------------------
27
+
28
+ # The JSON key used to store the class name of an object
29
+ # in a Pointer datatype.
30
+ KEY_CLASS_NAME = "className"
31
+
32
+ # The JSON key used to store the ID of Parse objects
33
+ # in their JSON representation.
34
+ KEY_OBJECT_ID = "objectId"
35
+
36
+ # The JSON key used to store the creation timestamp of
37
+ # Parse objects in their JSON representation.
38
+ KEY_CREATED_AT = "createdAt"
39
+
40
+ # The JSON key used to store the last modified timestamp
41
+ # of Parse objects in their JSON representation.
42
+ KEY_UPDATED_AT = "updatedAt"
43
+
44
+ # The JSON key used in the top-level response object
45
+ # to indicate that the response contains an array of objects.
46
+ RESPONSE_KEY_RESULTS = "results"
47
+
48
+ # The JSON key used to identify an operator in the increment/decrement
49
+ # API call.
50
+ KEY_OP = "__op"
51
+
52
+ # The JSON key used to identify the datatype of a special value.
53
+ KEY_TYPE = "__type"
54
+
55
+ # The JSON key used to specify the numerical value in the
56
+ # increment/decrement API call.
57
+ KEY_AMOUNT = "amount"
58
+
59
+ RESERVED_KEYS = [ KEY_CLASS_NAME, KEY_CREATED_AT, KEY_OBJECT_ID ]
60
+
61
+ # Other Constants
62
+ # ----------------------------------------
63
+
64
+ # Operation name for incrementing an objects field value remotely
65
+ OP_INCREMENT = "Increment"
66
+
67
+ # Operation name for decrementing an objects field value remotely
68
+ OP_DECREMENT = "Decrement"
69
+
70
+
71
+ # The data type name for special JSON objects representing a reference
72
+ # to another Parse object.
73
+ TYPE_POINTER = "Pointer"
74
+
75
+ # The data type name for special JSON objects containing an array of
76
+ # encoded bytes.
77
+ TYPE_BYTES = "Bytes"
78
+
79
+ # The data type name for special JSON objects representing a date/time.
80
+ TYPE_DATE = "Date"
81
+
82
+ # The data type name for special JSON objects representing a
83
+ # location specified as a latitude/longitude pair.
84
+ TYPE_GEOPOINT = "GeoPoint"
85
+
86
+ # The data type name for special JSON objects representing
87
+ # a file.
88
+ TYPE_FILE = "File"
89
+
90
+ # The class name for User objects, when referenced by a Pointer.
91
+ CLASS_USER = "_User"
92
+
93
+ # URI Helpers
94
+ # ----------------------------------------
95
+
96
+ # Construct a uri referencing a given Parse object
97
+ # class or instance (of object_id is non-nil).
98
+ def Protocol.class_uri(class_name, object_id = nil)
99
+ if object_id
100
+ "/#{VERSION}/classes/#{class_name}/#{object_id}"
101
+ else
102
+ "/#{VERSION}/classes/#{class_name}"
103
+ end
104
+ end
105
+
106
+ # Construct a uri referencing a given Parse user
107
+ # instance or the users category.
108
+ def Protocol.user_uri(user_id = nil)
109
+ if user_id
110
+ "/#{VERSION}/users/#{user_id}"
111
+ else
112
+ "/#{VERSION}/users"
113
+ end
114
+ end
115
+
116
+ # Construct a uri referencing a file stored by the API.
117
+ def Protocol.file_uri(file_name)
118
+ "/#{VERSION}/files/#{file_name}"
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,79 @@
1
+ require 'cgi'
2
+
3
+ module Parse
4
+
5
+ class Query
6
+ attr_accessor :where
7
+ attr_accessor :class_name
8
+ attr_accessor :order_by
9
+ attr_accessor :order
10
+ attr_accessor :limit
11
+ attr_accessor :skip
12
+
13
+ def initialize(cls_name)
14
+ @class_name = cls_name
15
+ @where = {}
16
+ @order = :ascending
17
+ end
18
+
19
+ def add_constraint(field, constraint)
20
+ current = where[field]
21
+ if current && current.is_a?(Hash) && constraint.is_a?(Hash)
22
+ current.merge! constraint
23
+ else
24
+ where[field] = constraint
25
+ end
26
+ end
27
+ private :add_constraint
28
+
29
+ def eq(field, value)
30
+ add_constraint field, value
31
+ self
32
+ end
33
+
34
+ def regex(field, expression)
35
+ add_constraint field, { "$regex" => expression }
36
+ self
37
+ end
38
+
39
+ def less_than(field, value)
40
+ add_constraint field, { "$lt" => value }
41
+ self
42
+ end
43
+
44
+ def less_eq(field, value)
45
+ add_constraint field, { "$lte" => value }
46
+ self
47
+ end
48
+
49
+ def greater_than(field, value)
50
+ add_constraint field, { "$gt" => value }
51
+ self
52
+ end
53
+
54
+ def greater_eq(field, value)
55
+ add_constraint field, { "$gte" => value }
56
+ self
57
+ end
58
+
59
+ def value_in(field, values)
60
+ add_constraint field, { "$in" => values }
61
+ self
62
+ end
63
+
64
+ def exists(field, value = true)
65
+ add_constraint field, { "$exists" => value }
66
+ self
67
+ end
68
+
69
+ def get
70
+ uri = Protocol.class_uri @class_name
71
+ query = { "where" => CGI.escape(@where.to_json) }
72
+
73
+ response = Parse.client.request uri, :get, nil, query
74
+ Parse.client.parse_response class_name, response
75
+ end
76
+
77
+ end
78
+
79
+ end
data/lib/parse/util.rb ADDED
@@ -0,0 +1,49 @@
1
+ module Parse
2
+
3
+ # Parse a JSON representation into a fully instantiated
4
+ # class. obj can be either a string or a Hash as parsed
5
+ # by JSON.parse
6
+ # @param class_name [Object]
7
+ # @param obj [Object]
8
+ def Parse.parse_json(class_name, obj)
9
+
10
+ if obj.nil?
11
+ nil
12
+ # String
13
+ elsif obj.is_a? String
14
+ parse_json class_name, JSON.parse(obj)
15
+
16
+ # Array
17
+ elsif obj.is_a? Array
18
+ obj.collect { |o| parse_json(class_name, o) }
19
+
20
+ # Hash
21
+ elsif obj.is_a? Hash
22
+
23
+ # If it's a datatype hash
24
+ if obj.has_key?(Protocol::KEY_TYPE)
25
+ parse_datatype obj
26
+ elsif obj.size == 1 && obj.has_key?(Protocol::KEY_RESULTS) && obj[Protocol::KEY_RESULTS].is_a?(Array)
27
+ obj[Protocol::KEY_RESULTS].collect { |o| parse_json(class_name, o) }
28
+ else # otherwise it must be a regular object
29
+ Parse::Object.new class_name, obj
30
+ end
31
+
32
+ else
33
+ obj
34
+ end
35
+ end
36
+
37
+ def Parse.parse_datatype(obj)
38
+ type = obj[Protocol::KEY_TYPE]
39
+
40
+ case type
41
+ when Protocol::TYPE_POINTER
42
+ Parse::Pointer.new obj
43
+ when Protocol::TYPE_BYTES
44
+ Parse::Bytes.new obj
45
+ when Protocol::TYPE_DATE
46
+ Parse::Date.new obj
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,69 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "parse-ruby-client"
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Alan deLevie", "Adam Alpern"]
12
+ s.date = "2012-02-05"
13
+ s.description = "A simple Ruby client for the parse.com REST API"
14
+ s.email = "adelevie@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "LICENSE.txt",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "example.rb",
27
+ "lib/parse.rb",
28
+ "lib/parse/client.rb",
29
+ "lib/parse/datatypes.rb",
30
+ "lib/parse/error.rb",
31
+ "lib/parse/object.rb",
32
+ "lib/parse/protocol.rb",
33
+ "lib/parse/query.rb",
34
+ "lib/parse/util.rb",
35
+ "parse-ruby-client.gemspec",
36
+ "pkg/parse-ruby-client-0.0.1.gem",
37
+ "pkg/parse-ruby-client-1-0.0.1.gem",
38
+ "pkg/parse-ruby-client.gem",
39
+ "test/helper.rb",
40
+ "test/test_parse-ruby-client.rb"
41
+ ]
42
+ s.homepage = "http://github.com/adelevie/parse-ruby-client"
43
+ s.licenses = ["MIT"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = "1.8.10"
46
+ s.summary = "A simple Ruby client for the parse.com REST API"
47
+
48
+ if s.respond_to? :specification_version then
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
53
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
54
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
55
+ s.add_development_dependency(%q<rcov>, [">= 0"])
56
+ else
57
+ s.add_dependency(%q<shoulda>, [">= 0"])
58
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
59
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
60
+ s.add_dependency(%q<rcov>, [">= 0"])
61
+ end
62
+ else
63
+ s.add_dependency(%q<shoulda>, [">= 0"])
64
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
65
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
66
+ s.add_dependency(%q<rcov>, [">= 0"])
67
+ end
68
+ end
69
+
Binary file
Binary file
Binary file
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'parse-ruby-client-1'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestParseRubyClient < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parse-ruby-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alan deLevie
9
+ - Adam Alpern
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-02-05 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: shoulda
17
+ requirement: &70311876454920 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *70311876454920
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: &70311876453900 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *70311876453900
37
+ - !ruby/object:Gem::Dependency
38
+ name: jeweler
39
+ requirement: &70311876434760 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 1.6.4
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *70311876434760
48
+ - !ruby/object:Gem::Dependency
49
+ name: rcov
50
+ requirement: &70311876433780 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *70311876433780
59
+ description: A simple Ruby client for the parse.com REST API
60
+ email: adelevie@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files:
64
+ - LICENSE.txt
65
+ - README.md
66
+ files:
67
+ - Gemfile
68
+ - Gemfile.lock
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - VERSION
73
+ - example.rb
74
+ - lib/parse.rb
75
+ - lib/parse/client.rb
76
+ - lib/parse/datatypes.rb
77
+ - lib/parse/error.rb
78
+ - lib/parse/object.rb
79
+ - lib/parse/protocol.rb
80
+ - lib/parse/query.rb
81
+ - lib/parse/util.rb
82
+ - parse-ruby-client.gemspec
83
+ - pkg/parse-ruby-client-0.0.1.gem
84
+ - pkg/parse-ruby-client-1-0.0.1.gem
85
+ - pkg/parse-ruby-client.gem
86
+ - test/helper.rb
87
+ - test/test_parse-ruby-client.rb
88
+ homepage: http://github.com/adelevie/parse-ruby-client
89
+ licenses:
90
+ - MIT
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ segments:
102
+ - 0
103
+ hash: -1143289306669216906
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 1.8.10
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: A simple Ruby client for the parse.com REST API
116
+ test_files: []