dm-parse 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +63 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/lib/adapters/parse_adapter.rb +238 -0
- data/lib/collection.rb +8 -0
- data/lib/dm-parse.rb +12 -0
- data/lib/is/parse.rb +36 -0
- data/lib/parse/query.rb +117 -0
- data/lib/parse/resource.rb +19 -0
- data/lib/property/parse_date.rb +17 -0
- data/lib/property/parse_key.rb +26 -0
- data/lib/property/parse_pointer.rb +21 -0
- data/spec/integration_spec.rb +98 -0
- data/spec/parse_adapter_spec.rb +302 -0
- data/spec/parse_date_spec.rb +45 -0
- data/spec/parse_key_spec.rb +32 -0
- data/spec/parse_pointer_spec.rb +32 -0
- data/spec/query_spec.rb +25 -0
- data/spec/resource_spec.rb +5 -0
- data/spec/spec_helper.rb +58 -0
- metadata +315 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
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 "rspec", ">= 2.10.0"
|
10
|
+
gem "yard", ">= 0.7"
|
11
|
+
gem "rdoc", ">= 3.12"
|
12
|
+
gem "bundler", ">= 1.0.0"
|
13
|
+
gem "jeweler", ">= 1.8.3"
|
14
|
+
gem "simplecov", ">= 0.6"
|
15
|
+
gem "debugger", ">= 1.1"
|
16
|
+
end
|
17
|
+
|
18
|
+
gem "dm-core", ">= 1.2"
|
19
|
+
gem "dm-validations", ">= 1.2"
|
20
|
+
gem "activesupport", ">= 3.2"
|
21
|
+
gem "nestful", ">= 0.0.8"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.2.6)
|
5
|
+
i18n (~> 0.6)
|
6
|
+
multi_json (~> 1.0)
|
7
|
+
addressable (2.2.8)
|
8
|
+
columnize (0.3.6)
|
9
|
+
debugger (1.1.3)
|
10
|
+
columnize (>= 0.3.1)
|
11
|
+
debugger-linecache (~> 1.1.1)
|
12
|
+
debugger-ruby_core_source (~> 1.1.2)
|
13
|
+
debugger-linecache (1.1.1)
|
14
|
+
debugger-ruby_core_source (>= 1.1.1)
|
15
|
+
debugger-ruby_core_source (1.1.2)
|
16
|
+
diff-lcs (1.1.3)
|
17
|
+
dm-core (1.2.0)
|
18
|
+
addressable (~> 2.2.6)
|
19
|
+
dm-validations (1.2.0)
|
20
|
+
dm-core (~> 1.2.0)
|
21
|
+
git (1.2.5)
|
22
|
+
i18n (0.6.0)
|
23
|
+
jeweler (1.8.3)
|
24
|
+
bundler (~> 1.0)
|
25
|
+
git (>= 1.2.5)
|
26
|
+
rake
|
27
|
+
rdoc
|
28
|
+
json (1.7.3)
|
29
|
+
multi_json (1.3.6)
|
30
|
+
nestful (0.0.8)
|
31
|
+
activesupport (>= 3.0.0.beta)
|
32
|
+
rake (0.9.2.2)
|
33
|
+
rdoc (3.12)
|
34
|
+
json (~> 1.4)
|
35
|
+
rspec (2.10.0)
|
36
|
+
rspec-core (~> 2.10.0)
|
37
|
+
rspec-expectations (~> 2.10.0)
|
38
|
+
rspec-mocks (~> 2.10.0)
|
39
|
+
rspec-core (2.10.1)
|
40
|
+
rspec-expectations (2.10.0)
|
41
|
+
diff-lcs (~> 1.1.3)
|
42
|
+
rspec-mocks (2.10.1)
|
43
|
+
simplecov (0.6.4)
|
44
|
+
multi_json (~> 1.0)
|
45
|
+
simplecov-html (~> 0.5.3)
|
46
|
+
simplecov-html (0.5.3)
|
47
|
+
yard (0.8.2.1)
|
48
|
+
|
49
|
+
PLATFORMS
|
50
|
+
ruby
|
51
|
+
|
52
|
+
DEPENDENCIES
|
53
|
+
activesupport (>= 3.2)
|
54
|
+
bundler (>= 1.0.0)
|
55
|
+
debugger (>= 1.1)
|
56
|
+
dm-core (>= 1.2)
|
57
|
+
dm-validations (>= 1.2)
|
58
|
+
jeweler (>= 1.8.3)
|
59
|
+
nestful (>= 0.0.8)
|
60
|
+
rdoc (>= 3.12)
|
61
|
+
rspec (>= 2.10.0)
|
62
|
+
simplecov (>= 0.6)
|
63
|
+
yard (>= 0.7)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Zhi-Qiang Lei
|
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.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= dm-parse
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to dm-parse
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2012 Zhi-Qiang Lei. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
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 = "dm-parse"
|
18
|
+
gem.homepage = "https://github.com/siegfried/dm-parse"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{An extension to make DataMapper working on Parse.com}
|
21
|
+
gem.description = %Q{An extension to make DataMapper working on Parse.com}
|
22
|
+
gem.email = "zhiqiang.lei@gmail.com"
|
23
|
+
gem.authors = ["Zhi-Qiang Lei"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
gem.add_dependency "dm-core", ">= 1.2"
|
26
|
+
gem.add_dependency "dm-validations", ">= 1.2"
|
27
|
+
gem.add_dependency "activesupport", ">= 3.2"
|
28
|
+
gem.add_dependency "nestful", ">= 0.0.8"
|
29
|
+
end
|
30
|
+
Jeweler::RubygemsDotOrgTasks.new
|
31
|
+
|
32
|
+
require 'rspec/core'
|
33
|
+
require 'rspec/core/rake_task'
|
34
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
35
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
36
|
+
end
|
37
|
+
|
38
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
39
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
40
|
+
spec.rcov = true
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :spec
|
44
|
+
|
45
|
+
require 'yard'
|
46
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,238 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Adapters
|
3
|
+
|
4
|
+
class ParseAdapter < AbstractAdapter
|
5
|
+
include Parse::Conditions
|
6
|
+
include Query::Conditions
|
7
|
+
|
8
|
+
HOST = "https://api.parse.com"
|
9
|
+
VERSION = "1"
|
10
|
+
APP_ID_HEADER = "X-Parse-Application-Id"
|
11
|
+
API_KEY_HEADER = "X-Parse-REST-API-Key"
|
12
|
+
MASTER_KEY_HEADER = "X-Parse-Master-Key"
|
13
|
+
|
14
|
+
attr_reader :classes, :users, :login, :password_reset
|
15
|
+
|
16
|
+
def initialize(name, options)
|
17
|
+
super
|
18
|
+
@classes = build_parse_resource_for "classes"
|
19
|
+
@users = build_parse_resource_for "users"
|
20
|
+
@login = build_parse_resource_for "login"
|
21
|
+
@password_reset = build_parse_resource_for "requestPasswordReset"
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_resources_for(model)
|
25
|
+
storage_name = model.storage_name
|
26
|
+
storage_name == "_User" ? users : classes[storage_name]
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_resource_for(resource)
|
30
|
+
parse_resources_for(resource.model)[resource.id]
|
31
|
+
end
|
32
|
+
|
33
|
+
def create(resources)
|
34
|
+
resources.each do |resource|
|
35
|
+
params = attributes_as_fields(resource.attributes(:property)).except("objectId", "createdAt", "updatedAt")
|
36
|
+
model = resource.model
|
37
|
+
result = parse_resources_for(model).post params: params
|
38
|
+
initialize_serial resource, result["objectId"]
|
39
|
+
resource.created_at = resource.updated_at = result["createdAt"]
|
40
|
+
end.size
|
41
|
+
end
|
42
|
+
|
43
|
+
def read(query)
|
44
|
+
model = query.model
|
45
|
+
params = parse_params_for(query)
|
46
|
+
response = parse_resources_for(model).get params: params
|
47
|
+
response["results"]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Read the "count" from Parse
|
51
|
+
# This is Parse-only
|
52
|
+
#
|
53
|
+
# @param [Query] query
|
54
|
+
# the query to match resources in the datastore
|
55
|
+
#
|
56
|
+
# @return [Integer]
|
57
|
+
# the number of records that match the query
|
58
|
+
#
|
59
|
+
# @api semipublic
|
60
|
+
def read_count(query)
|
61
|
+
model = query.model
|
62
|
+
params = parse_params_for(query)
|
63
|
+
params[:count] = 1
|
64
|
+
params[:limit] = 0
|
65
|
+
response = parse_resources_for(model).get params: params
|
66
|
+
response["count"]
|
67
|
+
end
|
68
|
+
|
69
|
+
# Login, which is Parse-only
|
70
|
+
#
|
71
|
+
# @param [String] username
|
72
|
+
# the username
|
73
|
+
# @param [String] password
|
74
|
+
# the password
|
75
|
+
#
|
76
|
+
# @return [Hash]
|
77
|
+
# the user information
|
78
|
+
#
|
79
|
+
# @api semipublic
|
80
|
+
def sign_in(username, password)
|
81
|
+
login.get params: {username: username, password: password}
|
82
|
+
end
|
83
|
+
|
84
|
+
# Request a password reset email
|
85
|
+
# Parse-only
|
86
|
+
#
|
87
|
+
# @param [String] email
|
88
|
+
# the email address
|
89
|
+
#
|
90
|
+
# @return [Hash]
|
91
|
+
# a empty Hash
|
92
|
+
def request_password_reset(email)
|
93
|
+
password_reset.post params: {email: email}
|
94
|
+
end
|
95
|
+
|
96
|
+
def delete(resources)
|
97
|
+
resources.each do |resource|
|
98
|
+
parse_resource_for(resource).delete
|
99
|
+
end.size
|
100
|
+
end
|
101
|
+
|
102
|
+
def update(attributes, resources)
|
103
|
+
resources.each do |resource|
|
104
|
+
params = attributes_as_fields(attributes).except("createdAt", "updatedAt")
|
105
|
+
parse_resource_for(resource).put(params: params)
|
106
|
+
end.size
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
def build_parse_resource_for(name)
|
111
|
+
key_type = @options[:master] ? MASTER_KEY_HEADER : API_KEY_HEADER
|
112
|
+
headers = {APP_ID_HEADER => @options[:app_id], key_type => @options[:api_key]}
|
113
|
+
Parse::Resource.new(HOST, format: :json, headers: headers)[VERSION][name]
|
114
|
+
end
|
115
|
+
|
116
|
+
def parse_params_for(query)
|
117
|
+
result = { :limit => parse_limit_for(query) }
|
118
|
+
if conditions = parse_conditions_for(query)
|
119
|
+
result[:where] = conditions.to_json
|
120
|
+
end
|
121
|
+
if (offset = parse_offset_for(query)) > 0
|
122
|
+
result[:skip] = offset
|
123
|
+
end
|
124
|
+
if orders = parse_orders_for(query)
|
125
|
+
result[:order] = orders
|
126
|
+
end
|
127
|
+
result
|
128
|
+
end
|
129
|
+
|
130
|
+
def parse_orders_for(query)
|
131
|
+
orders = query.order
|
132
|
+
return nil unless orders
|
133
|
+
|
134
|
+
# cannot use objectId as order field on Parse
|
135
|
+
orders = orders.reject { |order| order.target.field == "objectId" }.map do |order|
|
136
|
+
field = order.target.field
|
137
|
+
order.operator == :desc ? "-" + field : field
|
138
|
+
end.join(",")
|
139
|
+
|
140
|
+
orders.blank? ? nil : orders
|
141
|
+
end
|
142
|
+
|
143
|
+
def parse_offset_for(query)
|
144
|
+
query.offset
|
145
|
+
end
|
146
|
+
|
147
|
+
def parse_limit_for(query)
|
148
|
+
limit = query.limit || 1000
|
149
|
+
raise "Parse limit: only number from 0 to 1000 is valid" unless (0..1000).include?(limit)
|
150
|
+
limit
|
151
|
+
end
|
152
|
+
|
153
|
+
def parse_conditions_for(query)
|
154
|
+
conditions = query.conditions
|
155
|
+
return nil if conditions.blank?
|
156
|
+
|
157
|
+
case conditions
|
158
|
+
when NotOperation
|
159
|
+
parse_query = Parse::Conditions::And.new
|
160
|
+
feed_reversely(parse_query, conditions)
|
161
|
+
when AndOperation
|
162
|
+
parse_query = Parse::Conditions::And.new
|
163
|
+
feed_directly(parse_query, conditions)
|
164
|
+
when OrOperation
|
165
|
+
parse_query = Parse::Conditions::Or.new
|
166
|
+
feed_or(parse_query, conditions)
|
167
|
+
end
|
168
|
+
|
169
|
+
parse_query.build
|
170
|
+
end
|
171
|
+
|
172
|
+
def feed_for(parse_query, condition, comparison_class)
|
173
|
+
field = condition.subject.field
|
174
|
+
comparison = comparison_class.new condition.value
|
175
|
+
parse_query.add field, comparison
|
176
|
+
end
|
177
|
+
|
178
|
+
def feed_reversely(parse_query, conditions)
|
179
|
+
conditions.each do |condition|
|
180
|
+
case condition
|
181
|
+
when EqualToComparison then feed_for(parse_query, condition, Ne)
|
182
|
+
when GreaterThanComparison then feed_for(parse_query, condition, Lte)
|
183
|
+
when GreaterThanOrEqualToComparison then feed_for(parse_query, condition, Lt)
|
184
|
+
when LessThanComparison then feed_for(parse_query, condition, Gte)
|
185
|
+
when LessThanOrEqualToComparison then feed_for(parse_query, condition, Gt)
|
186
|
+
when NotOperation then feed_directly(parse_query, condition)
|
187
|
+
when AndOperation then feed_reversely(parse_query, condition)
|
188
|
+
when InclusionComparison then feed_inclusion(parse_query, condition, Nin)
|
189
|
+
else
|
190
|
+
raise NotImplementedError
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def feed_directly(parse_query, conditions)
|
196
|
+
conditions.each do |condition|
|
197
|
+
feed_with_condition parse_query, condition
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def feed_or(queries, conditions)
|
202
|
+
conditions.each do |condition|
|
203
|
+
parse_query = Parse::Conditions::And.new
|
204
|
+
feed_with_condition parse_query, condition
|
205
|
+
queries.add parse_query
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def feed_with_condition(parse_query, condition)
|
210
|
+
case condition
|
211
|
+
when RegexpComparison then feed_for(parse_query, condition, Regex)
|
212
|
+
when EqualToComparison then feed_for(parse_query, condition, Eql)
|
213
|
+
when GreaterThanComparison then feed_for(parse_query, condition, Gt)
|
214
|
+
when GreaterThanOrEqualToComparison then feed_for(parse_query, condition, Gte)
|
215
|
+
when LessThanComparison then feed_for(parse_query, condition, Lt)
|
216
|
+
when LessThanOrEqualToComparison then feed_for(parse_query, condition, Lte)
|
217
|
+
when NotOperation then feed_reversely(parse_query, condition)
|
218
|
+
when AndOperation then feed_directly(parse_query, condition)
|
219
|
+
when InclusionComparison then feed_inclusion(parse_query, condition, In)
|
220
|
+
else
|
221
|
+
raise NotImplementedError
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def feed_inclusion(parse_query, condition, comparison_class)
|
226
|
+
if condition.subject.is_a?(DataMapper::Associations::OneToMany::Relationship)
|
227
|
+
child_key = condition.subject.child_key.first.name
|
228
|
+
parse_query.add "objectId", comparison_class.new(condition.value.map { |resource| resource.send child_key })
|
229
|
+
else
|
230
|
+
feed_for(parse_query, condition, comparison_class)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
const_added(:ParseAdapter)
|
237
|
+
end
|
238
|
+
end
|
data/lib/collection.rb
ADDED
data/lib/dm-parse.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "dm-core"
|
2
|
+
require "dm-validations"
|
3
|
+
require "active_support/all"
|
4
|
+
require "nestful"
|
5
|
+
require "collection"
|
6
|
+
require "property/parse_key"
|
7
|
+
require "property/parse_pointer"
|
8
|
+
require "property/parse_date"
|
9
|
+
require "parse/resource"
|
10
|
+
require "parse/query"
|
11
|
+
require "adapters/parse_adapter"
|
12
|
+
require "is/parse"
|
data/lib/is/parse.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Is
|
3
|
+
|
4
|
+
module Parse
|
5
|
+
def is_parse(options = {})
|
6
|
+
property :id, Property::ParseKey
|
7
|
+
property :created_at, Property::ParseDate, field: "createdAt"
|
8
|
+
property :updated_at, Property::ParseDate, field: "updatedAt"
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_parse_user(options = {})
|
12
|
+
is_parse(options)
|
13
|
+
|
14
|
+
storage_names[:default] = "_User"
|
15
|
+
|
16
|
+
property :username, Property::String
|
17
|
+
property :password, Property::String
|
18
|
+
property :email, Property::String, format: :email_address
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def authenticate(username, password)
|
22
|
+
result = repository.adapter.sign_in(username, password)
|
23
|
+
get(result["objectId"])
|
24
|
+
end
|
25
|
+
|
26
|
+
def request_password_reset(email)
|
27
|
+
repository.adapter.request_password_reset email
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
Model.append_extensions(Is::Parse)
|
36
|
+
end
|
data/lib/parse/query.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Parse
|
3
|
+
|
4
|
+
module Conditions
|
5
|
+
class Comparison
|
6
|
+
def initialize(value)
|
7
|
+
@value = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def build
|
11
|
+
{ key_name => @value }
|
12
|
+
end
|
13
|
+
|
14
|
+
def key_name
|
15
|
+
"$#{self.class.to_s.demodulize.downcase}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Eql < Comparison
|
20
|
+
def build
|
21
|
+
@value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Ne < Comparison
|
26
|
+
end
|
27
|
+
|
28
|
+
class Lt < Comparison
|
29
|
+
end
|
30
|
+
|
31
|
+
class Lte < Comparison
|
32
|
+
end
|
33
|
+
|
34
|
+
class Gt < Comparison
|
35
|
+
end
|
36
|
+
|
37
|
+
class Gte < Comparison
|
38
|
+
end
|
39
|
+
|
40
|
+
class InComparison < Comparison
|
41
|
+
def initialize(value)
|
42
|
+
@value = value.to_a
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class In < InComparison
|
47
|
+
end
|
48
|
+
|
49
|
+
class Nin < InComparison
|
50
|
+
end
|
51
|
+
|
52
|
+
class Regex < Comparison
|
53
|
+
def options
|
54
|
+
options = @value.options
|
55
|
+
result = []
|
56
|
+
result << "i" if options[0] == 1
|
57
|
+
result << "m" if options[2] == 1
|
58
|
+
result.join
|
59
|
+
end
|
60
|
+
|
61
|
+
def build
|
62
|
+
{key_name => @value.source}.tap { |value| value["$options"] = options if options.present? }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class And
|
67
|
+
def initialize
|
68
|
+
@conditions = Mash.new
|
69
|
+
end
|
70
|
+
|
71
|
+
def add(field, comparison)
|
72
|
+
@conditions[field] ||= []
|
73
|
+
@conditions[field] << comparison
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
def combine(comparisons)
|
78
|
+
groups = comparisons.group_by { |comparison| comparison.is_a? Eql }
|
79
|
+
equals = groups[true]
|
80
|
+
others = groups[false]
|
81
|
+
|
82
|
+
if equals.present? && others.present?
|
83
|
+
raise "Parse Query: cannot combine Eql with others"
|
84
|
+
elsif equals.present?
|
85
|
+
raise "can only use one EqualToComparison for a field" unless equals.size == 1
|
86
|
+
equals.first.build
|
87
|
+
elsif others.present?
|
88
|
+
others.inject({}) do |result, comparison|
|
89
|
+
result.merge! comparison.build
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def build
|
95
|
+
@conditions.inject({}) do |result, (field, comparisons)|
|
96
|
+
result.merge! field => combine(comparisons)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Or
|
102
|
+
def initialize
|
103
|
+
@queries = []
|
104
|
+
end
|
105
|
+
|
106
|
+
def add(query)
|
107
|
+
@queries << query
|
108
|
+
end
|
109
|
+
|
110
|
+
def build
|
111
|
+
{"$or" => @queries.map { |query| query.build } }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Parse
|
3
|
+
class Resource < ::Nestful::Resource
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
def ==(resource)
|
7
|
+
url == resource.url && options == resource.options
|
8
|
+
end
|
9
|
+
|
10
|
+
def delete(options = {})
|
11
|
+
Nestful.delete(url, options.merge(@options))
|
12
|
+
end
|
13
|
+
|
14
|
+
def put(options = {})
|
15
|
+
Nestful.put(url, options.merge(@options))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module DataMapper
|
2
|
+
class Property
|
3
|
+
|
4
|
+
class ParseDate < Object
|
5
|
+
|
6
|
+
def dump(value)
|
7
|
+
value && {"__type" => "Date", "iso" => value.to_datetime.utc.iso8601(3)}
|
8
|
+
end
|
9
|
+
|
10
|
+
def load(value)
|
11
|
+
value && (value.is_a?(Hash) ? value["iso"].to_datetime : value.to_datetime)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DataMapper
|
2
|
+
class Property
|
3
|
+
|
4
|
+
class ParseKey < Object
|
5
|
+
def serial?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
key true
|
10
|
+
field "objectId"
|
11
|
+
|
12
|
+
def load(value)
|
13
|
+
value.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def dump(value)
|
17
|
+
value && value.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_child_key
|
21
|
+
Property::ParsePointer
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DataMapper
|
2
|
+
class Property
|
3
|
+
|
4
|
+
class ParsePointer < Object
|
5
|
+
|
6
|
+
def dump(value)
|
7
|
+
value && {"__type" => "Pointer", "className" => target_storage_name, "objectId" => value.to_s}
|
8
|
+
end
|
9
|
+
|
10
|
+
def load(value)
|
11
|
+
value.is_a?(Hash) ? value["objectId"] : super
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def target_storage_name
|
16
|
+
model.relationships.select { |r| r.child_key.include? self }.first.parent_model.storage_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|