parsecom 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,286 @@
1
+ module Parse
2
+ class Query
3
+ attr_reader :keys
4
+ attr_accessor :parse_class_name, :parse_client
5
+
6
+ def initialize parse_class_or_name=nil
7
+ @parse_class, @parse_class_name, @parse_client =
8
+ parse_class_or_name === Parse::Object \
9
+ ? [parse_class_or_name, parse_class_or_name.parse_class_name,
10
+ parse_class_or_name.parse_client]
11
+ : [nil, parse_class_or_name.to_s, Parse::Client.default_client]
12
+ @limit = nil
13
+ @skip = nil
14
+ @count = false
15
+ @where = []
16
+ @order = []
17
+ @include = []
18
+ @keys = []
19
+ end
20
+
21
+ def invoke &block
22
+ block = proc do |body|
23
+ # TODO: should handle error
24
+ body['results']
25
+ end unless block
26
+ endpoint = %w(User).include?(@parse_class_name) \
27
+ ? "#{@parse_class_name.lowercase}s"
28
+ : "classes/#{@parse_class_name}"
29
+ @parse_client.call_api :get, "#{endpoint}?#{to_params}", nil, &block
30
+ end
31
+
32
+ def limit val=nil
33
+ if val
34
+ @limit = val
35
+ else
36
+ @limit
37
+ end
38
+ self
39
+ end
40
+
41
+ def skip val=nil
42
+ if val
43
+ @skip = val
44
+ else
45
+ @skip
46
+ end
47
+ self
48
+ end
49
+
50
+ def count val=nil
51
+ if val
52
+ @count = val
53
+ else
54
+ @count
55
+ end
56
+ self
57
+ end
58
+
59
+ def where hash=nil, &block
60
+ return @where if hash.nil? && block.nil?
61
+
62
+ if hash.is_a? Hash
63
+ hash.each do |k, v|
64
+ @where << %Q|"#{k}":"#{v}"|
65
+ end
66
+ else
67
+ block = hash if hash.is_a?(Proc) && block.nil?
68
+ instance_eval &block if block
69
+ end
70
+ self
71
+ end
72
+
73
+ def order *vals
74
+ return @order if vals.empty?
75
+ @order += vals
76
+ self
77
+ end
78
+ alias order_asc order
79
+
80
+ def order_desc *vals
81
+ order *(vals.map do |val|
82
+ val[0] == '-' ? val[1..-1] : "-#{val}"
83
+ end)
84
+ end
85
+
86
+ def keys *vals
87
+ return @keys if vals.empty?
88
+ @keys += vals
89
+ self
90
+ end
91
+
92
+ def include *vals
93
+ return @include if vals.empty?
94
+ @include += vals
95
+ end
96
+
97
+ def to_params
98
+ params = []
99
+
100
+ where = @where.join ','
101
+ order = @order.join ','
102
+ keys = @keys.join ','
103
+ include = @include.join ','
104
+ params.push "where=#{URI.encode "{#{where}}"}" unless where.empty?
105
+ params.push "order=#{URI.encode order}" unless order.empty?
106
+ params.push "keys=#{URI.encode keys}" unless keys.empty?
107
+ params.push "include=#{URI.encode include}" unless include.empty?
108
+ params.push "skip=#{URI.encode @skip.to_s}" if @skip
109
+ params.push "limit=#{URI.encode @limit.to_s}" if @limit
110
+
111
+ params.join '&'
112
+ end
113
+
114
+ def inspect
115
+ "#{@parse_class_name}, #{to_params}"
116
+ end
117
+
118
+ private
119
+
120
+ def column name
121
+ Condition.new(self, name).tap do |condition|
122
+ @where.push condition
123
+ end
124
+ end
125
+ alias _ column
126
+
127
+ def subquery_for parse_class_name
128
+ Subquery.new parse_class_name
129
+ end
130
+
131
+ def or_condition *conds
132
+ conds.each do |cond|
133
+ @where.delete cond
134
+ end
135
+ OrCondition.new(conds).tap do |condition|
136
+ @where.push condition
137
+ end
138
+ end
139
+ alias _or_ or_condition
140
+
141
+ class Subquery < Query
142
+ attr_accessor :parent
143
+
144
+ def initialize parse_class_name=nil
145
+ super
146
+ @key = nil
147
+ end
148
+
149
+ def key= val
150
+ @key = val
151
+ self
152
+ end
153
+ alias key key=
154
+
155
+ def inspect
156
+ # TODO should be refactored
157
+ case @parent
158
+ when :select
159
+ %Q|{"query":{"className":#{parse_class_name.to_s.inspect},"where":{#{@where.join ','}}},"key":#{@key.inspect}}|
160
+ when :in_query, :not_in_query
161
+ %Q|{"where":{#{@where.join ','}},"className":#{parse_class_name.to_s.inspect}}|
162
+ end
163
+ end
164
+ end
165
+
166
+ class Condition
167
+ def initialize query, column_name
168
+ @query = query
169
+ @column_name = column_name
170
+ @conditions = []
171
+ end
172
+
173
+ # receive nop
174
+ %w(eq contains).each do |op|
175
+ eval %Q{
176
+ def #{op} val
177
+ @conditions.push val
178
+ self
179
+ end
180
+ }
181
+ end
182
+ alias equal_to eq
183
+
184
+ # receive single param
185
+ %w(lt lte gt gte ne).each do |op|
186
+ eval %Q{
187
+ def #{op} val
188
+ @conditions.push ['$#{op}', val]
189
+ self
190
+ end
191
+ }
192
+ end
193
+ alias less_than lt
194
+ alias less_that_or_equal_to lte
195
+ alias greater_than gt
196
+ alias greater_that_or_equal_to gte
197
+ alias not_equal_to ne
198
+
199
+ def exists val=true
200
+ @conditions.push ['$exists', val]
201
+ self
202
+ end
203
+
204
+ # receive multi params
205
+ %w(in nin all).each do |op|
206
+ eval %Q{
207
+ def #{op} *vals
208
+ @conditions.push ['$#{op}', vals]
209
+ self
210
+ end
211
+ }
212
+ end
213
+ alias not_in nin
214
+
215
+ # receive subquery
216
+ %w(select dont_select in_query not_in_query).each do |op|
217
+ eval %Q{
218
+ def #{op} subquery
219
+ subquery.parent = :#{op}
220
+ @conditions.push ['$#{op.camelize :lower}', subquery]
221
+ self
222
+ end
223
+ }
224
+ end
225
+
226
+ def or cond
227
+ # quite dirty!!
228
+ @query.where.delete self
229
+ @query.where.delete cond
230
+ or_cond = Condition.new @query, '$or'
231
+ or_cond.eq [self, cond]
232
+ @query.where.push or_cond
233
+ end
234
+
235
+ def to_s
236
+ if @conditions.size == 1 && !@conditions[0].is_a?(Array)
237
+ "#{@column_name.to_s.inspect}:#{condition_to_s @conditions[0]}"
238
+ elsif @conditions.size == 1 && @conditions[0][0].to_s[0] != '$'
239
+ # $or
240
+ "#{@column_name.to_s.inspect}:#{condition_to_s @conditions[0]}"
241
+ else
242
+ "#{@column_name.to_s.inspect}:{#{@conditions.map{|c| condition_to_s c}.join ','}}"
243
+ end
244
+ end
245
+
246
+ private
247
+
248
+ def condition_value_to_s val
249
+ case val
250
+ when Parse::Object
251
+ %Q|{"__type":"Pointer","className":"#{val.parse_class_name}","objectId":"#{val.obj_id}"}|
252
+ else
253
+ val.inspect
254
+ end
255
+ end
256
+
257
+ def condition_to_s condition
258
+ case condition
259
+ when Array
260
+ if condition[0].to_s[0] == '$'
261
+ "#{condition[0].inspect}:#{condition_value_to_s condition[1]}"
262
+ else
263
+ # $or
264
+ "[#{condition.map{|c| "{#{condition_value_to_s c}}"}.join ','}]"
265
+ end
266
+ else
267
+ condition_value_to_s condition
268
+ end
269
+ end
270
+ end
271
+
272
+ class OrCondition
273
+ def initialize conds
274
+ @conditions = conds
275
+ end
276
+
277
+ def to_s
278
+ %Q|"$or":[#{@conditions.map {|c| "{#{c.to_s}}"}.join ','}]|
279
+ end
280
+ end
281
+ end
282
+
283
+ def Query parse_class_name=nil
284
+ Query.new parse_class_name
285
+ end
286
+ end
data/lib/parse/user.rb ADDED
@@ -0,0 +1,47 @@
1
+ # coding:utf-8
2
+ module Parse
3
+ class User < Object
4
+
5
+ class << self
6
+ def sign_up username, password, hash={}
7
+ self.new(username, password, hash).sign_up
8
+ end
9
+
10
+ def log_in username, password
11
+ self.new(username, password).log_in
12
+ end
13
+ end
14
+
15
+ def initialize username, password, hash={}
16
+ super hash
17
+ @username = username
18
+ @password = password
19
+ end
20
+
21
+ def sign_up
22
+ parse_client.sign_up @username, @password, opts do |resp_body|
23
+ @obj_id = resp_body['objectId']
24
+ @created_at = resp_body['createdAt']
25
+ @raw_hash.update(@updated_hash).update resp_body
26
+ @updated_hash.clear
27
+ parse_client.session_token = resp_body['sessionToken']
28
+ self
29
+ end
30
+ end
31
+
32
+ def log_in
33
+ parse_client.log_in @username, @password do |resp_body|
34
+ @obj_id = resp_body['objectId']
35
+ @created_at = resp_body['createdAt']
36
+ @updated_at = resp_body['updatedAt']
37
+ @raw_hash.update(@updated_hash).update resp_body
38
+ @updated_hash.clear
39
+ parse_client.session_token = resp_body['sessionToken']
40
+ self
41
+ end
42
+ end
43
+
44
+ def log_out
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ module Parse
2
+ VERSION = "0.0.1"
3
+ end
data/lib/parsecom.rb ADDED
@@ -0,0 +1,58 @@
1
+ # coding:utf-8
2
+ require "parse/version"
3
+
4
+ require 'open-uri'
5
+ require 'fileutils'
6
+ require 'json'
7
+ require 'date'
8
+ require 'net/https'
9
+ require 'uri'
10
+ require 'parse/ext/string'
11
+ require 'parse/http_client'
12
+ require 'parse/client'
13
+ require 'parse/query'
14
+ require 'parse/object'
15
+ require 'parse/user'
16
+ require 'parse/pointer'
17
+ require 'parse/file'
18
+
19
+ module Parse
20
+ @@application_id = nil
21
+ @@api_key = nil
22
+ @@auto_snake_case = false
23
+
24
+ module_function
25
+
26
+ def application_id
27
+ @@application_id
28
+ end
29
+
30
+ def application_id= application_id
31
+ @@application_id = application_id
32
+ end
33
+
34
+ def api_key
35
+ @@api_key
36
+ end
37
+
38
+ def api_key= api_key
39
+ @@api_key = api_key
40
+ end
41
+
42
+ def credentials hash
43
+ @@application_id = hash[:application_id]
44
+ @@api_key = hash[:api_key]
45
+ end
46
+
47
+ def credentials= hash
48
+ credentials hash
49
+ end
50
+
51
+ def auto_snake_case
52
+ @@auto_snake_case
53
+ end
54
+
55
+ def auto_snake_case= auto_snake_case
56
+ @@auto_snake_case = auto_snake_case
57
+ end
58
+ end
data/parsecom.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'parse/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "parsecom"
8
+ gem.version = Parse::VERSION
9
+ gem.authors = ["Ando Yasushi"]
10
+ gem.email = ["andyjpn@gmail.com"]
11
+ gem.description = %q{Pure Ruby version of parse.com client. This library allow you to access straightforwardly to parse.com REST API.}
12
+ gem.summary = %q{Pure Ruby version of parse.com client}
13
+ gem.homepage = "https://github.com/technohippy/parsecom"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency "rspec"
21
+ end
@@ -0,0 +1,54 @@
1
+ # coding:utf-8
2
+ require 'spec_helper'
3
+
4
+ # define a parse class
5
+ class ClassA < Parse::Object; end
6
+ # you can also write:
7
+ # Parse::Object(:ClassA)
8
+ # or
9
+ # Parse::Object.create :ClassA
10
+
11
+ describe Parse::Object, 'when it defines a parse class' do
12
+ it 'should return a new class' do
13
+ Parse::Object(:ClassB).should be_a_kind_of(Class)
14
+ ClassB.should be < Parse::Object
15
+ end
16
+
17
+ it 'should return the existing class' do
18
+ Parse::Object(:ClassA).should == ClassA
19
+ end
20
+
21
+ it 'should also return a new class' do
22
+ Parse::Object.create :ClassC
23
+ ClassC.should be_a_kind_of(Class)
24
+ ClassC.should be < Parse::Object
25
+ end
26
+
27
+ it 'should raise an error when creating an existing class' do
28
+ expect {
29
+ Parse::Object.create :ClassB
30
+ }.to raise_error
31
+ end
32
+ end
33
+
34
+ describe Parse::Object, 'when it creates a new parse object' do
35
+ it 'should create a parse object' do
36
+ class_a = ClassA.new
37
+ class_a.columnA = 'Hello, parse.com'
38
+ class_a.new?.should be_true
39
+ class_a.save
40
+ class_a.new?.should be_false
41
+
42
+ class_a2 = ClassA.find class_a.obj_id
43
+ class_a2.columnA.should eql('Hello, parse.com')
44
+
45
+ class_as = ClassA.find :where => proc{column(:columnB).gt 5},
46
+ :order => 'createdAt', :keys => 'columnB', :limit => 3
47
+ class_as.size.should == 3
48
+
49
+ class_a = ClassA.find :where => {'objectId' => 'avp6UKaG1k'},
50
+ :order => 'createdAt', :keys => 'columnB', :limit => 3
51
+ class_a.size.should == 1
52
+ end
53
+ end
54
+
@@ -0,0 +1,176 @@
1
+ # coding:utf-8
2
+ require 'spec_helper'
3
+ require 'parsecom'
4
+
5
+ describe Parse::Query, 'when it builds conditions' do
6
+ it 'should build a correct "where" parameter' do
7
+ query = Parse::Query.new ClassA
8
+ query.to_params.should be_empty
9
+
10
+ query.where :column1 => 'val1'
11
+ query.to_params.should have_params('where' => '{"column1":"val1"}')
12
+
13
+ query.where.clear
14
+ query.to_params.should be_empty
15
+
16
+ query.where :column1 => 'val1', :column2 => 'val2'
17
+ query.to_params.should have_params('where' => '{"column1":"val1","column2":"val2"}')
18
+
19
+ query.where :column3 => 'val3'
20
+ query.to_params.should have_params('where' => '{"column1":"val1","column2":"val2","column3":"val3"}')
21
+
22
+ query.where.clear
23
+
24
+ query.where do
25
+ column(:playerName).eq('Sean Plott')
26
+ column(:cheatMode).eq(false)
27
+ end
28
+ query.to_params.should have_params('where' => '{"playerName":"Sean Plott","cheatMode":false}')
29
+ query.where.clear
30
+
31
+ query.where do
32
+ column(:score).gte(1000).lte(3000)
33
+ end
34
+ query.to_params.should have_params('where' => '{"score":{"$gte":1000,"$lte":3000}}')
35
+ query.where.clear
36
+
37
+ query.where do
38
+ column(:score).in(1, 3 ,5, 7, 9)
39
+ end
40
+ query.to_params.should have_params('where' => '{"score":{"$in":[1, 3, 5, 7, 9]}}')
41
+ query.where.clear
42
+
43
+ query.where do
44
+ column(:playerName).nin("Jonathan Walsh","Dario Wunsch","Shawn Simon")
45
+ end
46
+ query.to_params.should have_params('where' => '{"playerName":{"$nin":["Jonathan Walsh", "Dario Wunsch", "Shawn Simon"]}}')
47
+ query.where.clear
48
+
49
+ query.where do
50
+ column(:score).exists(true)
51
+ end
52
+ query.to_params.should have_params('where' => '{"score":{"$exists":true}}')
53
+ query.where.clear
54
+
55
+ query.where do
56
+ column(:score).exists(false)
57
+ end
58
+ query.to_params.should have_params('where' => '{"score":{"$exists":false}}')
59
+ query.where.clear
60
+
61
+ query.where do
62
+ subquery = subquery_for :Team
63
+ subquery.where do
64
+ column(:winPct).gt(0.5)
65
+ end
66
+ subquery.key = 'city'
67
+ column(:hometown).select(subquery)
68
+ end
69
+ query.to_params.should have_params('where' => '{"hometown":{"$select":{"query":{"className":"Team","where":{"winPct":{"$gt":0.5}}},"key":"city"}}}')
70
+ query.where.clear
71
+
72
+ query.where do
73
+ column(:arrayKey).contains(2)
74
+ end
75
+ query.to_params.should have_params('where' => '{"arrayKey":2}')
76
+ query.where.clear
77
+
78
+ query.where do
79
+ column(:arrayKey).all(2,3,4)
80
+ end
81
+ query.to_params.should have_params('where' => '{"arrayKey":{"$all":[2, 3, 4]}}')
82
+ query.where.clear
83
+
84
+ query.where do
85
+ post = Parse::Object(:Post).new :objectId => '8TOXdXf3tz'
86
+ column(:post).eq(post)
87
+ end
88
+ query.to_params.should have_params('where' => '{"post":{"__type":"Pointer","className":"Post","objectId":"8TOXdXf3tz"}}')
89
+ query.where.clear
90
+
91
+ query.where do
92
+ subquery = subquery_for :Post
93
+ subquery.where do
94
+ column(:image).exists(true)
95
+ end
96
+ column(:post).in_query(subquery)
97
+ end
98
+ query.to_params.should have_params('where' => '{"post":{"$inQuery":{"where":{"image":{"$exists":true}},"className":"Post"}}}')
99
+ query.where.clear
100
+
101
+ query.where do
102
+ subquery = subquery_for :Post
103
+ subquery.where do
104
+ column(:image).exists(true)
105
+ end
106
+ column(:post).not_in_query(subquery)
107
+ end
108
+ query.to_params.should have_params('where' => '{"post":{"$notInQuery":{"where":{"image":{"$exists":true}},"className":"Post"}}}')
109
+ query.where.clear
110
+
111
+ # query.where do
112
+ # #{
113
+ # # "$relatedTo":{
114
+ # # "object":{
115
+ # # "__type":"Pointer",
116
+ # # "className":"Post",
117
+ # # "objectId":"8TOXdXf3tz"
118
+ # # },
119
+ # # "key":"likes"
120
+ # # }
121
+ # #}
122
+ # # TODO
123
+ # end
124
+ # query.to_params.should == 'where={}'
125
+ # query.where.clear
126
+
127
+ query.where do
128
+ column(:wins).gt(150).or column(:wins).lt(5)
129
+ end
130
+ query.to_params.should have_params('where' => '{"$or":[{"wins":{"$gt":150}},{"wins":{"$lt":5}}]}')
131
+ query.where.clear
132
+
133
+ query.where do
134
+ or_condition column(:wins).gt(150), column(:wins).lt(5)
135
+ end
136
+ query.to_params.should have_params('where' => '{"$or":[{"wins":{"$gt":150}},{"wins":{"$lt":5}}]}')
137
+ query.where.clear
138
+
139
+ query.where do
140
+ or_condition column(:wins).gt(150), column(:wins).lt(5), column(:loses).lt(5)
141
+ end
142
+ query.to_params.should have_params('where' => '{"$or":[{"wins":{"$gt":150}},{"wins":{"$lt":5}},{"loses":{"$lt":5}}]}')
143
+ query.where.clear
144
+ end
145
+
146
+ it 'should build a correct "order" parameter' do
147
+ query = Parse::Query.new ClassA
148
+ query.order 'score', '-name'
149
+ query.to_params.should == 'order=score,-name'
150
+ query.order.clear
151
+
152
+ query.order_desc '-score', 'name'
153
+ query.to_params.should == 'order=score,-name'
154
+ query.order.clear
155
+
156
+ query.order 'score'
157
+ query.order_desc 'name'
158
+ query.to_params.should == 'order=score,-name'
159
+ query.order.clear
160
+ end
161
+
162
+ it 'should success request' do
163
+ query = Parse::Query.new(ClassA).
164
+ order('createdAt').
165
+ keys('columnB').
166
+ limit(3).
167
+ where {column(:columnB).gt 5}
168
+ # query.invoke
169
+ query.to_params.should have_params(
170
+ 'order' => 'createdAt',
171
+ 'keys' => 'columnB',
172
+ 'limit' => 3,
173
+ 'where' => '{"columnB":{"$gt":5}}'
174
+ )
175
+ end
176
+ end
@@ -0,0 +1,23 @@
1
+ # coding:utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Parse::User, 'when it signs up' do
5
+ it 'should create new user' do
6
+ user = Parse::User.sign_up "username#{rand 1000}", 'password'
7
+ user.obj_id.should be_an_instance_of String
8
+ user.parse_client.session_token.should be_an_instance_of String
9
+ end
10
+ end
11
+
12
+ describe Parse::User, 'when it logs in' do
13
+ it 'should get the session token' do
14
+ user = Parse::User.log_in 'username', 'password'
15
+ user.obj_id.should be_an_instance_of String
16
+ user.parse_client.session_token.should be_an_instance_of String
17
+ end
18
+ end
19
+
20
+ describe Parse::User, 'when it logs out' do
21
+ it 'should get the session token' do
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+
3
+ RSpec::Matchers.define :have_params do |expected|
4
+ match do |actual|
5
+ expected.to_a.map do |k, v|
6
+ actual.include? "#{k}=#{URI.encode v.to_s}"
7
+ end.inject(true) {|s, v| s || v}
8
+ end
9
+ end
10
+
11
+ require 'parsecom'
12
+ # setup Parse object
13
+ Parse.credentials application_id: ENV['APPLICATION_ID'], api_key: ENV['API_KEY']
14
+ #puts('set your credentials in spec/spec_helper.rb and remove this line') || exit