parse-stack 1.3.8 → 1.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changes.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +99 -56
- data/bin/console +5 -0
- data/lib/parse/api/batch.rb +1 -2
- data/lib/parse/client.rb +13 -8
- data/lib/parse/client/caching.rb +33 -21
- data/lib/parse/model/core/actions.rb +17 -0
- data/lib/parse/model/core/properties.rb +5 -1
- data/lib/parse/model/date.rb +8 -0
- data/lib/parse/model/model.rb +3 -2
- data/lib/parse/model/object.rb +25 -4
- data/lib/parse/query.rb +4 -4
- data/lib/parse/stack.rb +2 -0
- data/lib/parse/stack/generators/rails.rb +35 -0
- data/lib/parse/stack/generators/templates/model.erb +52 -0
- data/lib/parse/stack/generators/templates/model_installation.rb +6 -0
- data/lib/parse/stack/generators/templates/model_role.rb +6 -0
- data/lib/parse/stack/generators/templates/model_session.rb +6 -0
- data/lib/parse/stack/generators/templates/model_user.rb +13 -0
- data/lib/parse/stack/generators/templates/parse.rb +13 -0
- data/lib/parse/stack/generators/templates/webhooks.rb +11 -0
- data/lib/parse/stack/railtie.rb +17 -0
- data/lib/parse/stack/tasks.rb +17 -1
- data/lib/parse/stack/version.rb +1 -1
- data/lib/parse/webhooks.rb +19 -13
- data/lib/parse/webhooks/registration.rb +2 -2
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba96b9a68cd8bee15dfcd3f63b00fe7b3ce66a79
|
4
|
+
data.tar.gz: 627c40c21e1866fd1473171cc5794f48becd2d0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad583e23a9af975fef0dd3e3a5badfe4a494a1754778d7f14c48b0aecd26863cd915994a1609a07824bfadb39ddb298dd69d83cb6ec23a1089d81e3c2be782c7
|
7
|
+
data.tar.gz: 59cc21ab26200badfd575729a9ee47b239374b948b6715fdc36418a8077edf4f86d035904e84c675996a1d0cb21b9ea0832364f2b64ef819d32471129b44a944
|
data/Changes.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Parse-Stack Changes
|
2
2
|
|
3
|
+
1.4.2
|
4
|
+
-----------
|
5
|
+
- NEW: Support for rails generators: `parse_stack:install` and `parse_stack:model`.
|
6
|
+
- Support Parse::Date with ActiveSupport::TimeWithZone.
|
7
|
+
- :date properties will now raise an error if value was not converted to a Parse::Date.
|
8
|
+
- Support for calling `before_save` and `before_destroy` callbacks in your model when a Parse::Object is returned by your `before_save` or `before_delete` webhook respectively.
|
9
|
+
- Parse::Query `:cache` expression now allows integer values to define the specific cache duration for this specific query request. If `false` is passed, will ignore the cache and make the request regardless if a cache response is available. If `true` is passed (default), it will use the value configured when setting up when calling `Parse.setup`.
|
10
|
+
- Fixes the use of `:use_master_key` in Parse::Query.
|
11
|
+
- Fixes to the cache key used in middleware.
|
12
|
+
- Parse::User before_save callback clears the record ACLs.
|
13
|
+
- Added `anonymous?` instance method to `Parse::User` class.
|
14
|
+
|
3
15
|
1.3.8
|
4
16
|
-----------
|
5
17
|
- Support for reloading the Parse config data with `Parse.config!`.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -5,6 +5,35 @@ Parse-Stack is a [Parse Server](https://github.com/ParsePlatform/parse-server) R
|
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/parse-stack.svg)](https://badge.fury.io/rb/parse-stack)
|
6
6
|
[![Build Status](https://travis-ci.org/modernistik/parse-stack.svg?branch=master)](https://travis-ci.org/modernistik/parse-stack)
|
7
7
|
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'parse-stack'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install parse-stack
|
21
|
+
|
22
|
+
### Rails
|
23
|
+
Parse-Stack comes with support for Rails by adding additional rake tasks and generators. After adding `parse-stack` as a gem dependency in your Gemfile and running `bundle`, you should run the install script:
|
24
|
+
|
25
|
+
$ rails g parse_stack:install
|
26
|
+
|
27
|
+
This will create a configuration file (`config/initializers/parse.rb`) and a set of sample models and hooks under `app/models` directory. Modify `config/initializers/parse.rb` file with your Parse-Server API keys. You can then generate models with the `parse_stack:model` generator.
|
28
|
+
|
29
|
+
$ rails g parse_stack:model Song name:string released:date genres:array
|
30
|
+
|
31
|
+
This would create a `song.rb` file in `app/models` with the provided properties. Once you are ready to update your schema, you can run the `parse:upgrade` task to upgrade the remote Parse-Server schema to match your new models.
|
32
|
+
|
33
|
+
$ rails parse:upgrade
|
34
|
+
|
35
|
+
That should create the new collection `Song` in your Parse-Server backend. For a more full featured example, see [Parse-Server-Rails-Example](https://github.com/modernistik/parse-server-rails-example).
|
36
|
+
|
8
37
|
## Table Of Contents
|
9
38
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
10
39
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
@@ -85,18 +114,17 @@ Parse-Stack is a [Parse Server](https://github.com/ParsePlatform/parse-server) R
|
|
85
114
|
- [Bounding Box Constraint](#bounding-box-constraint)
|
86
115
|
- [Relational Queries](#relational-queries)
|
87
116
|
- [Compound Queries](#compound-queries)
|
88
|
-
- [Cloud Code Functions](#cloud-code-functions)
|
89
|
-
- [
|
90
|
-
- [
|
117
|
+
- [Calling Cloud Code Functions](#calling-cloud-code-functions)
|
118
|
+
- [Calling Background Jobs](#calling-background-jobs)
|
119
|
+
- [Model Callbacks](#model-callbacks)
|
91
120
|
- [Schema Upgrades and Migrations](#schema-upgrades-and-migrations)
|
92
121
|
- [Push Notifications](#push-notifications)
|
93
122
|
- [Cloud Code Webhooks](#cloud-code-webhooks)
|
94
|
-
- [Cloud Code
|
123
|
+
- [Cloud Code Functions](#cloud-code-functions)
|
95
124
|
- [Cloud Code Triggers](#cloud-code-triggers)
|
96
125
|
- [Mounting Webhooks Application](#mounting-webhooks-application)
|
97
126
|
- [Register Webhooks](#register-webhooks)
|
98
127
|
- [Parse REST API Client](#parse-rest-api-client)
|
99
|
-
- [Options](#options-2)
|
100
128
|
- [Request Caching](#request-caching)
|
101
129
|
- [Installation](#installation)
|
102
130
|
- [Development](#development)
|
@@ -104,7 +132,7 @@ Parse-Stack is a [Parse Server](https://github.com/ParsePlatform/parse-server) R
|
|
104
132
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
105
133
|
|
106
134
|
## Overview
|
107
|
-
Parse
|
135
|
+
Parse-Stack is a full stack framework that utilizes several ideas behind [DataMapper](http://datamapper.org/docs/find.html) and [ActiveModel](https://github.com/rails/rails/tree/master/activemodel) to manage and maintain larger scale ruby applications and tools that utilize the Parse Platform. If you are familiar with these technologies, the framework should feel familiar to you.
|
108
136
|
|
109
137
|
```ruby
|
110
138
|
|
@@ -430,13 +458,18 @@ This class represents the data and columns contained in the standard Parse `_Ins
|
|
430
458
|
|
431
459
|
```ruby
|
432
460
|
class Parse::Installation < Parse::Object
|
433
|
-
property :channels, :array
|
434
461
|
property :gcm_sender_id, :string, field: :GCMSenderId
|
462
|
+
property :app_identifier
|
463
|
+
property :app_name
|
464
|
+
property :app_version
|
435
465
|
property :badge, :integer
|
436
|
-
property :
|
466
|
+
property :channels, :array
|
437
467
|
property :device_token
|
468
|
+
property :device_token_last_modified, :integer
|
438
469
|
property :device_type
|
470
|
+
property :installation_id
|
439
471
|
property :locale_identifier
|
472
|
+
property :parse_version
|
440
473
|
property :push_type
|
441
474
|
property :time_zone
|
442
475
|
end
|
@@ -508,39 +541,39 @@ Using the example above, we can add the base properties to our classes.
|
|
508
541
|
|
509
542
|
```ruby
|
510
543
|
class Post < Parse::Object
|
511
|
-
|
512
|
-
|
544
|
+
property :title
|
545
|
+
property :content, :string # explicit
|
513
546
|
|
514
|
-
|
515
|
-
|
547
|
+
# treat the values of this field as symbols instead of strings.
|
548
|
+
property :category, :string, symbolize: true
|
516
549
|
|
517
|
-
|
518
|
-
|
550
|
+
# maybe a count of comments.
|
551
|
+
property :comment_count, :integer, default: 0
|
519
552
|
|
520
553
|
# use lambda to access the instance object.
|
521
554
|
# Set draft_date to the created_at date if empty.
|
522
555
|
property :draft_date, :date, default: lambda { |x| x.created_at }
|
523
556
|
# the published date. Maps to "publishDate"
|
524
|
-
|
557
|
+
property :publish_date, :date, default: lambda { |x| DateTime.now }
|
525
558
|
|
526
|
-
|
527
|
-
|
559
|
+
# maybe whether it is currently visible
|
560
|
+
property :visible, :boolean
|
528
561
|
|
529
|
-
|
530
|
-
|
562
|
+
# a list using
|
563
|
+
property :tags, :array
|
531
564
|
|
532
|
-
|
533
|
-
|
565
|
+
# Maps to "featuredImage" column representing a File.
|
566
|
+
property :featured_image, :file
|
534
567
|
|
535
|
-
|
568
|
+
property :location, :geopoint
|
536
569
|
|
537
570
|
# Support bytes
|
538
571
|
property :data, :bytes
|
539
572
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
573
|
+
# store SEO information. Make sure we map it to the column
|
574
|
+
# "SEO", otherwise it would have implicitly used "seo"
|
575
|
+
# as the remote column name
|
576
|
+
property :seo, :object, field: "SEO"
|
544
577
|
end
|
545
578
|
```
|
546
579
|
|
@@ -953,7 +986,7 @@ songs.save
|
|
953
986
|
```
|
954
987
|
|
955
988
|
### Magic `save_all`
|
956
|
-
By default, all Parse queries have a maximum fetch limit of 1000. While using the `:max` option,
|
989
|
+
By default, all Parse queries have a maximum fetch limit of 1000. While using the `:max` option, Parse-Stack can increase this up to 11,000. In the cases where you need to update a large number of objects, you can utilize the `Parse::Object#save_all` method
|
957
990
|
to fetch, modify and save objects.
|
958
991
|
|
959
992
|
This methodology works by continually fetching and saving older records related to the time you begin a `save_all` request (called an "anchor date"), until there are no records left to update. To enable this to work, you must have confidence that any modifications you make to the records will successfully save through you validations that may be present in your `before_save`. This is important, as saving a record will set its `updated_at` date to one newer than the "anchor date" of when the `save_all` started. This `save_all` process will stop whenever no more records match the provided constraints that are older than the "anchor date", or when an object that was previously updated, is seen again in a future fetch (_which means the object failed to save_). Note that `save_all` will automatically manage the correct `updated_at` constraints in the query, so it is recommended that you do not use it as part of the initial constraints.
|
@@ -1004,7 +1037,7 @@ You can destroy a Parse record, just call the `#destroy` method. It will return
|
|
1004
1037
|
```
|
1005
1038
|
|
1006
1039
|
### Auto-Fetching Associations
|
1007
|
-
All associations in
|
1040
|
+
All associations in are fetched lazily by default. If you wish to include objects as part of your query results you can use the `:includes` expression.
|
1008
1041
|
|
1009
1042
|
```ruby
|
1010
1043
|
song = Song.first
|
@@ -1016,7 +1049,7 @@ All associations in `Parse::Stack` are fetched lazily by default. If you wish to
|
|
1016
1049
|
|
1017
1050
|
```
|
1018
1051
|
|
1019
|
-
However,
|
1052
|
+
However, Parse-Stack performs automatic fetching of associations when the associated classes and their properties are locally defined. Using our Artist and Song examples. In this example, the Song object fetched only has a pointer object in its `#artist` field. However, because the framework knows there is a `Artist#name` property, calling `#name` on the artist pointer will automatically go to Parse to fetch the associated object and provide you with the value.
|
1020
1053
|
|
1021
1054
|
```ruby
|
1022
1055
|
song = Song.first
|
@@ -1181,11 +1214,14 @@ Use with limit to paginate through results. Default is 0 with maximum value bein
|
|
1181
1214
|
```
|
1182
1215
|
|
1183
1216
|
#### :cache
|
1184
|
-
A true
|
1217
|
+
A `true`, `false` or integer value. If you are using the built-in caching middleware, `Parse::Middleware::Caching`, setting this to `false` will prevent it from using a previously cached result if available. You may pass an integer value, which will allow this request to be cached for the specified number of seconds. The default value is `true`, which uses the [`:expires`](#expires) value that was passed when [configuring the client](#connection-setup).
|
1185
1218
|
|
1186
1219
|
```ruby
|
1187
1220
|
# don't use a cached result if available
|
1188
|
-
Song.all limit:
|
1221
|
+
Song.all limit: 500, cache: false
|
1222
|
+
|
1223
|
+
# cache this particular request for 60 seconds
|
1224
|
+
Song.all limit: 500, cache: 1.minute
|
1189
1225
|
```
|
1190
1226
|
|
1191
1227
|
#### :use_master_key
|
@@ -1201,6 +1237,7 @@ A Parse session token string. If you would like to perform a query as a particul
|
|
1201
1237
|
|
1202
1238
|
```ruby
|
1203
1239
|
# disable sending the master key in the request if configured
|
1240
|
+
# and perform this request as a Parse user represented by this token
|
1204
1241
|
Song.all limit: 3, session_token: "<session_token>"
|
1205
1242
|
```
|
1206
1243
|
|
@@ -1499,7 +1536,7 @@ query.or_where(:wins.lt => 5)
|
|
1499
1536
|
results = query.results
|
1500
1537
|
```
|
1501
1538
|
|
1502
|
-
## Cloud Code Functions
|
1539
|
+
## Calling Cloud Code Functions
|
1503
1540
|
You can call on your defined Cloud Code functions using the `call_function()` method. The result will be `nil` in case of errors or the value of the `result` field in the Parse response.
|
1504
1541
|
|
1505
1542
|
```ruby
|
@@ -1512,7 +1549,7 @@ You can call on your defined Cloud Code functions using the `call_function()` me
|
|
1512
1549
|
response.result unless response.error?
|
1513
1550
|
```
|
1514
1551
|
|
1515
|
-
##
|
1552
|
+
## Calling Background Jobs
|
1516
1553
|
You can trigger background jobs that you have configured in your Parse application as follows.
|
1517
1554
|
|
1518
1555
|
```ruby
|
@@ -1525,7 +1562,7 @@ You can trigger background jobs that you have configured in your Parse applicati
|
|
1525
1562
|
response.result unless response.error?
|
1526
1563
|
```
|
1527
1564
|
|
1528
|
-
##
|
1565
|
+
## Model Callbacks
|
1529
1566
|
All `Parse::Object` subclasses extend [`ActiveModel::Callbacks`](http://api.rubyonrails.org/classes/ActiveModel/Callbacks.html) for `#save` and `#destroy` operations. You can setup internal hooks for `before`, `during` and `after`. See
|
1530
1567
|
|
1531
1568
|
```ruby
|
@@ -1542,7 +1579,7 @@ end
|
|
1542
1579
|
|
1543
1580
|
song = Song.new name: "my title"
|
1544
1581
|
puts song.name # 'my title'
|
1545
|
-
song.save
|
1582
|
+
song.save # runs :save callbacks
|
1546
1583
|
puts song.name # 'My Title'
|
1547
1584
|
|
1548
1585
|
```
|
@@ -1590,24 +1627,34 @@ Push notifications are implemented through the `Parse::Push` class. To send push
|
|
1590
1627
|
## Cloud Code Webhooks
|
1591
1628
|
Parse Parse allows you to receive Cloud Code webhooks on your own hosted server. The `Parse::Webhooks` class is a lightweight Rack application that routes incoming Cloud Code webhook requests and payloads to locally registered handlers. The payloads are `Parse::Payload` type of objects that represent that data that Parse sends webhook handlers. You can register any of the Cloud Code webhook trigger hooks (`beforeSave`, `afterSave`, `beforeDelete`, `afterDelete`) and function hooks.
|
1592
1629
|
|
1593
|
-
### Cloud Code
|
1630
|
+
### Cloud Code Functions
|
1594
1631
|
You can use the `route()` method to register handler blocks. The last value returned by the block will be returned back to the client in a success response. If `error!(value)` is called inside the block, we will return the correct Parse error response with the value you provided.
|
1595
1632
|
|
1596
1633
|
```ruby
|
1597
1634
|
# Register handling the 'helloWorld' function.
|
1598
1635
|
Parse::Webhooks.route(:function, :helloWorld) do
|
1599
1636
|
# use the Parse::Payload instance methods in this block
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1637
|
+
name = params['name'].to_s #function params
|
1638
|
+
puts "CloudCode Webhook helloWorld called in Ruby!"
|
1603
1639
|
# will return proper error response
|
1604
|
-
error!("Missing argument 'name'.") unless name.present?
|
1605
|
-
|
1606
|
-
"Hello #{name}!"
|
1640
|
+
# error!("Missing argument 'name'.") unless name.present?
|
1641
|
+
|
1642
|
+
name.present? ? "Hello #{name}!" : "Hello World!"
|
1607
1643
|
end
|
1608
1644
|
|
1609
1645
|
# Advanced: you can register handlers through classes if you prefer
|
1610
|
-
Parse::Webhooks.route :function, :myFunc, MyClass.method(:my_func)
|
1646
|
+
# Parse::Webhooks.route :function, :myFunc, MyClass.method(:my_func)
|
1647
|
+
```
|
1648
|
+
|
1649
|
+
If you have registered this webhook (see instructions below), you should be able to test it out by running curl using the command below. For a more in-depth example, see [Parse-Server-Rails-Example](https://github.com/modernistik/parse-server-rails-example).
|
1650
|
+
|
1651
|
+
```bash
|
1652
|
+
curl -X POST \
|
1653
|
+
-H "X-Parse-Application-Id: ${APPLICATION_ID}" \
|
1654
|
+
-H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
|
1655
|
+
-H "Content-Type: application/json" \
|
1656
|
+
-d '{}' \
|
1657
|
+
https://api.parse.com/1/functions/helloWorld
|
1611
1658
|
```
|
1612
1659
|
|
1613
1660
|
If you are creating `Parse::Object` subclasses, you may also register them there to keep common code and functionality centralized.
|
@@ -1619,7 +1666,7 @@ class Song < Parse::Object
|
|
1619
1666
|
the_user = user # available if a Parse user made the call
|
1620
1667
|
params = params
|
1621
1668
|
# ... do stuff ...
|
1622
|
-
|
1669
|
+
some_result
|
1623
1670
|
end
|
1624
1671
|
|
1625
1672
|
end
|
@@ -1653,7 +1700,7 @@ You can register webhooks to handle the different object triggers: `:before_save
|
|
1653
1700
|
|
1654
1701
|
For any `after_*` hook, return values are not needed since Parse does not utilize them. You may also register as many `after_save` or `after_delete` handlers as you prefer, all of them will be called.
|
1655
1702
|
|
1656
|
-
`before_save` and `before_delete` hooks have special functionality. When the `error!` method is called by the provided block, the framework will return the correct error response to Parse with value provided. Returning an error will prevent Parse from saving the object in the case of `before_save` and will prevent Parse from deleting the object when in a `before_delete`. In addition, for a `before_save`, the last value returned by the block will be the value returned in the success response. If the block returns nil or an `empty?` value, it will return `true` as the default response. You can also return a JSON object in a hash format to override the values that will be saved
|
1703
|
+
`before_save` and `before_delete` hooks have special functionality. When the `error!` method is called by the provided block, the framework will return the correct error response to Parse with value provided. Returning an error will prevent Parse from saving the object in the case of `before_save` and will prevent Parse from deleting the object when in a `before_delete`. In addition, for a `before_save`, the last value returned by the block will be the value returned in the success response. If the block returns nil or an `empty?` value, it will return `true` as the default response. You can also return a JSON object in a hash format to override the values that will be saved. However, we recommend modifying the `parse_object` provided since it has dirty tracking, and then returning that same object. This will automatically call your model specific `before_save` callbacks and send the proper payload back to Parse. For more details, see [Cloud Code BeforeSave Webhooks](https://parse.com/docs/cloudcode/guide#cloud-code-advanced-beforesave-webhooks)
|
1657
1704
|
|
1658
1705
|
```ruby
|
1659
1706
|
# recommended way
|
@@ -1680,7 +1727,7 @@ class Artist < Parse::Object
|
|
1680
1727
|
end
|
1681
1728
|
|
1682
1729
|
# *important* returns a special hash of changed values
|
1683
|
-
artist
|
1730
|
+
artist
|
1684
1731
|
end
|
1685
1732
|
|
1686
1733
|
webhook :before_delete do
|
@@ -1696,16 +1743,16 @@ end
|
|
1696
1743
|
The app can be mounted like any regular Rack-based application.
|
1697
1744
|
|
1698
1745
|
```ruby
|
1699
|
-
# Rack
|
1746
|
+
# Rack (add this to config.ru)
|
1700
1747
|
map "/webhooks" do
|
1701
1748
|
run Parse::Webhooks
|
1702
1749
|
end
|
1703
1750
|
|
1704
|
-
# Padrino (
|
1751
|
+
# or in Padrino (add this to apps.rb)
|
1705
1752
|
Padrino.mount('Parse::Webhooks', :cascade => true).to('/webhooks')
|
1706
1753
|
|
1707
|
-
# Rails
|
1708
|
-
|
1754
|
+
# or in Rails (add this in routes.rb)
|
1755
|
+
Rails.application.routes.draw do
|
1709
1756
|
mount Parse::Webhooks, :at => '/webhooks'
|
1710
1757
|
end
|
1711
1758
|
```
|
@@ -1733,16 +1780,12 @@ end
|
|
1733
1780
|
|
1734
1781
|
```
|
1735
1782
|
|
1736
|
-
However, we have predefined a few rake tasks you can use in your application. Just require `parse/stack/tasks` in your `Rakefile` and call `Parse::Stack.load_tasks`. This is useful for web frameworks like `Padrino
|
1783
|
+
However, we have predefined a few rake tasks you can use in your application. Just require `parse/stack/tasks` in your `Rakefile` and call `Parse::Stack.load_tasks`. This is useful for web frameworks like `Padrino`. Note that if you are using Parse-Stack with Rails, this is automatically done for you through the Railtie.
|
1737
1784
|
|
1738
1785
|
```ruby
|
1739
|
-
#
|
1740
|
-
require_relative 'config/application'
|
1786
|
+
# Add to your Rakefile (if not using Rails)
|
1741
1787
|
require 'parse/stack/tasks' # add this line
|
1742
|
-
|
1743
|
-
Rails.application.load_tasks
|
1744
1788
|
Parse::Stack.load_tasks # add this line
|
1745
|
-
|
1746
1789
|
```
|
1747
1790
|
|
1748
1791
|
Then you can see the tasks available by typing `rake -T`.
|
data/bin/console
CHANGED
@@ -11,6 +11,11 @@ Parse.setup
|
|
11
11
|
|
12
12
|
class Song < Parse::Object
|
13
13
|
property :name
|
14
|
+
|
15
|
+
before_save do
|
16
|
+
self.name = self.name.truncate(60)
|
17
|
+
end
|
18
|
+
|
14
19
|
end
|
15
20
|
# You can add fixtures and/or initialization code here to make experimenting
|
16
21
|
# with your gem easier. You can also use a different console, if you like.
|
data/lib/parse/api/batch.rb
CHANGED
@@ -48,7 +48,6 @@ module Parse
|
|
48
48
|
end
|
49
49
|
|
50
50
|
class BatchOperation
|
51
|
-
MAX_REQ_SEC = 30
|
52
51
|
|
53
52
|
attr_accessor :requests, :responses
|
54
53
|
include Enumerable
|
@@ -120,7 +119,7 @@ module Parse
|
|
120
119
|
@requests.each_slice(segment) do |slice|
|
121
120
|
@responses << client.batch_request( BatchOperation.new(slice) )
|
122
121
|
#throttle
|
123
|
-
sleep (slice.count.to_f / MAX_REQ_SEC.to_f )
|
122
|
+
# sleep (slice.count.to_f / MAX_REQ_SEC.to_f )
|
124
123
|
end
|
125
124
|
@responses.flatten!
|
126
125
|
#puts "Requests: #{@requests.count} == Response: #{@responses.count}"
|
data/lib/parse/client.rb
CHANGED
@@ -54,7 +54,7 @@ module Parse
|
|
54
54
|
include Parse::API::Push
|
55
55
|
include Parse::API::Schema
|
56
56
|
RETRY_COUNT = 2
|
57
|
-
RETRY_DELAY =
|
57
|
+
RETRY_DELAY = 2 #seconds
|
58
58
|
|
59
59
|
attr_accessor :session, :cache
|
60
60
|
attr_reader :application_id, :api_key, :master_key, :server_url
|
@@ -189,13 +189,13 @@ module Parse
|
|
189
189
|
# http method
|
190
190
|
method = method.downcase.to_sym
|
191
191
|
# set the User-Agent
|
192
|
-
headers["User-Agent".freeze] = "Parse-
|
192
|
+
headers["User-Agent".freeze] = "Parse-Server Ruby Client v#{Parse::Stack::VERSION}".freeze
|
193
193
|
|
194
194
|
if opts[:cache] == false
|
195
|
-
headers[Parse::Middleware::Caching::CACHE_CONTROL] = "no-cache"
|
195
|
+
headers[Parse::Middleware::Caching::CACHE_CONTROL] = "no-cache".freeze
|
196
196
|
elsif opts[:cache].is_a?(Numeric)
|
197
|
-
#
|
198
|
-
|
197
|
+
# specify the cache duration of this request
|
198
|
+
headers[Parse::Middleware::Caching::CACHE_EXPIRES_DURATION] = opts[:cache].to_i
|
199
199
|
end
|
200
200
|
|
201
201
|
if opts[:use_master_key] == false
|
@@ -259,15 +259,20 @@ module Parse
|
|
259
259
|
response
|
260
260
|
rescue Parse::ServiceUnavailableError => e
|
261
261
|
if retry_count > 0
|
262
|
-
puts "[Parse:
|
262
|
+
puts "[Parse:Retry] Retries remaining #{retry_count} : #{response.request}"
|
263
263
|
sleep RETRY_DELAY
|
264
264
|
retry_count -= 1
|
265
265
|
retry
|
266
266
|
end
|
267
267
|
raise e
|
268
268
|
rescue Faraday::Error::ClientError, Net::OpenTimeout => e
|
269
|
-
|
270
|
-
|
269
|
+
if retry_count > 0
|
270
|
+
puts "[Parse:Retry] Retries remaining #{retry_count} : #{_request}"
|
271
|
+
sleep RETRY_DELAY
|
272
|
+
retry_count -= 1
|
273
|
+
retry
|
274
|
+
end
|
275
|
+
raise Parse::ConnectionError, "#{_request} : #{e.class} - #{e.message}"
|
271
276
|
end
|
272
277
|
|
273
278
|
# shorthand for request(:get, uri, query: {})
|
data/lib/parse/client/caching.rb
CHANGED
@@ -5,6 +5,7 @@ require_relative 'protocol'
|
|
5
5
|
# This is a caching middleware for Parse queries using Moneta.
|
6
6
|
module Parse
|
7
7
|
module Middleware
|
8
|
+
class CachingError < Exception; end;
|
8
9
|
class Caching < Faraday::Middleware
|
9
10
|
include Parse::Protocol
|
10
11
|
# Cache-Control: no-cache
|
@@ -18,6 +19,7 @@ module Parse
|
|
18
19
|
# * 410 - 'Gone' - removed
|
19
20
|
CACHEABLE_HTTP_CODES = [200, 203, 300, 301, 302].freeze
|
20
21
|
CACHE_CONTROL = 'Cache-Control'.freeze
|
22
|
+
CACHE_EXPIRES_DURATION = 'X-Parse-Stack-Cache-Expires'.freeze
|
21
23
|
|
22
24
|
class << self
|
23
25
|
attr_accessor :enabled, :logging
|
@@ -43,7 +45,7 @@ module Parse
|
|
43
45
|
@expires = @opts[:expires]
|
44
46
|
|
45
47
|
unless @store.is_a?(Moneta::Transformer)
|
46
|
-
raise
|
48
|
+
raise Parse::Middleware::CachingError, "Caching store object must a Moneta key/value store (Moneta::Transformer)."
|
47
49
|
end
|
48
50
|
|
49
51
|
end
|
@@ -53,54 +55,64 @@ module Parse
|
|
53
55
|
end
|
54
56
|
|
55
57
|
def call!(env)
|
58
|
+
@request_headers = env[:request_headers]
|
56
59
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
# get default caching state
|
61
|
+
@enabled = self.class.enabled
|
62
|
+
# disable cache for this request if "no-cache" was passed
|
63
|
+
if @request_headers[CACHE_CONTROL] == "no-cache".freeze
|
64
|
+
@enabled = false
|
65
|
+
end
|
62
66
|
|
63
|
-
|
64
|
-
|
67
|
+
# get the expires information from header (per-request) or instance default
|
68
|
+
if @request_headers[CACHE_EXPIRES_DURATION].to_i > 0
|
69
|
+
@expires = @request_headers[CACHE_EXPIRES_DURATION].to_i
|
65
70
|
end
|
66
71
|
|
72
|
+
# cleanup
|
73
|
+
@request_headers.delete(CACHE_CONTROL)
|
74
|
+
@request_headers.delete(CACHE_EXPIRES_DURATION)
|
75
|
+
|
76
|
+
# if caching is enabled and we have a valid cache duration, use cache
|
77
|
+
# otherwise work as a passthrough.
|
78
|
+
return @app.call(env) unless @store.present? && @enabled && @expires > 0
|
79
|
+
|
67
80
|
url = env.url
|
68
81
|
method = env.method
|
82
|
+
@cache_key = url.to_s
|
69
83
|
begin
|
70
|
-
if
|
71
|
-
puts("[Parse::Cache]
|
84
|
+
if method == :get && @cache_key.present? && @store.key?(@cache_key)
|
85
|
+
puts("[Parse::Cache::Hit] >> #{url}") if self.class.logging.present?
|
72
86
|
response = Faraday::Response.new
|
73
|
-
res_env = @store[
|
87
|
+
res_env = @store[@cache_key] # previous cached response
|
74
88
|
body = res_env.respond_to?(:body) ? res_env.body : nil
|
75
89
|
if body.present?
|
76
90
|
response.finish({status: 200, response_headers: { "X-Cache-Response" => true }, body: body })
|
77
91
|
return response
|
78
92
|
else
|
79
|
-
@store.delete
|
93
|
+
@store.delete @cache_key
|
80
94
|
end
|
81
|
-
elsif
|
95
|
+
elsif @cache_key.present?
|
82
96
|
#non GET requets should clear the cache for that same resource path.
|
83
97
|
#ex. a POST to /1/classes/Artist/<objectId> should delete the cache for a GET
|
84
98
|
# request for the same '/1/classes/Artist/<objectId>' where objectId are equivalent
|
85
|
-
@store.delete
|
99
|
+
@store.delete @cache_key
|
86
100
|
end
|
87
101
|
rescue Errno::EINVAL, Redis::CannotConnectError => e
|
88
102
|
# if the cache store fails to connect, catch the exception but proceed
|
89
103
|
# with the regular request, but turn off caching for this request. It is possible
|
90
104
|
# that the cache connection resumes at a later point, so this is temporary.
|
91
|
-
|
92
|
-
|
105
|
+
@enabled = false
|
106
|
+
puts "[Parse::Cache] Error: #{e}"
|
93
107
|
end
|
94
108
|
|
95
|
-
|
109
|
+
puts("[Parse::Cache::Miss] !! #{url}") if self.class.logging.present?
|
96
110
|
@app.call(env).on_complete do |response_env|
|
97
111
|
# Only cache GET requests with valid HTTP status codes whose content-length
|
98
112
|
# is greater than 20. Otherwise they could be errors, successes and empty result sets.
|
99
|
-
if
|
113
|
+
if @enabled && method == :get && CACHEABLE_HTTP_CODES.include?(response_env.status) &&
|
100
114
|
response_env.present? && response_env.response_headers["content-length".freeze].to_i > 20
|
101
|
-
|
102
|
-
@store.store(url, response_env, expires: @expires) # ||= response_env.body
|
103
|
-
|
115
|
+
@store.store(@cache_key, response_env, expires: @expires) # ||= response_env.body
|
104
116
|
end # if
|
105
117
|
# do something with the response
|
106
118
|
# response_env[:response_headers].merge!(...)
|
@@ -96,6 +96,7 @@ module Parse
|
|
96
96
|
|
97
97
|
anchor_date = Parse::Date.now
|
98
98
|
constraints.merge! :updated_at.on_or_before => anchor_date
|
99
|
+
constraints.merge! cache: false
|
99
100
|
# oldest first, so we create a reduction-cycle
|
100
101
|
constraints.merge! order: :updated_at.asc, limit: 100
|
101
102
|
update_query = query(constraints)
|
@@ -337,6 +338,22 @@ module Parse
|
|
337
338
|
success
|
338
339
|
end
|
339
340
|
|
341
|
+
def prepare_save!
|
342
|
+
run_callbacks(:save) { false }
|
343
|
+
end
|
344
|
+
|
345
|
+
def changes_payload
|
346
|
+
h = attribute_updates
|
347
|
+
if relation_changes?
|
348
|
+
r = relation_change_operations.select { |s| s.present? }.first
|
349
|
+
h.merge!(r) if r.present?
|
350
|
+
end
|
351
|
+
h.merge!(className: parse_class) unless h.empty?
|
352
|
+
h.as_json
|
353
|
+
end
|
354
|
+
|
355
|
+
alias_method :update_payload, :changes_payload
|
356
|
+
|
340
357
|
# this method is useful to generate an array of additions and removals to a relational
|
341
358
|
# column.
|
342
359
|
def relation_change_operations
|
@@ -26,6 +26,7 @@ module Parse
|
|
26
26
|
module Properties
|
27
27
|
# This is an exception that is thrown if there is an issue when creating a specific property for a class.
|
28
28
|
class DefinitionError < Exception; end;
|
29
|
+
class ValueError < Exception; end;
|
29
30
|
|
30
31
|
# These are the base types supported by Parse.
|
31
32
|
TYPES = [:id, :string, :relation, :integer, :float, :boolean, :date, :array, :file, :geopoint, :bytes, :object, :acl].freeze
|
@@ -411,13 +412,16 @@ module Parse
|
|
411
412
|
elsif val.is_a?(String)
|
412
413
|
# if it's a string, try parsing the date
|
413
414
|
val = Parse::Date.parse val
|
415
|
+
#elsif val.present?
|
416
|
+
# pus "[Parse::Stack] Invalid date value '#{val}' assigned to #{self.class}##{key}, it should be a Parse::Date or DateTime."
|
417
|
+
# raise ValueError, "Invalid date value '#{val}' assigned to #{self.class}##{key}, it should be a Parse::Date or DateTime."
|
414
418
|
end
|
415
419
|
else
|
416
420
|
# You can provide a specific class instead of a symbol format
|
417
421
|
if data_type.respond_to?(:typecast)
|
418
422
|
val = data_type.typecast(val)
|
419
423
|
else
|
420
|
-
warn "Property :#{key}: :#{data_type} has
|
424
|
+
warn "Property :#{key}: :#{data_type} has no valid data type"
|
421
425
|
val = val #default
|
422
426
|
end
|
423
427
|
end
|
data/lib/parse/model/date.rb
CHANGED
data/lib/parse/model/model.rb
CHANGED
@@ -66,9 +66,10 @@ module Parse
|
|
66
66
|
return Parse::File if str == TYPE_FILE.freeze
|
67
67
|
return Parse::GeoPoint if str == TYPE_GEOPOINT.freeze
|
68
68
|
return Parse::Date if str == TYPE_DATE.freeze
|
69
|
+
return Parse::Bytes if str == TYPE_BYTES.freeze
|
69
70
|
# return Parse::User if str == "User".freeze
|
70
71
|
# return Parse::Installation if str == "Installation".freeze
|
71
|
-
|
72
|
+
|
72
73
|
str = str.to_s
|
73
74
|
# Basically go through all Parse::Object subclasses and see who is has a parse_class
|
74
75
|
# set to this string. We will cache the results for future use.
|
@@ -86,7 +87,7 @@ end
|
|
86
87
|
class String
|
87
88
|
# short helper method to provide lower-first-camelcase
|
88
89
|
def columnize
|
89
|
-
return "objectId" if self == "id"
|
90
|
+
return "objectId".freeze if self == "id".freeze
|
90
91
|
camelize(:lower)
|
91
92
|
end;
|
92
93
|
|
data/lib/parse/model/object.rb
CHANGED
@@ -230,6 +230,10 @@ module Parse
|
|
230
230
|
self.class.new h
|
231
231
|
end
|
232
232
|
|
233
|
+
def pretty
|
234
|
+
JSON.pretty_generate( as_json )
|
235
|
+
end
|
236
|
+
|
233
237
|
def clear_attribute_change!(atts)
|
234
238
|
clear_attribute_changes(atts)
|
235
239
|
end
|
@@ -295,19 +299,35 @@ module Parse
|
|
295
299
|
property :email
|
296
300
|
property :password
|
297
301
|
property :username
|
302
|
+
|
303
|
+
before_save do
|
304
|
+
# You cannot specify user ACLs.
|
305
|
+
self.clear_attribute_change!(:acl)
|
306
|
+
end
|
307
|
+
|
308
|
+
def anonymous?
|
309
|
+
auth_data.present? && auth_data["anonymous"].present?
|
310
|
+
end
|
298
311
|
end
|
299
312
|
|
300
313
|
class Installation < Parse::Object
|
301
314
|
parse_class "_Installation".freeze
|
302
|
-
|
315
|
+
|
303
316
|
property :gcm_sender_id, :string, field: :GCMSenderId
|
317
|
+
property :app_identifier
|
318
|
+
property :app_name
|
319
|
+
property :app_version
|
304
320
|
property :badge, :integer
|
305
|
-
property :
|
321
|
+
property :channels, :array
|
306
322
|
property :device_token
|
323
|
+
property :device_token_last_modified, :integer
|
307
324
|
property :device_type
|
325
|
+
property :installation_id
|
308
326
|
property :locale_identifier
|
327
|
+
property :parse_version
|
309
328
|
property :push_type
|
310
329
|
property :time_zone
|
330
|
+
|
311
331
|
end
|
312
332
|
|
313
333
|
class Role < Parse::Object
|
@@ -346,10 +366,11 @@ class Array
|
|
346
366
|
# if it constains the proper fields. Non convertible objects will be removed
|
347
367
|
# If the className is not contained or known, you can pass a table name as an argument
|
348
368
|
def parse_objects(table = nil)
|
369
|
+
f = "className".freeze
|
349
370
|
map do |m|
|
350
371
|
next m if m.is_a?(Parse::Pointer)
|
351
|
-
if m.is_a?(Hash) && (m[
|
352
|
-
next Parse::Object.build m, (m[
|
372
|
+
if m.is_a?(Hash) && (m[f] || m[:className] || table)
|
373
|
+
next Parse::Object.build m, (m[f] || m[:className] || table)
|
353
374
|
end
|
354
375
|
nil
|
355
376
|
end.compact
|
data/lib/parse/query.rb
CHANGED
@@ -119,7 +119,7 @@ module Parse
|
|
119
119
|
elsif expression == :cache
|
120
120
|
self.cache = value
|
121
121
|
elsif expression == :use_master_key
|
122
|
-
self.
|
122
|
+
self.use_master_key = value
|
123
123
|
elsif expression == :session
|
124
124
|
# you can pass a session token or a Parse::Session
|
125
125
|
value = value.is_a?(Parse::Session) ? value.session_token : value
|
@@ -315,11 +315,11 @@ module Parse
|
|
315
315
|
|
316
316
|
def fetch!(compiled_query)
|
317
317
|
opts = {}
|
318
|
-
opts[:cache] =
|
319
|
-
opts[:
|
318
|
+
opts[:cache] = self.cache || false
|
319
|
+
opts[:use_master_key] = self.use_master_key
|
320
320
|
opts[:session_token] = self.session_token
|
321
321
|
# for now, don't cache requests where we disable master_key or provide session token
|
322
|
-
if opts[:
|
322
|
+
if opts[:use_master_key] == false || opts[:session_token].present?
|
323
323
|
opts[:cache] = false
|
324
324
|
end
|
325
325
|
|
data/lib/parse/stack.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'parse/stack'
|
2
|
+
require 'parse/stack/tasks'
|
3
|
+
require 'rails/generators'
|
4
|
+
require 'rails/generators/named_base'
|
5
|
+
|
6
|
+
module ParseStack
|
7
|
+
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
9
|
+
source_root File.expand_path("../templates", __FILE__)
|
10
|
+
|
11
|
+
desc "This generator creates an initializer file at config/initializers"
|
12
|
+
def generate_initializer
|
13
|
+
copy_file "parse.rb", "config/initializers/parse.rb"
|
14
|
+
copy_file "model_user.rb", File.join("app/models", "user.rb")
|
15
|
+
copy_file "model_role.rb", File.join("app/models", "role.rb")
|
16
|
+
copy_file "model_session.rb", File.join("app/models", "session.rb")
|
17
|
+
copy_file "model_installation.rb", File.join("app/models", "installation.rb")
|
18
|
+
copy_file "webhooks.rb", File.join("app/models", "webhooks.rb")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ModelGenerator < Rails::Generators::NamedBase
|
23
|
+
source_root File.expand_path(__dir__ + "/templates")
|
24
|
+
desc "Creates a Parse::Object model subclass."
|
25
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
26
|
+
check_class_collision
|
27
|
+
|
28
|
+
def create_model_file
|
29
|
+
@allowed_types = Parse::Properties::TYPES - [:acl, :id, :relation]
|
30
|
+
template "model.erb", File.join("app/models", class_path, "#{file_name}.rb")
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
class <%= class_name %> < Parse::Object
|
3
|
+
# See: https://github.com/modernistik/parse-stack#defining-properties
|
4
|
+
|
5
|
+
# You can change the inferred Parse table/collection name below
|
6
|
+
# parse_class "<%= class_name.to_s.to_parse_class %>"
|
7
|
+
<% attributes.each do |attr|
|
8
|
+
parse_type = attr.type.to_s.downcase.to_sym
|
9
|
+
unless @allowed_types.include?(parse_type)
|
10
|
+
puts "\n[Warning] Skipping property `#{attr.name}` with type `#{parse_type}`. Type should be one of #{@allowed_types}."
|
11
|
+
next
|
12
|
+
end
|
13
|
+
%>
|
14
|
+
property :<%= attr.name %>, :<%= parse_type -%>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
# See: https://github.com/modernistik/parse-stack#cloud-code-webhooks
|
18
|
+
# define a before save webhook for <%= class_name %>
|
19
|
+
webhook :before_save do
|
20
|
+
<%= class_name.to_s.underscore %> = parse_object
|
21
|
+
# perform any validations with <%= class_name.to_s.underscore %>
|
22
|
+
# use `error!(msg)` to fail the save
|
23
|
+
# ...
|
24
|
+
<%= class_name.to_s.underscore %>
|
25
|
+
end
|
26
|
+
|
27
|
+
## define an after save webhook for <%= class_name %>
|
28
|
+
#
|
29
|
+
# webhook :after_save do
|
30
|
+
# <%= class_name.to_s.underscore %> = parse_object
|
31
|
+
#
|
32
|
+
# end
|
33
|
+
|
34
|
+
## define a before delete webhook for <%= class_name %>
|
35
|
+
# webhook :before_delete do
|
36
|
+
# <%= class_name.to_s.underscore %> = parse_object
|
37
|
+
# # use `error!(msg)` to fail the delete
|
38
|
+
# true # allow the deletion
|
39
|
+
# end
|
40
|
+
|
41
|
+
## define an after delete webhook for <%= class_name %>
|
42
|
+
# webhook :after_delete do
|
43
|
+
# <%= class_name.to_s.underscore %> = parse_object
|
44
|
+
# end
|
45
|
+
|
46
|
+
## Example of a CloudCode Webhook function
|
47
|
+
## define a `helloWorld` Parse CloudCode function
|
48
|
+
# webhook :function, :helloWorld do
|
49
|
+
# "Hello!"
|
50
|
+
# end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
# See: https://github.com/modernistik/parse-stack#parseuser
|
3
|
+
class Parse::User < Parse::Object
|
4
|
+
# add additional properties
|
5
|
+
|
6
|
+
# define a before save webhook for Parse::User
|
7
|
+
# webhook :before_save do
|
8
|
+
# obj = parse_object # Parse::User
|
9
|
+
# # make changes to record....
|
10
|
+
# obj # will send the proper changelist back to Parse-Server
|
11
|
+
# end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'parse/stack'
|
2
|
+
|
3
|
+
# Set your specific Parse keys in your ENV. For all connection options, see
|
4
|
+
# https://github.com/modernistik/parse-stack#connection-setup
|
5
|
+
|
6
|
+
Parse.setup app_id: ENV['PARSE_APP_ID'],
|
7
|
+
api_key: ENV['PARSE_API_KEY'],
|
8
|
+
master_key: ENV['PARSE_MASTER_KEY'],
|
9
|
+
server_url: 'https://api.parse.com/1/'
|
10
|
+
# optional
|
11
|
+
# logging: false,
|
12
|
+
# cache: Moneta.new(:File, dir: 'tmp/cache'),
|
13
|
+
# expires: 1 # cache ttl 1 second
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
# See: https://github.com/modernistik/parse-stack#cloud-code-webhooks
|
3
|
+
Parse::Webhooks.route(:function, :helloWorld) do
|
4
|
+
# use the Parse::Payload instance methods in this block
|
5
|
+
name = params['name'].to_s #function params
|
6
|
+
|
7
|
+
# will return proper error response
|
8
|
+
# error!("Missing argument 'name'.") unless name.present?
|
9
|
+
|
10
|
+
name.present? ? "Hello #{name}!" : "Hello World!"
|
11
|
+
end
|
data/lib/parse/stack/tasks.rb
CHANGED
@@ -18,11 +18,27 @@ module Parse
|
|
18
18
|
|
19
19
|
def install_tasks
|
20
20
|
|
21
|
+
if defined?(::Rails)
|
22
|
+
unless Rake::Task.task_defined?('db:seed') || Rails.root.blank?
|
23
|
+
namespace :db do
|
24
|
+
desc "Seeds your database with by loading db/seeds.rb"
|
25
|
+
task :seed => 'parse:env' do
|
26
|
+
load Rails.root.join("db","seeds.rb")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
21
32
|
namespace :parse do
|
22
33
|
|
23
34
|
task :env do
|
35
|
+
|
24
36
|
if Rake::Task.task_defined?('environment')
|
25
37
|
Rake::Task['environment'].invoke
|
38
|
+
if defined?(::Rails)
|
39
|
+
Rails.application.eager_load! if Rails.application.present?
|
40
|
+
end
|
41
|
+
|
26
42
|
end
|
27
43
|
end
|
28
44
|
|
@@ -32,7 +48,7 @@ module Parse
|
|
32
48
|
raise "Please make sure you have setup the Parse.setup configuration before invoking task. Usually done in the :environment task."
|
33
49
|
end
|
34
50
|
|
35
|
-
endpoint = ENV['HOOKS_URL']
|
51
|
+
endpoint = ENV['HOOKS_URL'] || ''
|
36
52
|
unless endpoint.empty? || endpoint.starts_with?('https://')
|
37
53
|
raise "The ENV variable HOOKS_URL must be a <https> url : '#{endpoint}'. Ex. https://12345678.ngrok.io/webhooks"
|
38
54
|
end
|
data/lib/parse/stack/version.rb
CHANGED
data/lib/parse/webhooks.rb
CHANGED
@@ -68,16 +68,6 @@ module Parse
|
|
68
68
|
|
69
69
|
end
|
70
70
|
|
71
|
-
def update_payload
|
72
|
-
h = attribute_updates
|
73
|
-
if relation_changes?
|
74
|
-
r = relation_change_operations.select { |s| s.present? }.first
|
75
|
-
h.merge!(r) if r.present?
|
76
|
-
end
|
77
|
-
h.merge!(className: parse_class) unless h.empty?
|
78
|
-
h.as_json
|
79
|
-
end
|
80
|
-
|
81
71
|
end
|
82
72
|
|
83
73
|
class Payload
|
@@ -147,10 +137,26 @@ module Parse
|
|
147
137
|
return unless routes[type].present? && routes[type][className].present?
|
148
138
|
registry = routes[type][className]
|
149
139
|
|
150
|
-
|
151
|
-
|
152
|
-
|
140
|
+
if registry.is_a?(Array)
|
141
|
+
result = registry.map { |hook| payload.instance_exec(payload, &hook) }.last
|
142
|
+
else
|
143
|
+
result = payload.instance_exec(payload, ®istry)
|
144
|
+
end
|
145
|
+
|
146
|
+
if result.is_a?(Parse::Object)
|
147
|
+
# if it is a Parse::Object, we will call the registered ActiveModel callbacks
|
148
|
+
# and then send the proper changes payload
|
149
|
+
if type == :before_save
|
150
|
+
# returning false from the callback block only runs the before_* callback
|
151
|
+
result.prepare_save!
|
152
|
+
result = result.changes_payload
|
153
|
+
elsif type == :before_delete
|
154
|
+
result.run_callbacks(:destroy) { false }
|
155
|
+
result = true
|
156
|
+
end
|
157
|
+
end
|
153
158
|
|
159
|
+
result
|
154
160
|
end
|
155
161
|
|
156
162
|
def success(data = true)
|
@@ -36,7 +36,7 @@ module Parse
|
|
36
36
|
|
37
37
|
def register_functions!(endpoint)
|
38
38
|
|
39
|
-
unless endpoint.starts_with?('https://')
|
39
|
+
unless endpoint.present? && endpoint.starts_with?('https://')
|
40
40
|
raise "The HOOKS_URL must be https: '#{endpoint}''"
|
41
41
|
end
|
42
42
|
endpoint += '/' unless endpoint.ends_with?('/')
|
@@ -61,7 +61,7 @@ module Parse
|
|
61
61
|
|
62
62
|
def register_triggers!(endpoint, include_wildcard: false)
|
63
63
|
|
64
|
-
unless endpoint.starts_with?('https://')
|
64
|
+
unless endpoint.present? && endpoint.starts_with?('https://')
|
65
65
|
raise "The HOOKS_URL must be https: '#{endpoint}''"
|
66
66
|
end
|
67
67
|
endpoint += '/' unless endpoint.ends_with?('/')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parse-stack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3
|
4
|
+
version: 1.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Persaud
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -228,6 +228,15 @@ files:
|
|
228
228
|
- lib/parse/query/operation.rb
|
229
229
|
- lib/parse/query/ordering.rb
|
230
230
|
- lib/parse/stack.rb
|
231
|
+
- lib/parse/stack/generators/rails.rb
|
232
|
+
- lib/parse/stack/generators/templates/model.erb
|
233
|
+
- lib/parse/stack/generators/templates/model_installation.rb
|
234
|
+
- lib/parse/stack/generators/templates/model_role.rb
|
235
|
+
- lib/parse/stack/generators/templates/model_session.rb
|
236
|
+
- lib/parse/stack/generators/templates/model_user.rb
|
237
|
+
- lib/parse/stack/generators/templates/parse.rb
|
238
|
+
- lib/parse/stack/generators/templates/webhooks.rb
|
239
|
+
- lib/parse/stack/railtie.rb
|
231
240
|
- lib/parse/stack/tasks.rb
|
232
241
|
- lib/parse/stack/version.rb
|
233
242
|
- lib/parse/webhooks.rb
|
@@ -254,7 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
254
263
|
version: '0'
|
255
264
|
requirements: []
|
256
265
|
rubyforge_project:
|
257
|
-
rubygems_version: 2.
|
266
|
+
rubygems_version: 2.6.6
|
258
267
|
signing_key:
|
259
268
|
specification_version: 4
|
260
269
|
summary: Parse-Server Ruby Client and Active Model Object Relational Mapping
|