parse-ruby-client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []