fmrest 0.9.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -0
- data/README.md +94 -17
- data/UPGRADING +15 -0
- data/fmrest.gemspec +16 -5
- data/lib/fmrest.rb +10 -3
- data/lib/fmrest/connection_settings.rb +124 -0
- data/lib/fmrest/errors.rb +2 -0
- data/lib/fmrest/spyke/base.rb +0 -12
- data/lib/fmrest/spyke/container_field.rb +2 -2
- data/lib/fmrest/spyke/model.rb +3 -6
- data/lib/fmrest/spyke/model/associations.rb +15 -11
- data/lib/fmrest/spyke/model/attributes.rb +21 -29
- data/lib/fmrest/spyke/model/auth.rb +8 -0
- data/lib/fmrest/spyke/model/connection.rb +122 -24
- data/lib/fmrest/spyke/model/container_fields.rb +15 -0
- data/lib/fmrest/spyke/model/http.rb +42 -2
- data/lib/fmrest/spyke/model/orm.rb +61 -17
- data/lib/fmrest/spyke/model/record_id.rb +78 -0
- data/lib/fmrest/spyke/model/serialization.rb +26 -7
- data/lib/fmrest/spyke/model/uri.rb +3 -4
- data/lib/fmrest/spyke/spyke_formatter.rb +5 -5
- data/lib/fmrest/string_date.rb +46 -7
- data/lib/fmrest/token_store.rb +6 -0
- data/lib/fmrest/token_store/base.rb +3 -3
- data/lib/fmrest/token_store/null.rb +20 -0
- data/lib/fmrest/v1.rb +8 -4
- data/lib/fmrest/v1/auth.rb +30 -0
- data/lib/fmrest/v1/connection.rb +51 -25
- data/lib/fmrest/v1/dates.rb +81 -0
- data/lib/fmrest/v1/raise_errors.rb +3 -3
- data/lib/fmrest/v1/token_session.rb +41 -50
- data/lib/fmrest/v1/type_coercer.rb +111 -36
- data/lib/fmrest/v1/utils.rb +0 -17
- data/lib/fmrest/version.rb +1 -1
- metadata +41 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 725c8356fc5933f0484f7d04a4458ecc85c638838c8d9869d039a6e09b0b15f6
|
4
|
+
data.tar.gz: 2a8047594d93d13b0f6d9981159c16b1afdcaa19713c054391fc08ca1fa68633
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a41a3e747bf6ccfe0de7e5541b0db35d30d9723b7a0eeb895fbe9e21f4c2c7e6f08ccd3012c1e6f4edaa5403966e2a7563b74490327c721990fbdd4ab79608a
|
7
|
+
data.tar.gz: 16fb2868c102905a58632b1183581dcf4617d4e6c9f7529abad12916b78b3c692d21a469eaf7b23101d53ff55e8c9ccbfc1167556cc09855516647ce00565ea8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,40 @@
|
|
1
1
|
## Changelog
|
2
2
|
|
3
|
+
### 0.12.0
|
4
|
+
|
5
|
+
* Rename `FmRest::Spyke::Base#id=` to `FmRest::Spyke::Base#__record_id=` to
|
6
|
+
prevent clobbering of FileMaker layout-defined fields
|
7
|
+
* Better yard documentation
|
8
|
+
|
9
|
+
### 0.11.1
|
10
|
+
|
11
|
+
* Fix a couple crashes due to missing constants
|
12
|
+
|
13
|
+
### 0.11.0
|
14
|
+
|
15
|
+
* Added custom class for connection settings, providing indifferent access
|
16
|
+
(i.e. keys can be strings or symbols), and centralized default values and
|
17
|
+
validations
|
18
|
+
* Added `:autologin`, `:token` and `:token_store` connection settings
|
19
|
+
* Added `FmRest::Base.fmrest_config_overlay=` and related methods
|
20
|
+
* Added `FmRest::V1.request_auth_token` and
|
21
|
+
`FmRest::Spyke::Base.request_auth_token` (as well as `!`-suffixed versions
|
22
|
+
which raise exceptions on failure)
|
23
|
+
|
24
|
+
### 0.10.1
|
25
|
+
|
26
|
+
* Fix `URI.escape` obsolete warning messages in Ruby 2.7 by replacing it with
|
27
|
+
`URI.encode_www_form_component`
|
28
|
+
([PR#40](https://github.com/beezwax/fmrest-ruby/pull/40))
|
29
|
+
|
30
|
+
### 0.10.0
|
31
|
+
|
32
|
+
* Added `FmRest::StringDateAwareness` module to correct some issues when using
|
33
|
+
`FmRest::StringDate`
|
34
|
+
* Added basic timezones support
|
35
|
+
* Deprecated `class < FmRest::Spyke::Base(config_hash)` syntax in favor of
|
36
|
+
using `self.fmrest_config=`
|
37
|
+
|
3
38
|
### 0.9.0
|
4
39
|
|
5
40
|
* Added `FmRest::Spyke::Base.set_globals`
|
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# fmrest-ruby
|
2
2
|
|
3
|
-
|
3
|
+
[](https://rubygems.org/gems/fmrest)
|
4
|
+

|
4
5
|
|
5
6
|
A Ruby client for
|
6
|
-
[FileMaker 18's Data API](https://
|
7
|
+
[FileMaker 18 and 19's Data API](https://help.claris.com/en/data-api-guide)
|
7
8
|
using
|
8
9
|
[Faraday](https://github.com/lostisland/faraday) and with optional
|
9
10
|
[Spyke](https://github.com/balvig/spyke) support (ActiveRecord-ish models).
|
@@ -120,6 +121,9 @@ Option | Description | Format
|
|
120
121
|
`:date_format` | Date parsing format | String (FM date format) | `"MM/dd/yyyy"`
|
121
122
|
`:timestamp_format` | Timestmap parsing format | String (FM date format) | `"MM/dd/yyyy HH:mm:ss"`
|
122
123
|
`:time_format` | Time parsing format | String (FM date format) | `"HH:mm:ss"`
|
124
|
+
`:timezone` | The timezone for the FM server | `:local` \| `:utc` \| `nil` | `nil`
|
125
|
+
`:autologin` | Whether to automatically start Data API sessions | Boolean | `true`
|
126
|
+
`:token` | Used to manually provide a session token (e.g. if `:autologin` is `false`) | String | None
|
123
127
|
|
124
128
|
### Default connection settings
|
125
129
|
|
@@ -270,9 +274,9 @@ a DSL in model classes).
|
|
270
274
|
### Hybrid string/date objects
|
271
275
|
|
272
276
|
`FmRest::StringDate` and `FmRest::StringDateTime` are special classes that
|
273
|
-
inherit from `String`, but internally parse and store a `Date
|
274
|
-
|
275
|
-
|
277
|
+
inherit from `String`, but internally parse and store a `Date` or `DateTime`,
|
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.
|
276
280
|
|
277
281
|
You can use these when you want fmrest-ruby to provide you with date objects,
|
278
282
|
but you don't want to worry about date coercion of false positives (i.e. a
|
@@ -280,7 +284,29 @@ string field that gets converted to `Date` because it just so matched the given
|
|
280
284
|
date format).
|
281
285
|
|
282
286
|
Be warned however that these classes come with a fair share of known gotchas
|
283
|
-
(see GitHub wiki for more info).
|
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
|
284
310
|
|
285
311
|
|
286
312
|
## Spyke support (ActiveRecord-like ORM)
|
@@ -322,17 +348,6 @@ class Honeybee < FmRest::Spyke::Base
|
|
322
348
|
end
|
323
349
|
```
|
324
350
|
|
325
|
-
In this case you can pass the [`fmrest_config`](#modelfmrest_config) hash as an
|
326
|
-
argument to `Base()`:
|
327
|
-
|
328
|
-
```ruby
|
329
|
-
class Honeybee < FmRest::Spyke::Base(host: "...", database: "...", username: "...", password: "...")
|
330
|
-
end
|
331
|
-
|
332
|
-
Honeybee.fmrest_config
|
333
|
-
# => { host: "...", database: "...", username: "...", password: "..." }
|
334
|
-
```
|
335
|
-
|
336
351
|
All of Spyke's basic ORM operations work:
|
337
352
|
|
338
353
|
```ruby
|
@@ -406,6 +421,59 @@ class Honeybee < BeeBase
|
|
406
421
|
end
|
407
422
|
```
|
408
423
|
|
424
|
+
### Model.fmrest_config_overlay=
|
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:
|
451
|
+
|
452
|
+
# User-provided credentials
|
453
|
+
BeeBase.fmrest_config_overlay = {
|
454
|
+
username: params[:username],
|
455
|
+
password: params[:password]
|
456
|
+
}
|
457
|
+
|
458
|
+
# Perform some Data API requests ...
|
459
|
+
```
|
460
|
+
|
461
|
+
### Model.clear_fmrest_config_overlay
|
462
|
+
|
463
|
+
Clears the thread-local settings provided to `fmrest_config_overaly=`.
|
464
|
+
|
465
|
+
### Model.with_overlay
|
466
|
+
|
467
|
+
Runs a block with the given settings overlay, resetting them after the block
|
468
|
+
finishes running. It wraps execution in its own fiber, so it doesn't affect the
|
469
|
+
overlay of the currently-running thread.
|
470
|
+
|
471
|
+
```ruby
|
472
|
+
Honeybee.with_overlay(username: "...", password: "...") do
|
473
|
+
Honeybee.query(...)
|
474
|
+
end
|
475
|
+
```
|
476
|
+
|
409
477
|
### Model.layout
|
410
478
|
|
411
479
|
Use `layout` to set the `:layout` part of API URLs, e.g.:
|
@@ -422,6 +490,15 @@ Data API models.
|
|
422
490
|
Note that you only need to set this if the name of the model and the name of
|
423
491
|
the layout differ, otherwise the default will just work.
|
424
492
|
|
493
|
+
### Model.request_auth_token
|
494
|
+
|
495
|
+
Requests a Data API session token using the connection settings in
|
496
|
+
`fmrest_config` and returns it if successful, otherwise returns `false`.
|
497
|
+
|
498
|
+
You normally don't need to use this method as fmrest-ruby will automatically
|
499
|
+
request and store session tokens for you (provided that `:autologin` is
|
500
|
+
`true`).
|
501
|
+
|
425
502
|
### Model.logout
|
426
503
|
|
427
504
|
Use `logout` to log out from the database session (you may call it on any model
|
data/UPGRADING
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
=======================================
|
2
|
+
Notes on upgrading from fmrest < 0.12
|
3
|
+
=======================================
|
4
|
+
|
5
|
+
There's a small breaking change in fmrest 0.12 that will most likely not affect
|
6
|
+
you, but you may want to be aware of:
|
7
|
+
|
8
|
+
Previous to this version the record ID on an FmRest::Spyke::Base instance could
|
9
|
+
be set with `id=`. This caused problems in cases where a FileMaker layout had a
|
10
|
+
field named `id`, so `id=` got renamed to `__record_id=`. Setting the record ID
|
11
|
+
by hand however isn't something useful or that should be done at all, so it's
|
12
|
+
very unlikely that this change will affect your existing code at all.
|
13
|
+
|
14
|
+
Thanks for using fmrest-ruby!
|
15
|
+
|
data/fmrest.gemspec
CHANGED
@@ -14,23 +14,34 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
-
f.match(%r{^(test|spec|features|bin)/})
|
17
|
+
f.match(%r{^(\.github|test|spec|features|bin)/})
|
18
18
|
end
|
19
19
|
spec.bindir = "exe"
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
+
if File.exist?('UPGRADING')
|
24
|
+
spec.post_install_message = File.read("UPGRADING")
|
25
|
+
end
|
26
|
+
|
23
27
|
spec.add_dependency 'faraday', '>= 0.9.0', '< 2.0'
|
24
28
|
spec.add_dependency 'faraday_middleware', '>= 0.9.1', '< 2.0'
|
25
29
|
|
26
|
-
spec.add_development_dependency "bundler"
|
27
|
-
spec.add_development_dependency "rake"
|
30
|
+
spec.add_development_dependency "bundler"
|
31
|
+
spec.add_development_dependency "rake"
|
28
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
-
spec.add_development_dependency "spyke"
|
33
|
+
spec.add_development_dependency "spyke", ">= 5.3.3"
|
30
34
|
spec.add_development_dependency "webmock"
|
31
35
|
spec.add_development_dependency "pry-byebug"
|
32
36
|
spec.add_development_dependency "activerecord", ENV["ACTIVE_RECORD_VERSION"]
|
33
|
-
|
37
|
+
|
38
|
+
sqlite3_version = if (4.2..5.2).include?(ENV["ACTIVE_RECORD_VERSION"].to_s.gsub(/[^\d.]/, "").to_f)
|
39
|
+
"~> 1.3.0"
|
40
|
+
else
|
41
|
+
"~> 1.4.0"
|
42
|
+
end
|
43
|
+
|
44
|
+
spec.add_development_dependency "sqlite3", sqlite3_version
|
34
45
|
spec.add_development_dependency "mock_redis"
|
35
46
|
spec.add_development_dependency "moneta"
|
36
47
|
spec.add_development_dependency "yard"
|
data/lib/fmrest.rb
CHANGED
@@ -4,16 +4,23 @@ require "faraday"
|
|
4
4
|
require "faraday_middleware"
|
5
5
|
|
6
6
|
require "fmrest/version"
|
7
|
-
require "fmrest/
|
7
|
+
require "fmrest/connection_settings"
|
8
|
+
|
9
|
+
require "fmrest/errors"
|
8
10
|
|
9
11
|
module FmRest
|
12
|
+
autoload :V1, "fmrest/v1"
|
13
|
+
autoload :TokenStore, "fmrest/token_store"
|
14
|
+
|
10
15
|
class << self
|
11
16
|
attr_accessor :token_store
|
12
17
|
|
13
|
-
|
18
|
+
def default_connection_settings=(settings)
|
19
|
+
@default_connection_settings = ConnectionSettings.wrap(settings, skip_validation: true)
|
20
|
+
end
|
14
21
|
|
15
22
|
def default_connection_settings
|
16
|
-
@default_connection_settings || {}
|
23
|
+
@default_connection_settings || ConnectionSettings.new({}, skip_validation: true)
|
17
24
|
end
|
18
25
|
|
19
26
|
def config=(connection_hash)
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FmRest
|
4
|
+
# Wrapper class for connection settings hash, with a number of purposes:
|
5
|
+
#
|
6
|
+
# * Provide indifferent access (base hash can have either string or symbol
|
7
|
+
# keys)
|
8
|
+
# * Method access
|
9
|
+
# * Default values
|
10
|
+
# * Basic validation
|
11
|
+
# * Normalization (e.g. aliased settings)
|
12
|
+
# * Useful error messages
|
13
|
+
class ConnectionSettings
|
14
|
+
class MissingSetting < ArgumentError; end
|
15
|
+
|
16
|
+
PROPERTIES = %i(
|
17
|
+
host
|
18
|
+
database
|
19
|
+
username
|
20
|
+
password
|
21
|
+
token
|
22
|
+
token_store
|
23
|
+
autologin
|
24
|
+
ssl
|
25
|
+
proxy
|
26
|
+
log
|
27
|
+
coerce_dates
|
28
|
+
date_format
|
29
|
+
timestamp_format
|
30
|
+
time_format
|
31
|
+
timezone
|
32
|
+
).freeze
|
33
|
+
|
34
|
+
# NOTE: password intentionally left non-required since it's only really
|
35
|
+
# needed when no token exists, and should only be required when logging in
|
36
|
+
REQUIRED = %i(
|
37
|
+
host
|
38
|
+
database
|
39
|
+
).freeze
|
40
|
+
|
41
|
+
DEFAULT_DATE_FORMAT = "MM/dd/yyyy"
|
42
|
+
DEFAULT_TIME_FORMAT = "HH:mm:ss"
|
43
|
+
DEFAULT_TIMESTAMP_FORMAT = "#{DEFAULT_DATE_FORMAT} #{DEFAULT_TIME_FORMAT}"
|
44
|
+
|
45
|
+
DEFAULTS = {
|
46
|
+
autologin: true,
|
47
|
+
log: false,
|
48
|
+
date_format: DEFAULT_DATE_FORMAT,
|
49
|
+
time_format: DEFAULT_TIME_FORMAT,
|
50
|
+
timestamp_format: DEFAULT_TIMESTAMP_FORMAT,
|
51
|
+
coerce_dates: false
|
52
|
+
}.freeze
|
53
|
+
|
54
|
+
def self.wrap(settings, skip_validation: false)
|
55
|
+
if settings.kind_of?(self)
|
56
|
+
settings.validate unless skip_validation
|
57
|
+
return settings
|
58
|
+
end
|
59
|
+
new(settings, skip_validation: skip_validation)
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(settings, skip_validation: false)
|
63
|
+
@settings = settings.to_h.dup
|
64
|
+
normalize
|
65
|
+
validate unless skip_validation
|
66
|
+
end
|
67
|
+
|
68
|
+
PROPERTIES.each do |p|
|
69
|
+
define_method(p) do
|
70
|
+
get(p)
|
71
|
+
end
|
72
|
+
|
73
|
+
define_method("#{p}!") do
|
74
|
+
r = get(p)
|
75
|
+
raise MissingSetting, "Missing required setting: `#{p}'" if r.nil?
|
76
|
+
r
|
77
|
+
end
|
78
|
+
|
79
|
+
define_method("#{p}?") do
|
80
|
+
!!get(p)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def [](key)
|
85
|
+
raise ArgumentError, "Unknown setting `#{key}'" unless PROPERTIES.include?(key.to_sym)
|
86
|
+
get(key)
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_h
|
90
|
+
PROPERTIES.each_with_object({}) do |p, h|
|
91
|
+
v = get(p)
|
92
|
+
h[p] = v unless v == DEFAULTS[p]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def merge(other, **keyword_args)
|
97
|
+
other = self.class.wrap(other, skip_validation: true)
|
98
|
+
self.class.new(to_h.merge(other.to_h), **keyword_args)
|
99
|
+
end
|
100
|
+
|
101
|
+
def validate
|
102
|
+
missing = REQUIRED.select { |r| get(r).nil? }.map { |m| "`#{m}'" }
|
103
|
+
raise MissingSetting, "Missing required setting(s): #{missing.join(', ')}" unless missing.empty?
|
104
|
+
|
105
|
+
unless username? || token?
|
106
|
+
raise MissingSetting, "A minimum of `username' or `token' are required to be able to establish a connection"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def get(key)
|
113
|
+
return @settings[key.to_sym] if @settings.has_key?(key.to_sym)
|
114
|
+
return @settings[key.to_s] if @settings.has_key?(key.to_s)
|
115
|
+
DEFAULTS[key.to_sym]
|
116
|
+
end
|
117
|
+
|
118
|
+
def normalize
|
119
|
+
if !get(:username) && account_name = get(:account_name)
|
120
|
+
@settings[:username] = account_name
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/fmrest/errors.rb
CHANGED
@@ -21,6 +21,8 @@ module FmRest
|
|
21
21
|
class APIError::NoMatchingRecordsError < APIError::ParameterError; end
|
22
22
|
class APIError::ValidationError < APIError; end # error codes 500..599
|
23
23
|
class APIError::SystemError < APIError; end # error codes 800..899
|
24
|
+
class APIError::InvalidToken < APIError; end # error code 952
|
25
|
+
class APIError::MaximumDataAPICallsExceeded < APIError; end # error code 953
|
24
26
|
class APIError::ScriptError < APIError; end # error codes 1200..1299
|
25
27
|
class APIError::ODBCError < APIError; end # error codes 1400..1499
|
26
28
|
|
data/lib/fmrest/spyke/base.rb
CHANGED
@@ -5,17 +5,5 @@ module FmRest
|
|
5
5
|
class Base < ::Spyke::Base
|
6
6
|
include FmRest::Spyke::Model
|
7
7
|
end
|
8
|
-
|
9
|
-
class << self
|
10
|
-
def Base(config = nil)
|
11
|
-
if config
|
12
|
-
return Class.new(::FmRest::Spyke::Base) do
|
13
|
-
self.fmrest_config = config
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
::FmRest::Spyke::Base
|
18
|
-
end
|
19
|
-
end
|
20
8
|
end
|
21
9
|
end
|