parsecom 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.
@@ -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