opium 1.3.5 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +4 -0
- data/README.md +337 -2
- data/lib/generators/opium/templates/config.yml +22 -8
- data/lib/opium/config.rb +9 -7
- data/lib/opium/model/connectable.rb +5 -1
- data/lib/opium/version.rb +1 -1
- data/opium.gemspec +1 -1
- data/spec/opium/config/opium.yml +3 -1
- data/spec/opium/config_spec.rb +41 -33
- data/spec/opium/file_spec.rb +73 -73
- data/spec/opium/model/connectable_spec.rb +95 -51
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2703590e11e240c7ad03f62e04a0c19ff0cdf11
|
4
|
+
data.tar.gz: 139dac0e475494014a9996e417d2332705aba373
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81e4d0868f9e80dc8d08412bcf4b54ca67007d39bfb17c10a77ae9997fa4f7822775f9c6bc3e0ba5ae951c9bb22291fadca78529adb4602c8d9c8620ac0a7f10
|
7
|
+
data.tar.gz: 340904910c91a3a50f7a1d38420cddca627653aca189507af4240fdb524ae545f98e5427e6c17c4c203b22898023a5a774c61a5d30a565525e72f6a8dde7ed85
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## 1.4.0
|
2
|
+
### New Features
|
3
|
+
- #52: Opium should now be capable of easily connecting to a custom parse-server instance hosted on a third party server.
|
4
|
+
|
1
5
|
## 1.3.5
|
2
6
|
### New Features
|
3
7
|
- Due to ignorance, missed a method override for Opium::GeoPoint ===. Now implemented at the class level as well as at the instance level.
|
data/README.md
CHANGED
@@ -37,7 +37,18 @@ $ rails g opium:config
|
|
37
37
|
|
38
38
|
See the generated file at `config/opium.yml` for more details
|
39
39
|
|
40
|
-
|
40
|
+
### Parse.com server closure note
|
41
|
+
|
42
|
+
Opium was originally written to communicate with apps hosted on Parse.com; as hosted parse is hitting end-of-life in January 2017, any parse apps which wish to continue using the infrastructure need to be [migrated to third-party hosted parse-server instance](https://www.parse.com/migration).
|
43
|
+
|
44
|
+
As of version 1.4.0, Opium should be able to communicate with these third-party installations. Within the generated configuration file, two settings need to be updated to point Opium at the proper server instance:
|
45
|
+
|
46
|
+
- `server_url`: the URL of the server on which a parser-server API instance is hosted.
|
47
|
+
- `mount_point`: the sub-URI endpoint on `server_url` where Opium can reach the API.
|
48
|
+
|
49
|
+
As the config file suggests, it is suggested that these values be provided in a server environment via environment variables.
|
50
|
+
|
51
|
+
### Model Generator
|
41
52
|
|
42
53
|
A generator exists for creating new models; this should be invoked whenever `rails g model` gets invoked.
|
43
54
|
|
@@ -45,6 +56,12 @@ A generator exists for creating new models; this should be invoked whenever `rai
|
|
45
56
|
$ rails g model game title:string price:float
|
46
57
|
```
|
47
58
|
|
59
|
+
A separate generate is available for creating a model to wrap Parse's User model:
|
60
|
+
|
61
|
+
```bash
|
62
|
+
$ rails g opium:user
|
63
|
+
```
|
64
|
+
|
48
65
|
### Specifying a model
|
49
66
|
|
50
67
|
Models are defined by mixing in `Opium::Model` into a new class. Class names should match the names of the
|
@@ -54,7 +71,7 @@ class.
|
|
54
71
|
```ruby
|
55
72
|
class Game
|
56
73
|
include Opium::Model
|
57
|
-
|
74
|
+
|
58
75
|
field :title, type: String
|
59
76
|
field :price, type: Float
|
60
77
|
end
|
@@ -63,6 +80,324 @@ end
|
|
63
80
|
All models automatically come with three fields: *:id*, *:created_at*, and *:updated_at*. Field names are
|
64
81
|
converted from a native ruby snake_case naming convention to a Parse lowerCamel convention.
|
65
82
|
|
83
|
+
#### Field data types
|
84
|
+
|
85
|
+
Opium comes with support for a variety of different data types for fields. These automatically will convert native ruby representations of the stored values to values supported by the parse backend, and conversely. At this time, Opium supports the following field types, where the first column is the type specified in ruby, and the second column is the type as stored in parse.
|
86
|
+
|
87
|
+
| Ruby Type | Parse Type |
|
88
|
+
|-----------------|------------|
|
89
|
+
| Integer | Number |
|
90
|
+
| Float | Number |
|
91
|
+
| String | String |
|
92
|
+
| Symbol | String |
|
93
|
+
| Date | Date |
|
94
|
+
| DateTime | Date |
|
95
|
+
| Time | Date |
|
96
|
+
| Array | Array |
|
97
|
+
| Opium::Boolean | Boolean |
|
98
|
+
| Opium::GeoPoint | GeoPoint |
|
99
|
+
| Opium::File | File |
|
100
|
+
| Opium::Pointer | Pointer |
|
101
|
+
|
102
|
+
Field setters will generally attempt to convert any incoming value to a native ruby representation, as noted above. Opium will automatically convert these values to a parse-friendly representation as necessary: e.g., when performing a query or persisting data.
|
103
|
+
|
104
|
+
Setting the type for a field is done by specifying the `:type` option on the field method. If this option is not present, the field will default to a ruby type of `Object`, which acts as a pass-through of the values coming from and going to parse. In the example from the last section, the `Game` model has two fields, one which is specified as having a `String` type, while the other has a `Float` type.
|
105
|
+
|
106
|
+
#### Field options
|
107
|
+
|
108
|
+
Fields can be modified by a small set of options passed to their definition:
|
109
|
+
|
110
|
+
- `readonly`: Expects a boolean value. If present, Opium will prevent the field from being altered locally. (The associated parse column may still be modified from other locations outside of Opium.)
|
111
|
+
- `as`: Expects a string or symbol value. If present, this will specify the name of the associated column within parse where the value should be stored. This allows the field to be named something more semantically useful locally.
|
112
|
+
- `type`: See the previous section. Expects a class constant.
|
113
|
+
- `default`: Expects either a logically convertible literal or a lambda providing the same. If a lambda is provided, it will be evaluated each time a model is instantiated, unless a value is provided for the field.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
class Article
|
117
|
+
include Opium::Model
|
118
|
+
|
119
|
+
field :title, type: String, readonly: true
|
120
|
+
field :edited_on, type: Date, as: :last_edited
|
121
|
+
field :published_at, type: Date, default: -> { Time.now }
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
In the preceding example, an `Article` is never allowed to alter its `title`, while its `last_edited` field is locally aliased as `edited_on`, and it will provide a default value for `published_at`, should none otherwise be provided.
|
126
|
+
|
127
|
+
#### Model associations
|
128
|
+
|
129
|
+
Opium currently supports basic associations between models: an owning model can specify that it `has_many` of another model, which can specify that it `belongs_to` the former.
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
class Player
|
133
|
+
# ...
|
134
|
+
has_many :high_scores
|
135
|
+
# ...
|
136
|
+
end
|
137
|
+
|
138
|
+
class HighScore
|
139
|
+
# ...
|
140
|
+
belongs_to :player
|
141
|
+
# ...
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
Opium will attempt to infer the class name and inverse method of an association by standard Rails naming conventions: the singular, classified variant of the method name is taken to be the target class. In case naming conventions prohibit this inference from working properly, the following options are available:
|
146
|
+
|
147
|
+
- `class_name`: Expects a string. In case the class name cannot be inferred from the association name, it can be provided by this option.
|
148
|
+
- `inverse_of`: Expects a string or a symbol. In case the inverse method name cannot be inferred from the association name or its class name, it can be provided by this option.
|
149
|
+
|
150
|
+
Associations will be covered in more detail in the sections covering [creating models](#creating-and-updating-models) and [querying data](#querying-data). For now, note that Opium will attempt to manage the relationships between associated models for you and provides a robust, Rails-centric approach to manipulating and querying them.
|
151
|
+
|
152
|
+
#### Model field metadata
|
153
|
+
|
154
|
+
A set of utility class methods are provided to survey the defined fields and associations on any given model:
|
155
|
+
|
156
|
+
- `#fields`: returns a hash of all defined fields, keyed by the name of the field. Each value stored within the hash is an [`Opium::Model::Field`](lib/opium/model/field.rb) object, which has methods which reflect the settings discussed in [Field options](#field-options).
|
157
|
+
- `#has_field?` / `#field?`: expects a string or symbol, and returns a boolean value denoting whether the field is currently defined on the model.
|
158
|
+
- `#relations`: returns a hash of all defined associations, keyed by the method name used to define the relationship on the current model. Each value is a [`Opium::Model::Relatable::Metadata`](lib/opium/model/relatable/metadata.rb), which contains details pertaining to what the association is being made between.
|
159
|
+
|
160
|
+
Each of these methods would be called on the model you wish to inspect. In the following example, we ask the `Player` model if it has a `:name` field, get all of its readonly fields, and grab a list of its associations:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class Player
|
164
|
+
include Opium::Model
|
165
|
+
field :name, type: String
|
166
|
+
field :gamer_score, type: Integer
|
167
|
+
has_many :high_scores
|
168
|
+
has_many :played_games, class_name: 'GameSave', inverse_of: :played_by
|
169
|
+
end
|
170
|
+
|
171
|
+
# Should output the message, as Player does define a field called "name"
|
172
|
+
puts "'Player' has a name field!" if Player.has_field? :name
|
173
|
+
|
174
|
+
# Winnows the collection of field definitions by selecting only those which are readonly.
|
175
|
+
readonly_fields = Player.fields.select {|_, field| field.readonly?}
|
176
|
+
|
177
|
+
# #relations is a hash, indexed by name; in this example, the output should include the text "high_scores, played_games".
|
178
|
+
puts "'Player' defines the following associations: #{ Player.relations.keys.join(', ') }"
|
179
|
+
```
|
180
|
+
|
181
|
+
Model metadata is readonly; it shouldn't be updated after the model has been defined. It might be useful, however, for writing model concerns or some sort of view decorator to help DRY up model usage in Rails.
|
182
|
+
|
183
|
+
#### Validations
|
184
|
+
|
185
|
+
Opium provides access to `ActiveModel::Validations` on a per model basis, so it is possible to validate the integrity of any data stored within an instance prior to saving it. Validations follow the normal ActiveModel format:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
class Article
|
189
|
+
include Opium::Model
|
190
|
+
field :title, type: String
|
191
|
+
# ...
|
192
|
+
validates :title, presence: true
|
193
|
+
end
|
194
|
+
|
195
|
+
article = Article.new
|
196
|
+
article.valid? # false, as .title is nil.
|
197
|
+
article.errors # Standard ActiveModel::Errors object
|
198
|
+
article.title = 'Wibbly Wobbly Timey Wimey'
|
199
|
+
article.valid? # true, as .title has a value.
|
200
|
+
```
|
201
|
+
|
202
|
+
As is standard with `ActiveModel` compliant libraries, attempting to save an invalid model will either return false (and not trigger the save) or raise an exception, depending upon how the save was triggered.
|
203
|
+
|
204
|
+
#### Callback hooks
|
205
|
+
|
206
|
+
Each Opium model has a set of callback points which can be hooked into. Adhering to the standard of `ActiveModel::Callbacks`, these provide a model a means by which to tie into various parts of an instance's lifecycle.
|
207
|
+
|
208
|
+
A full list of the available callbacks can be found by accessing the following constant:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
Opium::Model::Callbacks::CALLBACKS
|
212
|
+
```
|
213
|
+
|
214
|
+
Opium defines callbacks for the following events:
|
215
|
+
|
216
|
+
| Event / Action | Supported hooks |
|
217
|
+
|----------------|-----------------------|
|
218
|
+
| Save | before, after, around |
|
219
|
+
| Create | before, after, around |
|
220
|
+
| Update | before, after, around |
|
221
|
+
| Destroy | before, after, around |
|
222
|
+
| Initialize | after |
|
223
|
+
| Find | after |
|
224
|
+
| Touch | after |
|
225
|
+
| Validation | before, after |
|
226
|
+
|
227
|
+
To define a callback, do something like in the following example. (This example uses Dirty attributes, which are discussed in the next section.)
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
class Game
|
231
|
+
# ...
|
232
|
+
field :price, type: Float
|
233
|
+
has_many :on_wishlists, class_name: Wishlist
|
234
|
+
# ...
|
235
|
+
|
236
|
+
before_save :notify_price_drop
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
def notify_price_drop
|
241
|
+
if price_changed? && price_was > price
|
242
|
+
# Send a message to all players who have the game on their wishlist ...
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
```
|
247
|
+
|
248
|
+
Please note that callbacks are only invokable within the context of Opium running in Ruby itself; these do not define Cloud Code JavaScript methods within the parse-server backend. If you need to tie into an instance's lifecycle outside the scope of a Ruby project, it is suggested that you look at using Cloud Code directly.
|
249
|
+
|
250
|
+
#### Dirty attribute tracking
|
251
|
+
|
252
|
+
Models will automatically gain attribute tracking, provided by `ActiveModel::Dirty`. As the fields of a model are altered, specialty events, of the form `<field-name>_will_change!` will be raised. At any point prior to saving, you can use the full suite of Dirty methods to query an instance about changes which have occurred to it. Dirty tracking follows a cycle: upon instance initialization, the model has no changes; upon a field changing, the current set of changes is updated; upon successfully saving the model, the current changes are cleared, and the previous changes get updated.
|
253
|
+
|
254
|
+
```ruby
|
255
|
+
class Article
|
256
|
+
include Opium::Model
|
257
|
+
field :title, type: String
|
258
|
+
field :body, type: String
|
259
|
+
end
|
260
|
+
|
261
|
+
article = Article.new
|
262
|
+
article.changes # Should be empty
|
263
|
+
|
264
|
+
# As we are updating the .title, it'll flag the dirty tracking with its update;
|
265
|
+
# as it has changed, some output denoting the alteration should be made.
|
266
|
+
article.title = 'Something happen'
|
267
|
+
puts article.title_change.inspect if article.title_changed?
|
268
|
+
```
|
269
|
+
|
270
|
+
Dirty tracking is provided for any attribute defined by the [`field`](#specifying-a-model) method.
|
271
|
+
|
272
|
+
#### JSON serialization
|
273
|
+
|
274
|
+
All Opium models should be serializable to JSON, using `ActiveModel::Serialization`. Note that a model instance does not include a root node.
|
275
|
+
|
276
|
+
JSON serialization is built around an object's `attributes` hash, which is publicly accessible. By default, all fields of the model are included within this hash. You can also store non-field data within `attributes` and have it show up in the JSON output.
|
277
|
+
|
278
|
+
Be aware that all Opium models use `ActiveModel::ForbiddenAttributesProtection` for mass assignment sanitization.
|
279
|
+
|
280
|
+
### Creating and updating models
|
281
|
+
|
282
|
+
After defining a model with Opium, you might want to create new instances of it, or update the data of an existing instance. Opium has been designed to be familiar to anyone who has used other Rails-centric ORMs, such as ActiveRecord. In this regard, object creation follows two patterns: delayed persistence, and immediate persistence.
|
283
|
+
|
284
|
+
With delayed persistence, a model object is partially built and capable of being manipulated before being persisted. To finally create the model in parse-server, call its `save` method:
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
class Player
|
288
|
+
include Opium::Model
|
289
|
+
field :name, type: String
|
290
|
+
field :gamer_score, type: Integer, default: 0
|
291
|
+
end
|
292
|
+
|
293
|
+
player = Player.new( name: 'The Doctor' )
|
294
|
+
player.gamer_score = 1000
|
295
|
+
|
296
|
+
player.persisted? # false, as it has not yet been saved
|
297
|
+
player.new_record? # true, as it has not yet been saved
|
298
|
+
|
299
|
+
if player.save
|
300
|
+
# persisted!
|
301
|
+
player.persisted? # true, as it has been saved
|
302
|
+
else
|
303
|
+
# there was a problem!
|
304
|
+
end
|
305
|
+
```
|
306
|
+
|
307
|
+
As this example suggests, you build a new model instance by calling its constructor, which accepts an attributes hash. The model may be altered and updated to taste. When ready to save, call the `save` method. `save` will run validations on the model, fire off any defined callbacks, update dirty tracking, and attempt to persist the model to parse-server. Should any of these steps fail, `save` will return false, and halt the operation at the point of failure. Otherwise, `save` will return true.
|
308
|
+
|
309
|
+
Alternatively, you can use `save!`, which, in the event of a failure, will raise an exception.
|
310
|
+
|
311
|
+
Note that the model's `id`, `created_at`, and `updated_at` fields will not have a value until it has been persisted.
|
312
|
+
|
313
|
+
With immediate persistence, a model object is built and then immediately stored to parse-server. This is achieved using the `create` class method, which accepts an attributes hash:
|
314
|
+
|
315
|
+
```ruby
|
316
|
+
player = Player.create( name: 'The Doctor', gamer_score: 1000 )
|
317
|
+
player.persisted? # true, as it has been saved
|
318
|
+
```
|
319
|
+
|
320
|
+
`create` behaves like `save!`: if there was a failure at any point in the persistence process, an exception will be raised. If there is no failure, the object returned by the method is a persisted model within parse-server.
|
321
|
+
|
322
|
+
Updating a model follows a similar set of patterns: at any time you wish to store changes to a persisted model, call `save` or `save!`:
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
player.gamer_score += 50
|
326
|
+
player.save!
|
327
|
+
```
|
328
|
+
|
329
|
+
Alternatively, if you want to update the attributes of a model and save it simultaneously, you can do so with either `update` or `update!`, which behave analogously to the save methods:
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
if player.update( gamer_score: player.gamer_score + 50 )
|
333
|
+
# persisted!
|
334
|
+
else
|
335
|
+
# There was a problem!
|
336
|
+
end
|
337
|
+
|
338
|
+
player.update!( gamer_score: 2000 )
|
339
|
+
```
|
340
|
+
|
341
|
+
`update` is aliased as `update_attributes`, and `update!` is aliased as `update_attributes!`; use whichever makes more semantic sense to you.
|
342
|
+
|
343
|
+
If a model has any associations, it will attempt to persist the changes to the association, which might very well cause a cascade of persistence. Opium will attempt to only trigger a save call to a model if it needs to. Due to parse-server's unique way of representing model associations in its API, updating the association between two models does require a separate API call. Be wary when attempting to modify many models simultaneously.
|
344
|
+
|
345
|
+
When you define an association between two models, one of those models receives a special collection field of type `Opium::Model::Relation` for storing instances of the other model. This collection has utility methods for building new instances of the associated model:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
class Player
|
349
|
+
# ...
|
350
|
+
has_many :high_scores
|
351
|
+
# ...
|
352
|
+
end
|
353
|
+
|
354
|
+
class HighScore
|
355
|
+
# ...
|
356
|
+
belongs_to :player
|
357
|
+
# ...
|
358
|
+
end
|
359
|
+
|
360
|
+
player = Player.new
|
361
|
+
player.high_scores.class # Opium::Model::Relation
|
362
|
+
player.high_scores.count # 0, as there are none, yet.
|
363
|
+
|
364
|
+
score1 = player.high_scores.build
|
365
|
+
score1.value = 200
|
366
|
+
|
367
|
+
# .build is aliased as .new; either accepts an attributes hash.
|
368
|
+
score2 = player.high_scores.new( value: 1000 )
|
369
|
+
|
370
|
+
player.high_scores.count # 2, from the previous actions
|
371
|
+
|
372
|
+
fail "That shouldn't have happened" unless score1.player == player
|
373
|
+
```
|
374
|
+
|
375
|
+
As you might expect, using a relation's build method will automatically add the built associated model to the collection; using this method will also cause the built model to point at the owner of the collection. You can also update the owner of a particular model on that model directly:
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
score3 = HighScore.new( value: 10000 )
|
379
|
+
score3.player = player
|
380
|
+
|
381
|
+
player.high_scores.include?( score3 ) # true, as the above setter updates player.
|
382
|
+
```
|
383
|
+
|
384
|
+
### Querying data
|
385
|
+
|
386
|
+
#### Find by id
|
387
|
+
|
388
|
+
#### Criteria & Scopes
|
389
|
+
|
390
|
+
#### Kaminari support
|
391
|
+
|
392
|
+
Opium comes with support for [Kaminari](https://rubygems.org/gems/kaminari). To ensure that Opium loads itself correctly, please specify it _after_ Kaminari within your Gemfile:
|
393
|
+
|
394
|
+
```Gemfile
|
395
|
+
gem 'kaminari'
|
396
|
+
gem 'opium'
|
397
|
+
```
|
398
|
+
|
399
|
+
Models and Criteria will gain the methods defined by Kaminari, and should be compatible with Kaminari's pagination partials.
|
400
|
+
|
66
401
|
## Contributing
|
67
402
|
|
68
403
|
1. Fork it ( https://github.com/[my-github-username]/opium/fork )
|
@@ -1,25 +1,37 @@
|
|
1
1
|
development:
|
2
2
|
# The Application ID for your Parse app. Mandatory
|
3
3
|
app_id: PARSE-APP-ID
|
4
|
-
|
4
|
+
|
5
5
|
# The REST API key for your Parse app. Mandatory
|
6
6
|
api_key: PARSE-API-KEY
|
7
|
-
|
8
|
-
# Your Parse app's master key setting. You may omit this,
|
9
|
-
# but you will be unable to edit Users except by ACL rules
|
7
|
+
|
8
|
+
# Your Parse app's master key setting. You may omit this,
|
9
|
+
# but you will be unable to edit Users except by ACL rules.
|
10
10
|
master_key: PARSE-MASTER-KEY
|
11
|
-
|
11
|
+
|
12
12
|
# You can store your app's webhook key here; Opium currently does not use this.
|
13
13
|
webhook_key: PARSE-WEBHOOK-KEY
|
14
|
-
|
14
|
+
|
15
|
+
# The URL of the server where the parse API is available. For hosted parse, this
|
16
|
+
# should be https://api.parse.com; for a parse-server installation, this will be
|
17
|
+
# whatever server the instance is running on.
|
18
|
+
server_url: https://api.parse.com
|
19
|
+
|
20
|
+
# The sub-URI on :server_url where the parse-server API is available. For hosted
|
21
|
+
# parse, this should be /1; for a default parse-server-example installation, this
|
22
|
+
# defaults to /parse.
|
23
|
+
mount_point: /1
|
24
|
+
|
15
25
|
# Any communications done with parse will either be (true) displayed or (false) silenced
|
16
26
|
log_network_responses: false
|
17
|
-
|
27
|
+
|
18
28
|
test:
|
19
29
|
app_id: PARSE-TEST-APP-ID
|
20
30
|
api_key: PARSE-TEST-API-KEY
|
21
31
|
master_key: PARSE-TEST-MASTER-KEY
|
22
32
|
webhook_key: PARSE-TEST-WEBHOOK-KEY
|
33
|
+
server_url: https://api.parse.com
|
34
|
+
mount_point: /1
|
23
35
|
log_network_responses: true
|
24
36
|
|
25
37
|
# You could hardcode the values for the production Parse app here, but it is suggested
|
@@ -29,4 +41,6 @@ production:
|
|
29
41
|
api_key: <%= ENV['PARSE_API_KEY'] %>
|
30
42
|
master_key: <%= ENV['PARSE_MASTER_KEY'] %>
|
31
43
|
webhook_key: <%= ENV['PARSE_WEBHOOK_KEY'] %>
|
32
|
-
|
44
|
+
server_url: <%= ENV['PARSE_SERVER_URL'] %>
|
45
|
+
mount_point: <%= ENV['PARSE_MOUNT_POINT'] %>
|
46
|
+
log_network_responses: false
|
data/lib/opium/config.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Opium
|
2
2
|
extend self
|
3
|
-
|
3
|
+
|
4
4
|
def configure
|
5
5
|
yield config
|
6
6
|
end
|
@@ -8,7 +8,7 @@ module Opium
|
|
8
8
|
def config
|
9
9
|
@config ||= Opium::Config.new
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def load!( path, environment = nil )
|
13
13
|
settings = load_yaml( path, environment )
|
14
14
|
configure do |config|
|
@@ -17,11 +17,11 @@ module Opium
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def reset
|
22
22
|
@config = nil
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
private
|
26
26
|
|
27
27
|
def load_yaml( path, environment = nil )
|
@@ -32,14 +32,16 @@ module Opium
|
|
32
32
|
def env_name
|
33
33
|
defined?( Rails ) ? Rails.env : ( ENV["RACK_ENV"] || ENV["OPIUM_ENV"] || raise( "Could not determine environment" ) )
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
class Config
|
37
37
|
include ActiveSupport::Configurable
|
38
|
-
|
38
|
+
|
39
39
|
config_accessor( :app_id ) { 'PARSE_APP_ID' }
|
40
40
|
config_accessor( :api_key ) { 'PARSE_API_KEY' }
|
41
41
|
config_accessor( :master_key ) { 'PARSE_MASTER_KEY' }
|
42
42
|
config_accessor( :webhook_key ) { 'PARSE_WEBHOOK_KEY' }
|
43
43
|
config_accessor( :log_network_responses ) { false }
|
44
|
+
config_accessor( :server_url ) { 'https://api.parse.com' }
|
45
|
+
config_accessor( :mount_point ) { '/1' }
|
44
46
|
end
|
45
|
-
end
|
47
|
+
end
|
@@ -19,8 +19,12 @@ module Opium
|
|
19
19
|
end
|
20
20
|
|
21
21
|
module ClassMethods
|
22
|
+
def parse_server_url
|
23
|
+
::URI.join( ::Opium.config.server_url, ::Opium.config.mount_point ).to_s
|
24
|
+
end
|
25
|
+
|
22
26
|
def connection
|
23
|
-
@@connection ||= Faraday.new( url:
|
27
|
+
@@connection ||= Faraday.new( url: parse_server_url ) do |faraday|
|
24
28
|
faraday.request :multipart
|
25
29
|
faraday.request :url_encoded
|
26
30
|
faraday.request :json
|
data/lib/opium/version.rb
CHANGED
data/opium.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'opium/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "opium"
|
8
8
|
spec.version = Opium::VERSION
|
9
|
-
spec.required_ruby_version = '>=
|
9
|
+
spec.required_ruby_version = '>= 2.0.0'
|
10
10
|
spec.authors = ["Joshua Bowers"]
|
11
11
|
spec.email = ["joshua.bowers+code@gmail.com"]
|
12
12
|
spec.summary = %q{An Object Parse.com Mapping technology for defining models.}
|
data/spec/opium/config/opium.yml
CHANGED
data/spec/opium/config_spec.rb
CHANGED
@@ -1,60 +1,68 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Opium do
|
4
|
-
it {
|
5
|
-
it {
|
6
|
-
it {
|
7
|
-
|
8
|
-
describe '
|
4
|
+
it { is_expected.to respond_to( :configure, :config ) }
|
5
|
+
it { is_expected.to respond_to( :load! ).with(2).arguments }
|
6
|
+
it { is_expected.to respond_to( :reset ) }
|
7
|
+
|
8
|
+
describe '.configure' do
|
9
9
|
it do
|
10
10
|
expect {|b| Opium.configure(&b) }.to yield_with_args
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
14
|
-
describe '
|
13
|
+
|
14
|
+
describe '.config' do
|
15
15
|
subject { Opium.config }
|
16
|
-
|
17
|
-
it {
|
18
|
-
it {
|
16
|
+
|
17
|
+
it { is_expected.to_not be_nil }
|
18
|
+
it { is_expected.to be_an( Opium::Config ) }
|
19
19
|
end
|
20
|
-
|
21
|
-
describe '
|
20
|
+
|
21
|
+
describe '.reset' do
|
22
22
|
after { described_class.config.log_network_responses = false }
|
23
|
-
|
23
|
+
|
24
24
|
it 'should put all changed settings back to their defaults' do
|
25
25
|
expect { described_class.config.log_network_responses = true }.to change( described_class.config, :log_network_responses ).from( false ).to( true )
|
26
26
|
described_class.reset
|
27
27
|
described_class.config.log_network_responses.should == false
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
31
|
-
describe '
|
30
|
+
|
31
|
+
describe '.load!' do
|
32
32
|
let(:file) { File.join( File.dirname( __FILE__ ), 'config', 'opium.yml' ) }
|
33
|
-
|
33
|
+
|
34
34
|
before do
|
35
35
|
described_class.load!( file, :test )
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
after do
|
39
39
|
described_class.reset
|
40
40
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
|
42
|
+
subject(:config) { described_class.config }
|
43
|
+
|
44
|
+
context 'a valid config file' do
|
45
|
+
it { expect( config.app_id ).to eq 'abcd1234' }
|
46
|
+
it { expect( config.api_key ).to eq 'efgh5678' }
|
47
|
+
it { expect( config.master_key ).to eq '9012ijkl' }
|
48
|
+
it { expect( config.webhook_key ).to eq 'mnop7654' }
|
49
|
+
it { expect( config.log_network_responses ).to eq true }
|
50
|
+
it { expect( config.server_url ).to eq 'https://example.com' }
|
51
|
+
it { expect( config.mount_point ).to eq '/parse' }
|
52
|
+
end
|
47
53
|
end
|
48
|
-
|
54
|
+
|
49
55
|
describe Opium::Config do
|
50
|
-
it { is_expected.to respond_to( :app_id, :api_key, :master_key, :webhook_key, :log_network_responses ) }
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
it { is_expected.to respond_to( :app_id, :api_key, :master_key, :webhook_key, :log_network_responses, :server_url, :mount_point ) }
|
57
|
+
|
58
|
+
context 'a default config' do
|
59
|
+
it { expect( subject.app_id ).to eq 'PARSE_APP_ID' }
|
60
|
+
it { expect( subject.api_key ).to eq 'PARSE_API_KEY' }
|
61
|
+
it { expect( subject.master_key ).to eq 'PARSE_MASTER_KEY' }
|
62
|
+
it { expect( subject.webhook_key ).to eq 'PARSE_WEBHOOK_KEY' }
|
63
|
+
it { expect( subject.log_network_responses ).to eq false }
|
64
|
+
it { expect( subject.server_url ).to eq 'https://api.parse.com' }
|
65
|
+
it { expect( subject.mount_point ).to eq '/1' }
|
58
66
|
end
|
59
67
|
end
|
60
|
-
end
|
68
|
+
end
|