fmrest 0.12.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +3 -0
- data/CHANGELOG.md +31 -0
- data/README.md +252 -851
- metadata +71 -108
- data/.gitignore +0 -26
- data/.rspec +0 -3
- data/.travis.yml +0 -5
- data/Gemfile +0 -3
- data/Rakefile +0 -6
- data/UPGRADING +0 -15
- data/fmrest.gemspec +0 -49
- data/lib/fmrest.rb +0 -36
- data/lib/fmrest/connection_settings.rb +0 -124
- data/lib/fmrest/errors.rb +0 -30
- data/lib/fmrest/spyke.rb +0 -21
- data/lib/fmrest/spyke/base.rb +0 -9
- data/lib/fmrest/spyke/container_field.rb +0 -59
- data/lib/fmrest/spyke/model.rb +0 -33
- data/lib/fmrest/spyke/model/associations.rb +0 -86
- data/lib/fmrest/spyke/model/attributes.rb +0 -163
- data/lib/fmrest/spyke/model/auth.rb +0 -43
- data/lib/fmrest/spyke/model/connection.rb +0 -163
- data/lib/fmrest/spyke/model/container_fields.rb +0 -40
- data/lib/fmrest/spyke/model/global_fields.rb +0 -40
- data/lib/fmrest/spyke/model/http.rb +0 -77
- data/lib/fmrest/spyke/model/orm.rb +0 -256
- data/lib/fmrest/spyke/model/record_id.rb +0 -78
- data/lib/fmrest/spyke/model/serialization.rb +0 -99
- data/lib/fmrest/spyke/model/uri.rb +0 -29
- data/lib/fmrest/spyke/portal.rb +0 -55
- data/lib/fmrest/spyke/relation.rb +0 -359
- data/lib/fmrest/spyke/spyke_formatter.rb +0 -273
- data/lib/fmrest/spyke/validation_error.rb +0 -25
- data/lib/fmrest/string_date.rb +0 -220
- data/lib/fmrest/token_store.rb +0 -12
- data/lib/fmrest/token_store/active_record.rb +0 -74
- data/lib/fmrest/token_store/base.rb +0 -25
- data/lib/fmrest/token_store/memory.rb +0 -26
- data/lib/fmrest/token_store/moneta.rb +0 -41
- data/lib/fmrest/token_store/null.rb +0 -20
- data/lib/fmrest/token_store/redis.rb +0 -45
- data/lib/fmrest/v1.rb +0 -23
- data/lib/fmrest/v1/auth.rb +0 -30
- data/lib/fmrest/v1/connection.rb +0 -115
- data/lib/fmrest/v1/container_fields.rb +0 -114
- data/lib/fmrest/v1/dates.rb +0 -81
- data/lib/fmrest/v1/paths.rb +0 -47
- data/lib/fmrest/v1/raise_errors.rb +0 -57
- data/lib/fmrest/v1/token_session.rb +0 -133
- data/lib/fmrest/v1/token_store/active_record.rb +0 -13
- data/lib/fmrest/v1/token_store/memory.rb +0 -13
- data/lib/fmrest/v1/type_coercer.rb +0 -192
- data/lib/fmrest/v1/utils.rb +0 -94
- data/lib/fmrest/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0740eb9d2b42cd5fb5b8fad3ed5d3d9b74edd9ec3da0b5aec23c000357d11f8
|
4
|
+
data.tar.gz: 612229666f1c67ede1549870d7f1f6abe0c1f3d7773b30161c21033767346f05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0ea144d5f62269ea55d418d16f95109f42925fa49b3ed9f89b9371ad7b386bf5bd97ad1e04bb7d854a53117e948ea8be4c4fb0052a3fd8fdd7d057ac8d00433
|
7
|
+
data.tar.gz: c4ea03c0c8cab5bfe18b8c25889c76edaa0d2269b06f3c6f92d4559c4b4ed2cfac72a9925ef143ad5dae2c68a33b03a9984ba05056e4ebb9d96495abb12653ba
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,40 @@
|
|
1
1
|
## Changelog
|
2
2
|
|
3
|
+
### 0.15.1
|
4
|
+
|
5
|
+
* Fix autoloading of `FmRest::Layout`
|
6
|
+
|
7
|
+
### 0.15.0
|
8
|
+
|
9
|
+
* Much improved querying API (see documentation on querying), adding new
|
10
|
+
`.query` capabilities, as well as two new methods: `.match` and `.or`
|
11
|
+
|
12
|
+
### 0.14.0
|
13
|
+
|
14
|
+
* Aliased `FmRest::Spyke::Base` as `FmRest::Layout` (now preferred), and
|
15
|
+
provided a shortcut version for setting the layout name (e.g. `class Foo <
|
16
|
+
FmRest::Layout("LayoutName")`)
|
17
|
+
* Made `layout` class setting subclass-inheritable
|
18
|
+
|
19
|
+
### 0.13.1
|
20
|
+
|
21
|
+
* Fix downloading of container field data from FMS19+
|
22
|
+
|
23
|
+
### 0.13.0
|
24
|
+
|
25
|
+
* Split `fmrest` gem into `fmrest-core` and `fmrest-spyke`. `fmrest` becomes a
|
26
|
+
wrapper for the two new gems.
|
27
|
+
* Fix bug preventing connection databases with spaces in their names.
|
28
|
+
* Improved portal support with ability to delete portal records, and better
|
29
|
+
refreshing of portal records after saving the parent.
|
30
|
+
* `FmRest::Spyke::Base#__record_id` and `FmRest::Spyke::Base#__mod_id` now
|
31
|
+
always return integers if set.
|
32
|
+
|
3
33
|
### 0.12.0
|
4
34
|
|
5
35
|
* Rename `FmRest::Spyke::Base#id=` to `FmRest::Spyke::Base#__record_id=` to
|
6
36
|
prevent clobbering of FileMaker layout-defined fields
|
37
|
+
* Removed previously deprecated `FmRest::Spyke::Base(config)` syntax
|
7
38
|
* Better yard documentation
|
8
39
|
|
9
40
|
### 0.11.1
|
data/README.md
CHANGED
@@ -2,122 +2,157 @@
|
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/fmrest)
|
4
4
|

|
5
|
+
[](https://rubydoc.info/github/beezwax/fmrest-ruby)
|
5
6
|
|
6
7
|
A Ruby client for
|
7
8
|
[FileMaker 18 and 19's Data API](https://help.claris.com/en/data-api-guide)
|
8
9
|
using
|
9
10
|
[Faraday](https://github.com/lostisland/faraday) and with optional
|
10
|
-
[Spyke](https://github.com/balvig/spyke)
|
11
|
+
ActiveRecord-ish ORM features through [Spyke](https://github.com/balvig/spyke).
|
11
12
|
|
12
|
-
|
13
|
-
API try the fabulous [ginjo-rfm gem](https://github.com/ginjo/rfm) instead.
|
14
|
-
|
15
|
-
fmrest-ruby only partially implements FileMaker 18's Data API.
|
13
|
+
fmrest-ruby only partially implements FileMaker 19's Data API.
|
16
14
|
See the [implementation completeness table](#api-implementation-completeness-table)
|
17
15
|
to see if a feature you need is natively supported by the gem.
|
18
16
|
|
17
|
+
## Gems
|
18
|
+
|
19
|
+
The `fmrest` gem is a wrapper for two other gems:
|
20
|
+
|
21
|
+
* `fmrest-spyke`, providing an ActiveRecord-like ORM library built on top
|
22
|
+
of `fmrest-core` and [Spyke](https://github.com/balvig/spyke).
|
23
|
+
* `fmrest-core`, providing the core Faraday connection builder, session
|
24
|
+
management, and other core utilities.
|
25
|
+
|
19
26
|
## Installation
|
20
27
|
|
21
|
-
Add this
|
28
|
+
Add this to your Gemfile:
|
22
29
|
|
23
30
|
```ruby
|
24
31
|
gem 'fmrest'
|
25
|
-
|
26
|
-
# Optional but recommended (for ORM features)
|
27
|
-
gem 'spyke'
|
28
32
|
```
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
To get a Faraday connection that can handle FM's Data API auth workflow:
|
34
|
+
Or if you just want to use the Faraday connection without the ORM features:
|
33
35
|
|
34
36
|
```ruby
|
35
|
-
|
36
|
-
host: "example.com",
|
37
|
-
database: "database name",
|
38
|
-
username: "username",
|
39
|
-
password: "password"
|
40
|
-
)
|
37
|
+
gem 'fmrest-core'
|
41
38
|
```
|
42
39
|
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
## Simple examples
|
41
|
+
|
42
|
+
### ORM example
|
46
43
|
|
47
|
-
|
44
|
+
Most people would want to use the ORM features:
|
48
45
|
|
49
46
|
```ruby
|
50
|
-
#
|
51
|
-
|
47
|
+
# A Layout model connecting to the "Honeybees Web" FileMaker layout
|
48
|
+
class Honeybee < FmRest::Layout("Honeybees Web")
|
49
|
+
# Connection settings
|
50
|
+
self.fmrest_config = {
|
51
|
+
host: "…",
|
52
|
+
database: "…",
|
53
|
+
username: "…",
|
54
|
+
password: "…"
|
55
|
+
}
|
52
56
|
|
53
|
-
#
|
54
|
-
|
55
|
-
req.url "layouts/MyFancyLayout/records"
|
57
|
+
# Mapped attributes
|
58
|
+
attributes name: "Bee Name", age: "Bee Age", created_on: "Created On"
|
56
59
|
|
57
|
-
#
|
58
|
-
|
60
|
+
# Portal associations
|
61
|
+
has_portal :tasks
|
62
|
+
|
63
|
+
# File containers
|
64
|
+
container :photo, field_name: "Bee Photo"
|
65
|
+
|
66
|
+
# Scopes
|
67
|
+
scope :can_legally_fly, -> { query(age: ">18") }
|
68
|
+
|
69
|
+
# Client-side validations
|
70
|
+
validates :name, presence: true
|
71
|
+
|
72
|
+
# Callbacks
|
73
|
+
before_save :set_created_on
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def set_created_on
|
78
|
+
self.created_on = Date.today
|
79
|
+
end
|
59
80
|
end
|
60
|
-
```
|
61
81
|
|
62
|
-
|
63
|
-
|
82
|
+
# Find a record by id
|
83
|
+
bee = Honeybee.find(9)
|
64
84
|
|
65
|
-
|
85
|
+
bee.name = "Hutch"
|
86
|
+
|
87
|
+
# Add a new record to portal
|
88
|
+
bee.tasks.build(urgency: "Today")
|
66
89
|
|
67
|
-
|
68
|
-
|
69
|
-
in order to log out from the session
|
70
|
-
([see docs](https://fmhelp.filemaker.com/docs/18/en/dataapi/#connect-database_log-out)).
|
90
|
+
bee.save
|
91
|
+
```
|
71
92
|
|
72
|
-
|
73
|
-
token is required to build the logout URL, this becomes a non-trivial action.
|
93
|
+
### Barebones connection example (without ORM)
|
74
94
|
|
75
|
-
|
76
|
-
|
77
|
-
|
95
|
+
In case you don't need the advanced ORM features (e.g. if you only need minimal
|
96
|
+
Data API interaction and just want a lightweight solution) you can simply use
|
97
|
+
the Faraday connection provided by `fmrest-core`:
|
78
98
|
|
79
99
|
```ruby
|
80
|
-
|
81
|
-
|
100
|
+
connection = FmRest::V1.build_connection(
|
101
|
+
host: "…",
|
102
|
+
database: "…",
|
103
|
+
username: "…",
|
104
|
+
password: "…"
|
105
|
+
)
|
106
|
+
|
107
|
+
# Get all records (as parsed JSON)
|
108
|
+
connection.get("layouts/FancyLayout/records")
|
109
|
+
|
110
|
+
# Create new record
|
111
|
+
connection.post do |req|
|
112
|
+
req.url "layouts/FancyLayout/records"
|
113
|
+
|
114
|
+
# You can just pass a hash for the JSON body
|
115
|
+
req.body = { … }
|
116
|
+
end
|
82
117
|
```
|
83
118
|
|
84
|
-
|
85
|
-
|
119
|
+
See the [main document on using the base
|
120
|
+
connection](docs/BaseConnectionUsage.md) for more.
|
86
121
|
|
87
122
|
## Connection settings
|
88
123
|
|
89
|
-
|
90
|
-
|
91
|
-
|
124
|
+
The minimum required connection settings are `:host`, `:database`, `:username`
|
125
|
+
and `:password`, but fmrest-ruby has many other options you can pass when
|
126
|
+
setting up a connection (see [full list](#full-list-of-available-options) below).
|
92
127
|
|
93
|
-
|
128
|
+
`:ssl` and `:proxy` are forwarded to the underlying
|
129
|
+
[Faraday](https://github.com/lostisland/faraday) connection. You can use this
|
130
|
+
to, for instance, disable SSL verification:
|
94
131
|
|
95
132
|
```ruby
|
96
|
-
|
97
|
-
host:
|
98
|
-
|
99
|
-
ssl:
|
100
|
-
|
133
|
+
{
|
134
|
+
host: "…",
|
135
|
+
…
|
136
|
+
ssl: { verify: false }
|
137
|
+
}
|
101
138
|
```
|
102
139
|
|
103
140
|
You can also pass a `:log` option for basic request logging, see the section on
|
104
141
|
[Logging](#Logging) below.
|
105
142
|
|
106
|
-
`:username` is also aliased as `:account_name` to provide cross-compatibility
|
107
|
-
with the ginjo-rfm gem.
|
108
|
-
|
109
143
|
### Full list of available options
|
110
144
|
|
111
145
|
Option | Description | Format | Default
|
112
146
|
--------------------|--------------------------------------------|-----------------------------|--------
|
113
147
|
`:host` | Hostname with optional port, e.g. `"example.com:9000"` | String | None
|
114
|
-
`:database` |
|
115
|
-
`:username` |
|
116
|
-
`:password` |
|
148
|
+
`:database` | The name of the database to connect to | String | None
|
149
|
+
`:username` | A Data API-ready account | String | None
|
150
|
+
`:password` | Your password | String | None
|
151
|
+
`:account_name` | Alias of `:username` | String | None
|
117
152
|
`:ssl` | SSL options to be forwarded to Faraday | Faraday SSL options | None
|
118
153
|
`:proxy` | Proxy options to be forwarded to Faraday | Faraday proxy options | None
|
119
154
|
`:log` | Log JSON responses to STDOUT | Boolean | `false`
|
120
|
-
`:coerce_dates` | See section on [date fields](#date-fields) | Boolean \| `:hybrid` \| `:full` | `false`
|
155
|
+
`:coerce_dates` | See section on [date fields](#date-fields-and-timezones) | Boolean \| `:hybrid` \| `:full` | `false`
|
121
156
|
`:date_format` | Date parsing format | String (FM date format) | `"MM/dd/yyyy"`
|
122
157
|
`:timestamp_format` | Timestmap parsing format | String (FM date format) | `"MM/dd/yyyy HH:mm:ss"`
|
123
158
|
`:time_format` | Time parsing format | String (FM date format) | `"HH:mm:ss"`
|
@@ -132,232 +167,62 @@ through `FmRest.default_connection_settings=`. E.g.:
|
|
132
167
|
|
133
168
|
```ruby
|
134
169
|
FmRest.default_connection_settings = {
|
135
|
-
host: "
|
136
|
-
database: "
|
137
|
-
username: "
|
138
|
-
password: "
|
170
|
+
host: "…",
|
171
|
+
database: "…",
|
172
|
+
username: "…",
|
173
|
+
password: "…"
|
139
174
|
}
|
140
175
|
```
|
141
176
|
|
142
|
-
|
143
|
-
|
144
|
-
|
177
|
+
These settings will be used by default by `FmRest::Layout` models whenever you
|
178
|
+
don't set `fmrest_config=` explicitly, as well as by
|
179
|
+
`FmRest::V1.build_connection` in case you're setting up your Faraday connection
|
180
|
+
manually.
|
145
181
|
|
146
182
|
## Session token store
|
147
183
|
|
148
|
-
|
149
|
-
This is generally good enough for development, but not good enough for
|
150
|
-
production, as in-memory tokens aren't shared across threads/processes.
|
151
|
-
|
152
|
-
Besides the default token store the following token stores are bundled with fmrest-ruby:
|
153
|
-
|
154
|
-
### ActiveRecord
|
155
|
-
|
156
|
-
On Rails apps already using ActiveRecord setting up this token store should be
|
157
|
-
dead simple:
|
158
|
-
|
159
|
-
```ruby
|
160
|
-
# config/initializers/fmrest.rb
|
161
|
-
require "fmrest/token_store/active_record"
|
162
|
-
|
163
|
-
FmRest.token_store = FmRest::TokenStore::ActiveRecord
|
164
|
-
```
|
165
|
-
|
166
|
-
No migrations are needed, the token store table will be created automatically
|
167
|
-
when needed, defaulting to the table name "fmrest_session_tokens". If you want
|
168
|
-
to change the table name you can do so by initializing the token store and
|
169
|
-
passing it the `:table_name` option:
|
170
|
-
|
171
|
-
```ruby
|
172
|
-
FmRest.token_store = FmRest::TokenStore::ActiveRecord.new(table_name: "my_token_store")
|
173
|
-
```
|
174
|
-
|
175
|
-
### Redis
|
176
|
-
|
177
|
-
To use the Redis token store do:
|
178
|
-
|
179
|
-
```ruby
|
180
|
-
require "fmrest/token_store/redis"
|
181
|
-
|
182
|
-
FmRest.token_store = FmRest::TokenStore::Redis
|
183
|
-
```
|
184
|
-
|
185
|
-
You can also initialize it with the following options:
|
186
|
-
|
187
|
-
* `:redis` - A `Redis` object to use as connection, if ommited a new `Redis`
|
188
|
-
object will be created with remaining options
|
189
|
-
* `:prefix` - The prefix to use for token keys, by default `"fmrest-token:"`
|
190
|
-
* Any other options will be passed to `Redis.new` if `:redis` isn't provided
|
191
|
-
|
192
|
-
Examples:
|
193
|
-
|
194
|
-
```ruby
|
195
|
-
# Passing a Redis connection explicitly
|
196
|
-
FmRest.token_store = FmRest::TokenStore::Redis.new(redis: Redis.new, prefix: "my-fancy-prefix:")
|
197
|
-
|
198
|
-
# Passing options for Redis.new
|
199
|
-
FmRest.token_store = FmRest::TokenStore::Redis.new(prefix: "my-fancy-prefix:", host: "10.0.1.1", port: 6380, db: 15)
|
200
|
-
```
|
201
|
-
|
202
|
-
NOTE: redis-rb is not included as a gem dependency of fmrest-ruby, so you'll
|
203
|
-
have to add it to your Gemfile.
|
204
|
-
|
205
|
-
### Moneta
|
206
|
-
|
207
|
-
[Moneta](https://github.com/moneta-rb/moneta) is a key/value store wrapper
|
208
|
-
around many different storage backends. If ActiveRecord or Redis don't suit
|
209
|
-
your needs, chances are Moneta will.
|
210
|
-
|
211
|
-
To use it:
|
212
|
-
|
213
|
-
```ruby
|
214
|
-
# config/initializers/fmrest.rb
|
215
|
-
require "fmrest/token_store/moneta"
|
216
|
-
|
217
|
-
FmRest.token_store = FmRest::TokenStore::Moneta
|
218
|
-
```
|
219
|
-
|
220
|
-
By default the `:Memory` moneta backend will be used.
|
221
|
-
|
222
|
-
You can also initialize it with the following options:
|
223
|
-
|
224
|
-
* `:backend` - The moneta backend to initialize the store with
|
225
|
-
* `:prefix` - The prefix to use for token keys, by default `"fmrest-token:"`
|
226
|
-
* Any other options will be passed to `Moneta.new`
|
227
|
-
|
228
|
-
Examples:
|
229
|
-
|
230
|
-
```ruby
|
231
|
-
# Using YAML as a backend with a custom prefix
|
232
|
-
FmRest.token_store = FmRest::TokenStore::Moneta.new(
|
233
|
-
backend: :YAML,
|
234
|
-
file: "tmp/tokens.yml",
|
235
|
-
prefix: "my-tokens"
|
236
|
-
)
|
237
|
-
```
|
238
|
-
|
239
|
-
NOTE: the moneta gem is not included as a dependency of fmrest-ruby, so
|
240
|
-
you'll have to add it to your Gemfile.
|
241
|
-
|
242
|
-
|
243
|
-
## Date fields
|
244
|
-
|
245
|
-
Since the Data API uses JSON (wich doesn't provide a native date/time object),
|
246
|
-
dates and timestamps are received in string format. By default fmrest-ruby
|
247
|
-
leaves those string fields untouched, but it provides an opt-in feature to try
|
248
|
-
to automatically "coerce" them into Ruby date objects.
|
249
|
-
|
250
|
-
The connection option `:coerce_dates` controls this feature. Possible values
|
251
|
-
are:
|
252
|
-
|
253
|
-
* `:full` - whenever a string matches the given date/timestamp/time format,
|
254
|
-
convert them to `Date` or `DateTime` objects as appropriate
|
255
|
-
* `:hybrid` or `true` - similar as above, but instead of converting to regular
|
256
|
-
`Date`/`DateTime` it converts strings to `FmRest::StringDate` and
|
257
|
-
`FmRest::StringDateTime`, "hybrid" classes provided by fmrest-ruby that
|
258
|
-
retain the functionality of `String` while also providing most the
|
259
|
-
functionality of `Date`/`DateTime` (more on this below)
|
260
|
-
* `false` - disable date coercion entirely (default), leave original string
|
261
|
-
values untouched
|
262
|
-
|
263
|
-
Enabling date coercion works with both basic fmrest-ruby connections and Spyke
|
264
|
-
models (ORM).
|
184
|
+
fmrest-ruby includes a number of options for storing session tokens:
|
265
185
|
|
266
|
-
|
267
|
-
|
268
|
-
|
186
|
+
* Memory
|
187
|
+
* ActiveRecord
|
188
|
+
* Redis
|
189
|
+
* Moneta
|
269
190
|
|
270
|
-
|
271
|
-
|
272
|
-
a DSL in model classes).
|
191
|
+
See the [main document on token stores](docs/TokenStore.md) for detailed info
|
192
|
+
on how to set up each store.
|
273
193
|
|
274
|
-
|
194
|
+
## Date fields and timezones
|
275
195
|
|
276
|
-
|
277
|
-
|
278
|
-
and delegate any methods not provided by `String` to those objects. In other
|
279
|
-
words, they quack like a duck *and* bark like a dog.
|
196
|
+
fmrest-ruby has automatic detection and coercion of date fields to and from
|
197
|
+
Ruby date/time objects. Basic timezone support is also provided.
|
280
198
|
|
281
|
-
|
282
|
-
but you don't want to worry about date coercion of false positives (i.e. a
|
283
|
-
string field that gets converted to `Date` because it just so matched the given
|
284
|
-
date format).
|
199
|
+
See the [main document on date fields](docs/DateFields.md) for more info.
|
285
200
|
|
286
|
-
|
287
|
-
(see GitHub wiki for more info). Some of those gothas can be removed by calling
|
288
|
-
|
289
|
-
```ruby
|
290
|
-
FmRest::StringDateAwareness.enable
|
291
|
-
```
|
292
|
-
|
293
|
-
Which will extend the core `Date` and `DateTime` classes to be aware of
|
294
|
-
`FmRest::StringDate`, especially when calling `Date.===`, `Date.parse` or
|
295
|
-
`Date._parse`.
|
296
|
-
|
297
|
-
If you're working with ActiveRecord models this will also make them accept
|
298
|
-
`FmRest::StringDate` values for date fields.
|
299
|
-
|
300
|
-
### Timezones
|
301
|
-
|
302
|
-
fmrest-ruby has basic timezone support. You can set the `:timezone` option in
|
303
|
-
your connection settings to one of the following values:
|
304
|
-
|
305
|
-
* `:local` - dates will be converted to your system local time offset (as
|
306
|
-
defined by `ENV["TZ"]`), or the timezone set by `Time.zone` if you're using
|
307
|
-
ActiveSupport
|
308
|
-
* `:utc` - dates will be converted to UTC offset
|
309
|
-
* `nil` - (default) ignore timezones altogether
|
310
|
-
|
311
|
-
|
312
|
-
## Spyke support (ActiveRecord-like ORM)
|
201
|
+
## ActiveRecord-like ORM (fmrest-spyke)
|
313
202
|
|
314
203
|
[Spyke](https://github.com/balvig/spyke) is an ActiveRecord-like gem for
|
315
|
-
building REST models. fmrest-ruby
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
```ruby
|
320
|
-
gem 'spyke'
|
321
|
-
```
|
322
|
-
|
323
|
-
Then require fmrest-ruby's Spyke support:
|
324
|
-
|
325
|
-
```ruby
|
326
|
-
# Put this in config/initializers/fmrest.rb if it's a Rails project
|
327
|
-
require "fmrest/spyke"
|
328
|
-
```
|
204
|
+
building REST ORM models. fmrest-ruby builds its ORM features atop Spyke,
|
205
|
+
bundled in the `fmrest-spyke` gem (already included if you're using the
|
206
|
+
`fmrest` gem).
|
329
207
|
|
330
|
-
|
208
|
+
To create a model you can inherit directly from `FmRest::Layout` (itself a
|
209
|
+
subclass of `Spyke::Base`).
|
331
210
|
|
332
211
|
```ruby
|
333
|
-
class Honeybee <
|
334
|
-
include FmRest::Spyke
|
212
|
+
class Honeybee < FmRest::Layout
|
335
213
|
end
|
336
214
|
```
|
337
215
|
|
338
|
-
|
339
|
-
token session auth. Find, create, update and destroy actions should all work
|
340
|
-
as expected.
|
341
|
-
|
342
|
-
Alternatively you can inherit directly from the shorthand
|
343
|
-
`FmRest::Spyke::Base`, which is in itself a subclass of `Spyke::Base` with
|
344
|
-
`FmRest::Spyke` already included:
|
345
|
-
|
346
|
-
```ruby
|
347
|
-
class Honeybee < FmRest::Spyke::Base
|
348
|
-
end
|
349
|
-
```
|
350
|
-
|
351
|
-
All of Spyke's basic ORM operations work:
|
216
|
+
All of Spyke's basic ORM operations work as expected:
|
352
217
|
|
353
218
|
```ruby
|
354
219
|
bee = Honeybee.new
|
355
220
|
|
356
221
|
bee.name = "Hutch"
|
357
|
-
bee.save # POST request
|
222
|
+
bee.save # POST request (creates new record)
|
358
223
|
|
359
224
|
bee.name = "ハッチ"
|
360
|
-
bee.save # PATCH request
|
225
|
+
bee.save # PATCH request (updates existing record)
|
361
226
|
|
362
227
|
bee.reload # GET request
|
363
228
|
|
@@ -366,54 +231,44 @@ bee.destroy # DELETE request
|
|
366
231
|
bee = Honeybee.find(9) # GET request
|
367
232
|
```
|
368
233
|
|
369
|
-
|
234
|
+
It's recommended that you read Spyke's documentation for more information on
|
235
|
+
these basic features. If you've used ActiveRecord or similar ORM libraries
|
236
|
+
you'll find it quite familiar.
|
237
|
+
|
238
|
+
Notice that `FmRest::Layout` is aliased as `FmRest::Spyke::Base`. Previous
|
239
|
+
versions of fmrest-ruby only provided the latter version, so if you're already
|
240
|
+
using `FmRest::Spyke::Base` there's no need to rename your classes to
|
241
|
+
`FmRest::Layout`, both will continue to work interchangeably.
|
370
242
|
|
371
|
-
In addition `FmRest::
|
243
|
+
In addition, `FmRest::Layout` extends `Spyke::Base` with the following
|
372
244
|
features:
|
373
245
|
|
374
|
-
###
|
246
|
+
### FmRest::Layout.fmrest_config=
|
375
247
|
|
376
|
-
|
248
|
+
This allows you to set Data API connection settings specific to your model
|
249
|
+
class:
|
377
250
|
|
378
251
|
```ruby
|
379
|
-
class Honeybee <
|
380
|
-
self.connection = Faraday.new(...)
|
381
|
-
end
|
382
|
-
```
|
383
|
-
|
384
|
-
fmrest-ruby simplfies the process of setting up your Spyke model with a Faraday
|
385
|
-
connection by allowing you to just set your Data API connection settings:
|
386
|
-
|
387
|
-
```ruby
|
388
|
-
class Honeybee < Spyke::Base
|
389
|
-
include FmRest::Spyke
|
390
|
-
|
252
|
+
class Honeybee < FmRest::Layout
|
391
253
|
self.fmrest_config = {
|
392
|
-
host: "
|
393
|
-
database: "
|
394
|
-
username: "
|
395
|
-
password: "
|
254
|
+
host: "…",
|
255
|
+
database: "…",
|
256
|
+
username: "…",
|
257
|
+
password: "…"
|
396
258
|
}
|
397
259
|
end
|
398
260
|
```
|
399
261
|
|
400
|
-
This will automatically create a proper Faraday connection
|
401
|
-
settings.
|
262
|
+
This will automatically create a proper Faraday connection using those
|
263
|
+
connection settings, so you don't have to worry about setting that up.
|
402
264
|
|
403
265
|
Note that these settings are inheritable, so you could create a base class that
|
404
266
|
does the initial connection setup and then inherit from it in models using that
|
405
267
|
same connection. E.g.:
|
406
268
|
|
407
269
|
```ruby
|
408
|
-
class BeeBase <
|
409
|
-
|
410
|
-
|
411
|
-
self.fmrest_config = {
|
412
|
-
host: "example.com",
|
413
|
-
database: "My Database",
|
414
|
-
username: "...",
|
415
|
-
password: "..."
|
416
|
-
}
|
270
|
+
class BeeBase < FmRest::Layout
|
271
|
+
self.fmrest_config = { host: "…", database: "…", … }
|
417
272
|
end
|
418
273
|
|
419
274
|
class Honeybee < BeeBase
|
@@ -421,76 +276,46 @@ class Honeybee < BeeBase
|
|
421
276
|
end
|
422
277
|
```
|
423
278
|
|
424
|
-
|
425
|
-
|
426
|
-
There may be cases where you want to use different connection settings
|
427
|
-
depending on context, for example if you want to use username and password
|
428
|
-
provided by the user in a web application. Since `Model.fmrest_config` is
|
429
|
-
global, changing the username/password for one context would also change it for
|
430
|
-
all other contexts, leading to security issues.
|
431
|
-
|
432
|
-
`Model.fmrest_config_overlay=` solves that issue by allowing you to override
|
433
|
-
some settings in a thread-local and reversible manner. That way, using the same
|
434
|
-
example as above, you could connect to the Data API with user-provided
|
435
|
-
credentials without having them leak into other users of your web application.
|
436
|
-
|
437
|
-
E.g.:
|
438
|
-
|
439
|
-
```ruby
|
440
|
-
class BeeBase < Spyke::Base
|
441
|
-
include FmRest::Spyke
|
442
|
-
|
443
|
-
# Host and database provided as base settings
|
444
|
-
self.fmrest_config = {
|
445
|
-
host: "example.com",
|
446
|
-
database: "My Database"
|
447
|
-
}
|
448
|
-
end
|
449
|
-
|
450
|
-
# E.g. in a controller-action of a Rails application:
|
279
|
+
Also, if not set, your model will try to use
|
280
|
+
`FmRest.default_connection_settings` instead.
|
451
281
|
|
452
|
-
|
453
|
-
BeeBase.fmrest_config_overlay = {
|
454
|
-
username: params[:username],
|
455
|
-
password: params[:password]
|
456
|
-
}
|
282
|
+
#### Connection settings overlays
|
457
283
|
|
458
|
-
|
459
|
-
|
284
|
+
There may be cases where you want to use a different set of connection settings
|
285
|
+
depending on context. For example, if you want to use username and password
|
286
|
+
provided by the user in a web application. Since `.fmrest_config`
|
287
|
+
is set at the class level, changing the username/password for the model in one
|
288
|
+
context would also change it in all other contexts, leading to security issues.
|
460
289
|
|
461
|
-
|
290
|
+
To solve this scenario, fmrest-ruby provides a way of defining thread-local and
|
291
|
+
reversible connection settings overlays through
|
292
|
+
`.fmrest_config_overlay=`.
|
462
293
|
|
463
|
-
|
294
|
+
See the [main document on connection setting overlays](docs/ConfigOverlays.md)
|
295
|
+
for details on how it works.
|
464
296
|
|
465
|
-
###
|
297
|
+
### FmRest::Layout.layout
|
466
298
|
|
467
|
-
|
468
|
-
finishes running. It wraps execution in its own fiber, so it doesn't affect the
|
469
|
-
overlay of the currently-running thread.
|
299
|
+
Use `layout` to set the layout name for your model.
|
470
300
|
|
471
301
|
```ruby
|
472
|
-
Honeybee
|
473
|
-
|
302
|
+
class Honeybee < FmRest::Layout
|
303
|
+
layout "Honeybees Web"
|
474
304
|
end
|
475
305
|
```
|
476
306
|
|
477
|
-
|
478
|
-
|
479
|
-
Use `layout` to set the `:layout` part of API URLs, e.g.:
|
307
|
+
Alternatively, if you're inheriting from `FmRest::Layout` directly you can set
|
308
|
+
the layout name in the class definition line:
|
480
309
|
|
481
310
|
```ruby
|
482
|
-
class Honeybee < FmRest::
|
483
|
-
layout "Honeybees Web" # uri path will be "layouts/Honeybees%20Web/records(/:id)"
|
484
|
-
end
|
311
|
+
class Honeybee < FmRest::Layout("Honeybees Web")
|
485
312
|
```
|
486
313
|
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
Note that you only need to set this if the name of the model and the name of
|
491
|
-
the layout differ, otherwise the default will just work.
|
314
|
+
Note that you only need to manually set the layout name if the name of the
|
315
|
+
class and the name of the layout differ, otherwise fmrest-ruby will just use
|
316
|
+
the name of the class.
|
492
317
|
|
493
|
-
###
|
318
|
+
### FmRest::Layout.request_auth_token
|
494
319
|
|
495
320
|
Requests a Data API session token using the connection settings in
|
496
321
|
`fmrest_config` and returns it if successful, otherwise returns `false`.
|
@@ -499,16 +324,16 @@ You normally don't need to use this method as fmrest-ruby will automatically
|
|
499
324
|
request and store session tokens for you (provided that `:autologin` is
|
500
325
|
`true`).
|
501
326
|
|
502
|
-
###
|
327
|
+
### FmRest::Layout.logout
|
503
328
|
|
504
|
-
Use
|
505
|
-
that uses the database session you want to log out from).
|
329
|
+
Use `.logout` to log out from the database session (you may call it on any
|
330
|
+
model that uses the database session you want to log out from).
|
506
331
|
|
507
332
|
```ruby
|
508
333
|
Honeybee.logout
|
509
334
|
```
|
510
335
|
|
511
|
-
### Mapped
|
336
|
+
### Mapped FmRest::Layout.attributes
|
512
337
|
|
513
338
|
Spyke allows you to define your model's attributes using `attributes`, however
|
514
339
|
sometimes FileMaker's field names aren't very Ruby-ORM-friendly, especially
|
@@ -517,7 +342,7 @@ fmrest-ruby extends `attributes`' functionality to allow you to map
|
|
517
342
|
Ruby-friendly attribute names to FileMaker field names. E.g.:
|
518
343
|
|
519
344
|
```ruby
|
520
|
-
class Honeybee < FmRest::
|
345
|
+
class Honeybee < FmRest::Layout
|
521
346
|
attributes first_name: "First Name", last_name: "Last Name"
|
522
347
|
end
|
523
348
|
```
|
@@ -536,62 +361,21 @@ bee.first_name = "Queen"
|
|
536
361
|
bee.attributes # => { "First Name": "Queen", "Last Name": "Buzz" }
|
537
362
|
```
|
538
363
|
|
539
|
-
###
|
364
|
+
### FmRest::Layout.has_portal
|
540
365
|
|
541
|
-
You can define portal associations on your model as such:
|
366
|
+
You can define portal associations on your model wth `has_portal`, as such:
|
542
367
|
|
543
368
|
```ruby
|
544
|
-
class Honeybee < FmRest::
|
369
|
+
class Honeybee < FmRest::Layout
|
545
370
|
has_portal :flowers
|
546
371
|
end
|
547
372
|
|
548
|
-
class Flower < FmRest::
|
373
|
+
class Flower < FmRest::Layout
|
549
374
|
attributes :color, :species
|
550
375
|
end
|
551
376
|
```
|
552
377
|
|
553
|
-
|
554
|
-
name to be both "flowers", i.e. the expected portal JSON portion should look
|
555
|
-
like this:
|
556
|
-
|
557
|
-
```json
|
558
|
-
...
|
559
|
-
"portalData": {
|
560
|
-
"flowers": [
|
561
|
-
{
|
562
|
-
"flowers::color": "red",
|
563
|
-
"flowers::species": "rose"
|
564
|
-
}
|
565
|
-
]
|
566
|
-
}
|
567
|
-
```
|
568
|
-
|
569
|
-
If you need to specify different values for them you can do so with
|
570
|
-
`portal_key` for the portal table name, and `attribute_prefix` for the portal
|
571
|
-
object name, and `class_name`, e.g.:
|
572
|
-
|
573
|
-
```ruby
|
574
|
-
class Honeybee < FmRest::Spyke::Base
|
575
|
-
has_portal :pollinated_flowers, portal_key: "Bee Flowers",
|
576
|
-
attribute_prefix: "Flower",
|
577
|
-
class_name: "Flower"
|
578
|
-
end
|
579
|
-
```
|
580
|
-
|
581
|
-
The above will use the `Flower` model class and expects the following portal JSON
|
582
|
-
portion:
|
583
|
-
|
584
|
-
```json
|
585
|
-
...
|
586
|
-
"portalData": {
|
587
|
-
"Bee Flowers": [
|
588
|
-
{
|
589
|
-
"Flower::color": "white",
|
590
|
-
"Flower::species": "rose"
|
591
|
-
}
|
592
|
-
]
|
593
|
-
}
|
594
|
-
```
|
378
|
+
See the [main document on portal associations](docs/Portals.md) for details.
|
595
379
|
|
596
380
|
### Dirty attributes
|
597
381
|
|
@@ -621,208 +405,11 @@ Guides](https://guides.rubyonrails.org/active_model_basics.html#dirty).
|
|
621
405
|
Since Spyke is API-agnostic it only provides a wide-purpose `.where` method for
|
622
406
|
passing arbitrary parameters to the REST backend. fmrest-ruby however is well
|
623
407
|
aware of its backend API, so it extends Spkye models with a bunch of useful
|
624
|
-
querying methods
|
625
|
-
|
626
|
-
#### .limit
|
627
|
-
|
628
|
-
`.limit` sets the limit for get and find request:
|
629
|
-
|
630
|
-
```ruby
|
631
|
-
Honeybee.limit(10)
|
632
|
-
```
|
633
|
-
|
634
|
-
NOTE: You can also set a default limit value for a model class, see
|
635
|
-
[other notes on querying](#other-notes-on-querying).
|
636
|
-
|
637
|
-
You can also use `.limit` to set limits on portals:
|
638
|
-
|
639
|
-
```ruby
|
640
|
-
Honeybee.limit(hives: 3, flowers: 2)
|
641
|
-
```
|
642
|
-
|
643
|
-
To remove the limit on a portal set it to `nil`:
|
644
|
-
|
645
|
-
```ruby
|
646
|
-
Honeybee.limit(flowers: nil)
|
647
|
-
```
|
648
|
-
|
649
|
-
#### .offset
|
650
|
-
|
651
|
-
`.offset` sets the offset for get and find requests:
|
652
|
-
|
653
|
-
```ruby
|
654
|
-
Honeybee.offset(10)
|
655
|
-
```
|
656
|
-
|
657
|
-
You can also use `.offset` to set offsets on portals:
|
658
|
-
|
659
|
-
```ruby
|
660
|
-
Honeybee.offset(hives: 3, flowers: 2)
|
661
|
-
```
|
662
|
-
|
663
|
-
To remove the offset on a portal set it to `nil`:
|
664
|
-
|
665
|
-
```ruby
|
666
|
-
Honeybee.offset(flowers: nil)
|
667
|
-
```
|
668
|
-
|
669
|
-
#### .sort
|
670
|
-
|
671
|
-
`.sort` (or `.order`) sets sorting options for get and find requests:
|
672
|
-
|
673
|
-
```ruby
|
674
|
-
Honeybee.sort(:name, :age)
|
675
|
-
Honeybee.order(:name, :age) # alias method
|
676
|
-
```
|
677
|
-
|
678
|
-
You can set descending sort order by appending either `!` or `__desc` to a sort
|
679
|
-
attribute (defaults to ascending order):
|
680
|
-
|
681
|
-
```ruby
|
682
|
-
Honeybee.sort(:name, :age!)
|
683
|
-
Honeybee.sort(:name, :age__desc)
|
684
|
-
```
|
685
|
-
|
686
|
-
NOTE: You can also set default sort values for a model class, see
|
687
|
-
[Other notes on querying](#other-notes-on-querying).
|
688
|
-
|
689
|
-
#### .portal
|
690
|
-
|
691
|
-
`.portal` (aliased as `.includes` and `.portals`) sets which portals to fetch
|
692
|
-
(if any) for get and find requests (this recognizes portals defined with
|
693
|
-
`has_portal`):
|
694
|
-
|
695
|
-
```ruby
|
696
|
-
Honeybee.portal(:hives) # include just the :hives portal
|
697
|
-
Honeybee.includes(:hives) # alias method
|
698
|
-
Honeybee.portals(:hives, :flowers) # alias for pluralization fundamentalists
|
699
|
-
```
|
700
|
-
|
701
|
-
Chaining calls to `.portal` will add portals to the existing included list:
|
702
|
-
|
703
|
-
```ruby
|
704
|
-
Honeybee.portal(:flowers).portal(:hives) # include both portals
|
705
|
-
```
|
706
|
-
|
707
|
-
If you want to disable portals for the scope call `.portal(false)`:
|
708
|
-
|
709
|
-
```ruby
|
710
|
-
Honeybee.portal(false) # disable portals for this scope
|
711
|
-
```
|
712
|
-
|
713
|
-
If you want to include all portals call `.portal(true)`:
|
714
|
-
|
715
|
-
```ruby
|
716
|
-
Honeybee.portal(true) # include all portals
|
717
|
-
```
|
718
|
-
|
719
|
-
For convenience you can also use `.with_all_portals` and `.without_portals`,
|
720
|
-
which behave just as calling `.portal(true)` and `portal(false)` respectively.
|
721
|
-
|
722
|
-
NOTE: By default all portals are included.
|
723
|
-
|
724
|
-
#### .query
|
725
|
-
|
726
|
-
`.query` sets query conditions for a find request (and supports attributes as
|
727
|
-
defined with `attributes`):
|
728
|
-
|
729
|
-
```ruby
|
730
|
-
Honeybee.query(name: "Hutch")
|
731
|
-
# JSON -> {"query": [{"Bee Name": "Hutch"}]}
|
732
|
-
```
|
733
|
-
|
734
|
-
Passing multiple attributes to `.query` will group them in the same JSON object:
|
735
|
-
|
736
|
-
```ruby
|
737
|
-
Honeybee.query(name: "Hutch", age: 4)
|
738
|
-
# JSON -> {"query": [{"Bee Name": "Hutch", "Bee Age": 4}]}
|
739
|
-
```
|
740
|
-
|
741
|
-
Calling `.query` multiple times or passing it multiple hashes creates separate
|
742
|
-
JSON objects (so you can define OR queries):
|
743
|
-
|
744
|
-
```ruby
|
745
|
-
Honeybee.query(name: "Hutch").query(name: "Maya")
|
746
|
-
Honeybee.query({ name: "Hutch" }, { name: "Maya" })
|
747
|
-
# JSON -> {"query": [{"Bee Name": "Hutch"}, {"Bee Name": "Maya"}]}
|
748
|
-
```
|
749
|
-
|
750
|
-
#### .omit
|
751
|
-
|
752
|
-
`.omit` works like `.query` but excludes matches:
|
753
|
-
|
754
|
-
```ruby
|
755
|
-
Honeybee.omit(name: "Hutch")
|
756
|
-
# JSON -> {"query": [{"Bee Name": "Hutch", "omit": "true"}]}
|
757
|
-
```
|
758
|
-
|
759
|
-
You can get the same effect by passing `omit: true` to `.query`:
|
760
|
-
|
761
|
-
```ruby
|
762
|
-
Honeybee.query(name: "Hutch", omit: true)
|
763
|
-
# JSON -> {"query": [{"Bee Name": "Hutch", "omit": "true"}]}
|
764
|
-
```
|
765
|
-
|
766
|
-
#### .script
|
767
|
-
|
768
|
-
`.script` enables the execution of scripts during query requests.
|
769
|
-
|
770
|
-
```ruby
|
771
|
-
Honeybee.script("My script").find_some # Fetch records and execute a script
|
772
|
-
```
|
773
|
-
|
774
|
-
See section on [script execution](#script-execution) below for more info.
|
775
|
-
|
776
|
-
#### Other notes on querying
|
777
|
-
|
778
|
-
You can chain all query methods together:
|
779
|
-
|
780
|
-
```ruby
|
781
|
-
Honeybee.limit(10).offset(20).sort(:name, :age!).portal(:hives).query(name: "Hutch")
|
782
|
-
```
|
783
|
-
|
784
|
-
You can also set default values for limit and sort on the class:
|
785
|
-
|
786
|
-
```ruby
|
787
|
-
class Honeybee < FmRest::Spyke::Base
|
788
|
-
self.default_limit = 1000
|
789
|
-
self.default_sort = [:name, :age!]
|
790
|
-
end
|
791
|
-
```
|
792
|
-
|
793
|
-
Calling any `Enumerable` method on the resulting scope object will trigger a
|
794
|
-
server request, so you can treat the scope as a collection:
|
795
|
-
|
796
|
-
```ruby
|
797
|
-
Honeybee.limit(10).sort(:name).each { |bee| ... }
|
798
|
-
```
|
799
|
-
|
800
|
-
If you want to explicitly run the request instead you can use `.find_some` on
|
801
|
-
the scope object:
|
802
|
-
|
803
|
-
```ruby
|
804
|
-
Honeybee.limit(10).sort(:name).find_some # => [<Honeybee...>, ...]
|
805
|
-
```
|
806
|
-
|
807
|
-
If you want just a single result you can use `.first` instead (this will
|
808
|
-
force `.limit(1)`):
|
809
|
-
|
810
|
-
```ruby
|
811
|
-
Honeybee.query(name: "Hutch").first # => <Honeybee...>
|
812
|
-
```
|
813
|
-
|
814
|
-
If you know the id of the record you should use `.find(id)` instead of
|
815
|
-
`.query(id: id).first` (so that the sent request is
|
816
|
-
`GET ../:layout/records/:id` instead of `POST ../:layout/_find`).
|
817
|
-
|
818
|
-
```ruby
|
819
|
-
Honeybee.find(89) # => <Honeybee...>
|
820
|
-
```
|
821
|
-
|
822
|
-
Note also that if you use `.find(id)` your `.query()` parameters (as well as
|
823
|
-
limit, offset and sort parameters) will be discarded as they're not supported
|
824
|
-
by the single record endpoint.
|
408
|
+
querying methods: `.query`, `.match`, `.omit`, `.limit`, `.offset`, `.sort`,
|
409
|
+
`.portal`, `.script`, etc.
|
825
410
|
|
411
|
+
See the [main document on querying](docs/Querying.md) for detailed information
|
412
|
+
on the query API methods.
|
826
413
|
|
827
414
|
### Finding records in batches
|
828
415
|
|
@@ -830,249 +417,63 @@ Sometimes you want to iterate over a very large number of records to do some
|
|
830
417
|
processing, but requesting them all at once would result in one huge request to
|
831
418
|
the Data API, and loading too many records in memory all at once.
|
832
419
|
|
833
|
-
To mitigate this problem you can use `.find_in_batches` and `.find_each`.
|
834
|
-
you've used ActiveRecord you're probably familiar with how they operate:
|
835
|
-
|
836
|
-
```ruby
|
837
|
-
# Find records in batches of 100 each
|
838
|
-
Honeybee.query(hive: "Queensville").find_in_batches(batch_size: 100) do |batch|
|
839
|
-
dispatch_bees(batch)
|
840
|
-
end
|
841
|
-
|
842
|
-
# Iterate over all records using batches
|
843
|
-
Honeybee.query(hive: "Queensville").find_each(batch_size: 100) do |bee|
|
844
|
-
bee.dispatch
|
845
|
-
end
|
846
|
-
```
|
847
|
-
|
848
|
-
`.find_in_batches` yields collections of records (batches), while `.find_each`
|
849
|
-
yields individual records, but using batches behind the scenes.
|
850
|
-
|
851
|
-
Both methods accept a block-less form in which case they return an
|
852
|
-
`Enumerator`:
|
853
|
-
|
854
|
-
```ruby
|
855
|
-
batch_enum = Honeybee.find_in_batches
|
856
|
-
|
857
|
-
batch = batch_enum.next # => Spyke::Collection
|
858
|
-
|
859
|
-
batch_enum.each do |batch|
|
860
|
-
process_batch(batch)
|
861
|
-
end
|
862
|
-
|
863
|
-
record_enum = Honeybee.find_each
|
864
|
-
|
865
|
-
record_enum.next # => Honeybee
|
866
|
-
```
|
867
|
-
|
868
|
-
NOTE: By its nature, batch processing is subject to race conditions if other
|
869
|
-
processes are modifying the database.
|
420
|
+
To mitigate this problem you can use `.find_in_batches` and `.find_each`.
|
870
421
|
|
422
|
+
See the [main document on finding in batches](docs/FindInBatches.md) for
|
423
|
+
detailed information on how those work.
|
871
424
|
|
872
425
|
### Container fields
|
873
426
|
|
874
427
|
You can define container fields on your model class with `container`:
|
875
428
|
|
876
429
|
```ruby
|
877
|
-
class Honeybee < FmRest::
|
430
|
+
class Honeybee < FmRest::Layout
|
878
431
|
container :photo, field_name: "Beehive Photo ID"
|
879
432
|
end
|
880
433
|
```
|
881
434
|
|
882
|
-
|
883
|
-
|
884
|
-
example).
|
885
|
-
|
886
|
-
(Note that you don't need to define container fields with `attributes` in
|
887
|
-
addition to the `container` definition.)
|
888
|
-
|
889
|
-
This will provide you with the following instance methods:
|
890
|
-
|
891
|
-
```ruby
|
892
|
-
bee = Honeybee.new
|
893
|
-
|
894
|
-
bee.photo.url # The URL of the container file on the FileMaker server
|
895
|
-
|
896
|
-
bee.photo.download # Download the contents of the container as an IO object
|
897
|
-
|
898
|
-
bee.photo.upload(filename_or_io) # Upload a file to the container
|
899
|
-
```
|
900
|
-
|
901
|
-
`upload` also accepts an options hash with the following options:
|
902
|
-
|
903
|
-
* `:repetition` - Sets the field repetition
|
904
|
-
* `:filename` - The filename to use when uploading (defaults to
|
905
|
-
`filename_or_io.original_filename` if available)
|
906
|
-
* `:content_type` - The MIME content type to use (defaults to
|
907
|
-
`application/octet-stream`)
|
908
|
-
|
435
|
+
See the [main document on container fields](docs/ContainerFields.md) for
|
436
|
+
details on how to use it.
|
909
437
|
|
910
438
|
### Script execution
|
911
439
|
|
912
|
-
The Data API allows running scripts as part of many types of requests
|
913
|
-
|
914
|
-
#### Model.execute_script
|
915
|
-
As of FM18 you can execute scripts directly. To do that for a specific model
|
916
|
-
use `Model.execute_script`:
|
917
|
-
|
918
|
-
```ruby
|
919
|
-
result = Honeybee.execute_script("My Script", param: "optional parameter")
|
920
|
-
```
|
921
|
-
|
922
|
-
This will return a `Spyke::Result` object containing among other things the
|
923
|
-
result of the script execution:
|
924
|
-
|
925
|
-
```ruby
|
926
|
-
result.metadata[:script][:after]
|
927
|
-
# => { result: "oh hi", error: "0" }
|
928
|
-
```
|
929
|
-
|
930
|
-
#### Script options object format
|
931
|
-
|
932
|
-
All other script-capable requests take one or more of three possible script
|
933
|
-
execution options: `script.prerequest`, `script.presort` and plain `script`
|
934
|
-
(which fmrest-ruby dubs `after` for convenience).
|
935
|
-
|
936
|
-
Because of that fmrest-ruby uses a common object format for specifying script options
|
937
|
-
across multiple methods. That object format is as follows:
|
938
|
-
|
939
|
-
```ruby
|
940
|
-
# Just a string means to execute that `after' script without a parameter
|
941
|
-
"My Script"
|
942
|
-
|
943
|
-
# A 2-elemnent array means [script name, script parameter]
|
944
|
-
["My Script", "parameter"]
|
945
|
-
|
946
|
-
# A hash with keys :prerequest, :presort and/or :after sets those scripts for
|
947
|
-
{
|
948
|
-
prerequest: "My Prerequest Script",
|
949
|
-
presort: "My Presort Script",
|
950
|
-
after: "My Script"
|
951
|
-
}
|
952
|
-
|
953
|
-
# Using 2-element arrays as objects in the hash allows specifying parameters
|
954
|
-
{
|
955
|
-
prerequest: ["My Prerequest Script", "parameter"],
|
956
|
-
presort: ["My Presort Script", "parameter"],
|
957
|
-
after: ["My Script", "parameter"]
|
958
|
-
}
|
959
|
-
```
|
960
|
-
|
961
|
-
#### Script execution on record save, destroy and reload
|
962
|
-
|
963
|
-
A record instance's `.save` and `.destroy` methods both accept a `script:`
|
964
|
-
option to which you can pass a script options object with
|
965
|
-
[the above format](#script-options-object-format):
|
966
|
-
|
967
|
-
```ruby
|
968
|
-
# Save the record and execute an `after' script called "My Script"
|
969
|
-
bee.save(script: "My Script")
|
970
|
-
|
971
|
-
# Same as above but with an added parameter
|
972
|
-
bee.save(script: ["My Script", "parameter"])
|
973
|
-
|
974
|
-
# Save the record and execute a presort script and an `after' script
|
975
|
-
bee.save(script: { presort: "My Presort Script", after: "My Script" })
|
976
|
-
|
977
|
-
# Destroy the record and execute a prerequest script with a parameter
|
978
|
-
bee.destroy(script: { prerequest: ["My Prerequest Script", "parameter"] })
|
979
|
-
|
980
|
-
# Reload the record and execute a prerequest script with a parameter
|
981
|
-
bee.reload(script: { prerequest: ["My Prerequest Script", "parameter"] })
|
982
|
-
```
|
983
|
-
|
984
|
-
#### Retrieving script execution results
|
985
|
-
|
986
|
-
Every time a request is ran on a model or record instance of a model, a
|
987
|
-
thread-local `Model.last_request_metadata` attribute is set on that model,
|
988
|
-
which is a hash containing the results of script executions, if any were
|
989
|
-
performed, among other metadata.
|
990
|
-
|
991
|
-
The results for `:after`, `:prerequest` and `:presort` scripts are stored
|
992
|
-
separately, under their matching key.
|
993
|
-
|
994
|
-
```ruby
|
995
|
-
bee.save(script: { presort: "My Presort Script", after: "My Script" })
|
996
|
-
|
997
|
-
Honeybee.last_request_metadata.script
|
998
|
-
# => { after: { result: "oh hi", error: "0" }, presort: { result: "lo", error: "0" } }
|
999
|
-
```
|
1000
|
-
|
1001
|
-
#### Executing scripts through query requests
|
1002
|
-
|
1003
|
-
As mentioned under the [Query API](#query-api) section, you can use the
|
1004
|
-
`.script` query method to specify that you want scripts executed when a query
|
1005
|
-
is performed on that scope.
|
1006
|
-
|
1007
|
-
`.script` takes the same options object specified [above](#script-options-object-format):
|
1008
|
-
|
1009
|
-
```ruby
|
1010
|
-
# Find one Honeybee record executing a presort and after script
|
1011
|
-
Honeybee.script(presort: ["My Presort Script", "parameter"], after: "My Script").first
|
1012
|
-
```
|
1013
|
-
|
1014
|
-
The model class' `.last_request_metadata` will be set in case you need to get the result.
|
1015
|
-
|
1016
|
-
In the case of retrieving multiple results (i.e. via `.find_some`) the
|
1017
|
-
resulting collection will have a `.metadata` attribute method containing the
|
1018
|
-
same metadata hash with script execution results. Note that this does not apply
|
1019
|
-
to retrieving single records, in that case you'll have to use
|
1020
|
-
`.last_request_metadata`.
|
440
|
+
The FM Data API allows running scripts as part of many types of requests, and
|
441
|
+
`fmrest-spyke` provides mechanisms for all of them.
|
1021
442
|
|
443
|
+
See the [main document on script execution](docs/ScriptExecution.md) for
|
444
|
+
details.
|
1022
445
|
|
1023
446
|
### Setting global field values
|
1024
447
|
|
1025
|
-
You can call `.set_globals` on any `FmRest::
|
448
|
+
You can call `.set_globals` on any `FmRest::Layout` model to set global
|
1026
449
|
field values on the database that model is configured for.
|
1027
450
|
|
1028
|
-
|
1029
|
-
|
1030
|
-
table name and the inner keys being the field names:
|
1031
|
-
|
1032
|
-
```ruby
|
1033
|
-
Honeybee.set_globals(
|
1034
|
-
"beeTable::myVar" => "value",
|
1035
|
-
"beeTable::myOtherVar" => "also a value"
|
1036
|
-
)
|
1037
|
-
|
1038
|
-
# Equivalent to the above example
|
1039
|
-
Honeybee.set_globals(beeTable: { myVar: "value", myOtherVar: "also a value" })
|
1040
|
-
|
1041
|
-
# Combined
|
1042
|
-
Honeybee.set_globals(
|
1043
|
-
"beeTable::myVar" => "value",
|
1044
|
-
beeTable: { myOtherVar: "also a value" }
|
1045
|
-
)
|
1046
|
-
```
|
1047
|
-
|
451
|
+
See the [main document on setting global field values](docs/GlobalFields.md)
|
452
|
+
for details.
|
1048
453
|
|
1049
454
|
## Logging
|
1050
455
|
|
1051
|
-
If using fmrest-
|
1052
|
-
|
456
|
+
If using `fmrest-spyke` with Rails then pretty log output will be set up for
|
457
|
+
you automatically by Spyke (see [their
|
1053
458
|
README](https://github.com/balvig/spyke#log-output)).
|
1054
459
|
|
1055
|
-
You can also enable simple STDOUT logging (useful for
|
1056
|
-
`log: true` in the options hash for either
|
460
|
+
You can also enable simple Faraday STDOUT logging of raw requests (useful for
|
461
|
+
debugging) by passing `log: true` in the options hash for either
|
1057
462
|
`FmRest.default_connection_settings=` or your models' `fmrest_config=`, e.g.:
|
1058
463
|
|
1059
464
|
```ruby
|
1060
465
|
FmRest.default_connection_settings = {
|
1061
|
-
host:
|
1062
|
-
|
1063
|
-
|
1064
|
-
password: "abc123",
|
1065
|
-
log: true
|
466
|
+
host: "…",
|
467
|
+
…
|
468
|
+
log: true
|
1066
469
|
}
|
1067
470
|
|
1068
471
|
# Or in your model
|
1069
|
-
class LoggyBee < FmRest::
|
472
|
+
class LoggyBee < FmRest::Layout
|
1070
473
|
self.fmrest_config = {
|
1071
|
-
host:
|
1072
|
-
|
1073
|
-
|
1074
|
-
password: "...",
|
1075
|
-
log: true
|
474
|
+
host: "…",
|
475
|
+
…
|
476
|
+
log: true
|
1076
477
|
}
|
1077
478
|
end
|
1078
479
|
```
|
@@ -1082,7 +483,7 @@ If you need to set up more complex logging for your models can use the
|
|
1082
483
|
Faraday connection, e.g.:
|
1083
484
|
|
1084
485
|
```ruby
|
1085
|
-
class LoggyBee < FmRest::
|
486
|
+
class LoggyBee < FmRest::Layout
|
1086
487
|
faraday do |conn|
|
1087
488
|
conn.response :logger, MyApp.logger, bodies: true
|
1088
489
|
end
|
@@ -1093,33 +494,43 @@ end
|
|
1093
494
|
|
1094
495
|
FM Data API reference: https://fmhelp.filemaker.com/docs/18/en/dataapi/
|
1095
496
|
|
1096
|
-
| FM 18 Data API feature | Supported by basic connection | Supported by FmRest::
|
1097
|
-
|
1098
|
-
| Log in using HTTP Basic Auth | Yes | Yes
|
1099
|
-
| Log in using OAuth | No | No
|
1100
|
-
| Log in to an external data source | No | No
|
1101
|
-
| Log in using a FileMaker ID account | No | No
|
1102
|
-
| Log out | Yes | Yes
|
1103
|
-
| Get product information | Manual* | No
|
1104
|
-
| Get database names | Manual* | No
|
1105
|
-
| Get script names | Manual* | No
|
1106
|
-
| Get layout names | Manual* | No
|
1107
|
-
| Get layout metadata | Manual* | No
|
1108
|
-
| Create a record | Manual* | Yes
|
1109
|
-
| Edit a record | Manual* | Yes
|
1110
|
-
| Duplicate a record | Manual* | No
|
1111
|
-
| Delete a record | Manual* | Yes
|
1112
|
-
|
|
1113
|
-
| Get a
|
1114
|
-
| Get
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
| Run a script
|
497
|
+
| FM 18 Data API feature | Supported by basic connection | Supported by FmRest::Layout |
|
498
|
+
|-------------------------------------|-------------------------------|-----------------------------|
|
499
|
+
| Log in using HTTP Basic Auth | Yes | Yes |
|
500
|
+
| Log in using OAuth | No | No |
|
501
|
+
| Log in to an external data source | No | No |
|
502
|
+
| Log in using a FileMaker ID account | No | No |
|
503
|
+
| Log out | Yes | Yes |
|
504
|
+
| Get product information | Manual* | No |
|
505
|
+
| Get database names | Manual* | No |
|
506
|
+
| Get script names | Manual* | No |
|
507
|
+
| Get layout names | Manual* | No |
|
508
|
+
| Get layout metadata | Manual* | No |
|
509
|
+
| Create a record | Manual* | Yes |
|
510
|
+
| Edit a record | Manual* | Yes |
|
511
|
+
| Duplicate a record | Manual* | No |
|
512
|
+
| Delete a record | Manual* | Yes |
|
513
|
+
| Edit portal records | Manual* | Yes |
|
514
|
+
| Get a single record | Manual* | Yes |
|
515
|
+
| Get a range of records | Manual* | Yes |
|
516
|
+
| Get container data | Manual* | Yes |
|
517
|
+
| Upload container data | Manual* | Yes |
|
518
|
+
| Perform a find request | Manual* | Yes |
|
519
|
+
| Set global field values | Manual* | Yes |
|
520
|
+
| Run a script | Manual* | Yes |
|
521
|
+
| Run a script with another request | Manual* | Yes |
|
1120
522
|
|
1121
523
|
\* You can manually supply the URL and JSON to a `FmRest` connection.
|
1122
524
|
|
525
|
+
## Supported Ruby versions
|
526
|
+
|
527
|
+
fmrest-ruby aims to support and is [tested against](https://github.com/beezwax/fmrest-ruby/actions?query=workflow%3ACI)
|
528
|
+
the following Ruby implementations:
|
529
|
+
|
530
|
+
* Ruby 2.5
|
531
|
+
* Ruby 2.6
|
532
|
+
* Ruby 2.7
|
533
|
+
* Ruby 3.0
|
1123
534
|
|
1124
535
|
## Gem development
|
1125
536
|
|
@@ -1128,20 +539,11 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
1128
539
|
prompt that will allow you to experiment (it will auto-load all fixtures in
|
1129
540
|
spec/fixtures).
|
1130
541
|
|
1131
|
-
To install
|
1132
|
-
release a new version, update the version
|
1133
|
-
|
1134
|
-
git
|
1135
|
-
[rubygems.org](https://rubygems.org).
|
1136
|
-
|
1137
|
-
|
1138
|
-
## Contributing
|
1139
|
-
|
1140
|
-
Bug reports and pull requests are welcome. This project is intended to be a
|
1141
|
-
safe, welcoming space for collaboration, and contributors are expected to
|
1142
|
-
adhere to the [Contributor Covenant](http://contributor-covenant.org) code of
|
1143
|
-
conduct.
|
1144
|
-
|
542
|
+
To install all gems onto your local machine, run
|
543
|
+
`bundle exec rake all:install`. To release a new version, update the version
|
544
|
+
number in `lib/fmrest/version.rb`, and then run `bundle exec rake all:release`,
|
545
|
+
which will create a git tag for the version, push git commits and tags, and
|
546
|
+
push the `.gem` files to [rubygems.org](https://rubygems.org).
|
1145
547
|
|
1146
548
|
## License
|
1147
549
|
|
@@ -1149,9 +551,8 @@ The gem is available as open source under the terms of the
|
|
1149
551
|
[MIT License](https://opensource.org/licenses/MIT).
|
1150
552
|
See [LICENSE.txt](LICENSE.txt).
|
1151
553
|
|
1152
|
-
|
1153
554
|
## Disclaimer
|
1154
555
|
|
1155
|
-
This project is not sponsored by or otherwise affiliated with
|
1156
|
-
an Apple subsidiary. FileMaker is a trademark of
|
1157
|
-
the U.S. and other countries.
|
556
|
+
This project is not sponsored by or otherwise affiliated with Claris
|
557
|
+
International Inc., an Apple Inc. subsidiary. FileMaker is a trademark of
|
558
|
+
Claris International Inc., registered in the U.S. and other countries.
|