fmrest 0.11.1 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/UPGRADING +15 -0
- data/fmrest.gemspec +14 -3
- data/lib/fmrest/spyke/base.rb +0 -14
- 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/connection.rb +40 -12
- 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 +2 -2
- data/lib/fmrest/spyke/model/uri.rb +3 -4
- data/lib/fmrest/spyke/spyke_formatter.rb +5 -5
- data/lib/fmrest/token_store/null.rb +20 -0
- data/lib/fmrest/version.rb +1 -1
- metadata +28 -10
- data/.github/workflows/ci.yml +0 -33
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,11 @@
|
|
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
|
+
|
3
9
|
### 0.11.1
|
4
10
|
|
5
11
|
* Fix a couple crashes due to missing constants
|
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
30
|
spec.add_development_dependency "bundler"
|
27
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/spyke/base.rb
CHANGED
@@ -5,19 +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
|
-
warn "[DEPRECATION] Inheriting from `FmRest::Spyke::Base(config)` is deprecated and will be removed, inherit from `FmRest::Spyke::Base` (without arguments) and use `fmrest_config=` instead"
|
12
|
-
|
13
|
-
if config
|
14
|
-
return Class.new(::FmRest::Spyke::Base) do
|
15
|
-
self.fmrest_config = config
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
::FmRest::Spyke::Base
|
20
|
-
end
|
21
|
-
end
|
22
8
|
end
|
23
9
|
end
|
@@ -42,7 +42,7 @@ module FmRest
|
|
42
42
|
)
|
43
43
|
|
44
44
|
# Update mod id on record
|
45
|
-
@base.
|
45
|
+
@base.__mod_id = response.body[:data][:__mod_id]
|
46
46
|
|
47
47
|
true
|
48
48
|
end
|
@@ -52,7 +52,7 @@ module FmRest
|
|
52
52
|
# @param repetition [Integer]
|
53
53
|
# @return [String] the path for uploading a file to the container
|
54
54
|
def upload_path(repetition)
|
55
|
-
FmRest::V1.container_field_path(@base.class.layout, @base.
|
55
|
+
FmRest::V1.container_field_path(@base.class.layout, @base.__record_id, name, repetition)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
data/lib/fmrest/spyke/model.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "fmrest/spyke/model/connection"
|
4
4
|
require "fmrest/spyke/model/uri"
|
5
|
+
require "fmrest/spyke/model/record_id"
|
5
6
|
require "fmrest/spyke/model/attributes"
|
6
7
|
require "fmrest/spyke/model/serialization"
|
7
8
|
require "fmrest/spyke/model/associations"
|
@@ -17,7 +18,8 @@ module FmRest
|
|
17
18
|
extend ::ActiveSupport::Concern
|
18
19
|
|
19
20
|
include Connection
|
20
|
-
include
|
21
|
+
include URI
|
22
|
+
include RecordID
|
21
23
|
include Attributes
|
22
24
|
include Serialization
|
23
25
|
include Associations
|
@@ -26,11 +28,6 @@ module FmRest
|
|
26
28
|
include GlobalFields
|
27
29
|
include Http
|
28
30
|
include Auth
|
29
|
-
|
30
|
-
included do
|
31
|
-
# @return [Integer] the record's modId
|
32
|
-
attr_accessor :mod_id
|
33
|
-
end
|
34
31
|
end
|
35
32
|
end
|
36
33
|
end
|
@@ -5,6 +5,8 @@ require "fmrest/spyke/portal"
|
|
5
5
|
module FmRest
|
6
6
|
module Spyke
|
7
7
|
module Model
|
8
|
+
# This module adds portal support to Spyke models.
|
9
|
+
#
|
8
10
|
module Associations
|
9
11
|
extend ::ActiveSupport::Concern
|
10
12
|
|
@@ -22,17 +24,18 @@ module FmRest
|
|
22
24
|
end
|
23
25
|
|
24
26
|
class_methods do
|
25
|
-
# Based on
|
27
|
+
# Based on `has_many`, but creates a special Portal association
|
26
28
|
# instead.
|
27
29
|
#
|
28
|
-
#
|
30
|
+
# @option :portal_key [String] The key used for the portal in the FM
|
31
|
+
# Data JSON portalData
|
32
|
+
# @option :attribute_prefix [String] The prefix used for portal
|
33
|
+
# attributes in the FM Data JSON
|
29
34
|
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# has_portal :jobs, portal_key: "JobsTable", attribute_prefix: "Job"
|
35
|
+
# @example
|
36
|
+
# class Person < FmRest::Spyke::Base
|
37
|
+
# has_portal :jobs, portal_key: "JobsTable", attribute_prefix: "Job"
|
38
|
+
# end
|
36
39
|
#
|
37
40
|
def has_portal(name, options = {})
|
38
41
|
create_association(name, Portal, options)
|
@@ -47,9 +50,8 @@ module FmRest
|
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
50
|
-
#
|
51
|
-
#
|
52
|
-
# each time.
|
53
|
+
# Spyke override -- Keep a cache of loaded portals. Spyke's default
|
54
|
+
# behavior is to reload the association each time.
|
53
55
|
#
|
54
56
|
def association(name)
|
55
57
|
@loaded_portals ||= {}
|
@@ -64,6 +66,8 @@ module FmRest
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
69
|
+
# Spyke override -- Add portals awareness
|
70
|
+
#
|
67
71
|
def reload(*_)
|
68
72
|
super.tap { @loaded_portals = nil }
|
69
73
|
end
|
@@ -5,6 +5,10 @@ require "fmrest/spyke/model/orm"
|
|
5
5
|
module FmRest
|
6
6
|
module Spyke
|
7
7
|
module Model
|
8
|
+
# Extends Spyke models with support for mapped attributes,
|
9
|
+
# `ActiveModel::Dirty` and forbidden attributes (e.g. Rails'
|
10
|
+
# `params.permit`).
|
11
|
+
#
|
8
12
|
module Attributes
|
9
13
|
extend ::ActiveSupport::Concern
|
10
14
|
|
@@ -20,9 +24,6 @@ module FmRest
|
|
20
24
|
# by Spyke
|
21
25
|
self.attribute_method_matchers.shift
|
22
26
|
|
23
|
-
# ActiveModel::Dirty methods for id
|
24
|
-
define_attribute_method(:id)
|
25
|
-
|
26
27
|
# Keep track of attribute mappings so we can get the FM field names
|
27
28
|
# for changed attributes
|
28
29
|
class_attribute :mapped_attributes, instance_writer: false, instance_predicate: false
|
@@ -36,10 +37,12 @@ module FmRest
|
|
36
37
|
end
|
37
38
|
|
38
39
|
class_methods do
|
40
|
+
# Spyke override
|
41
|
+
#
|
39
42
|
# Similar to Spyke::Base.attributes, but allows defining attribute
|
40
43
|
# methods that map to FM attributes with different names.
|
41
44
|
#
|
42
|
-
#
|
45
|
+
# @example
|
43
46
|
#
|
44
47
|
# class Person < Spyke::Base
|
45
48
|
# include FmRest::Spyke::Model
|
@@ -61,9 +64,10 @@ module FmRest
|
|
61
64
|
|
62
65
|
private
|
63
66
|
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
+
# Spyke override (private)
|
68
|
+
#
|
69
|
+
# Called whenever loading records from the HTTP API, so we can reset
|
70
|
+
# dirty info on freshly loaded records
|
67
71
|
#
|
68
72
|
# See: https://github.com/balvig/spyke/blob/master/lib/spyke/http.rb
|
69
73
|
#
|
@@ -88,8 +92,6 @@ module FmRest
|
|
88
92
|
end
|
89
93
|
|
90
94
|
def _fmrest_define_attribute(from, to)
|
91
|
-
raise ArgumentError, "attribute name `id' is reserved for the recordId" if from.to_s == "id"
|
92
|
-
|
93
95
|
# We use a setter here instead of injecting the hash key/value pair
|
94
96
|
# directly with #[]= so that we don't change the mapped_attributes
|
95
97
|
# hash on the parent class. The resulting hash is frozen for the
|
@@ -112,35 +114,25 @@ module FmRest
|
|
112
114
|
end
|
113
115
|
end
|
114
116
|
|
115
|
-
|
116
|
-
|
117
|
-
super
|
118
|
-
end
|
119
|
-
|
117
|
+
# Spyke override -- Adds AM::Dirty support
|
118
|
+
#
|
120
119
|
def reload(*args)
|
121
120
|
super.tap { |r| clear_changes_information }
|
122
121
|
end
|
123
122
|
|
123
|
+
# Spyke override -- Adds AM::Dirty support
|
124
|
+
#
|
124
125
|
def save(*args)
|
125
126
|
super.tap { |r| changes_applied_after_save if r }
|
126
127
|
end
|
127
128
|
|
128
|
-
#
|
129
|
-
#
|
130
|
-
# we override the instance variable name used by Spyke to avoid issues.
|
131
|
-
#
|
132
|
-
# TODO: Submit a pull request to Spyke so this isn't needed
|
133
|
-
#
|
134
|
-
def attributes
|
135
|
-
@_spyke_attributes
|
136
|
-
end
|
137
|
-
|
138
|
-
# In addition to the comments above on `attributes`, this also adds
|
139
|
-
# support for forbidden attributes
|
129
|
+
# Spyke override -- Adds support for forbidden attributes (i.e. Rails'
|
130
|
+
# `params.permit`, etc.)
|
140
131
|
#
|
141
132
|
def attributes=(new_attributes)
|
142
|
-
@
|
143
|
-
|
133
|
+
@spyke_attributes ||= ::Spyke::Attributes.new(scope.params)
|
134
|
+
return unless new_attributes && !new_attributes.empty?
|
135
|
+
use_setters(sanitize_for_mass_assignment(new_attributes))
|
144
136
|
end
|
145
137
|
|
146
138
|
private
|
@@ -153,7 +145,7 @@ module FmRest
|
|
153
145
|
mapped_attributes.values_at(*changed)
|
154
146
|
end
|
155
147
|
|
156
|
-
# Use known mapped_attributes for inspect
|
148
|
+
# Spyke override (private) -- Use known mapped_attributes for inspect
|
157
149
|
#
|
158
150
|
def inspect_attributes
|
159
151
|
mapped_attributes.except(primary_key).map do |k, v|
|
@@ -3,6 +3,9 @@
|
|
3
3
|
module FmRest
|
4
4
|
module Spyke
|
5
5
|
module Model
|
6
|
+
# This module provides methods for configuring the Farday connection for
|
7
|
+
# the model, as well as setting up the connection itself.
|
8
|
+
#
|
6
9
|
module Connection
|
7
10
|
extend ActiveSupport::Concern
|
8
11
|
|
@@ -10,7 +13,7 @@ module FmRest
|
|
10
13
|
class_attribute :faraday_block, instance_accessor: false, instance_predicate: false
|
11
14
|
class << self; private :faraday_block, :faraday_block=; end
|
12
15
|
|
13
|
-
# FM Data API expects PATCH for updates (Spyke
|
16
|
+
# FM Data API expects PATCH for updates (Spyke uses PUT by default)
|
14
17
|
self.callback_methods = { create: :post, update: :patch }.freeze
|
15
18
|
end
|
16
19
|
|
@@ -23,28 +26,37 @@ module FmRest
|
|
23
26
|
FmRest.default_connection_settings
|
24
27
|
end
|
25
28
|
|
26
|
-
#
|
27
|
-
#
|
29
|
+
# Sets the FileMaker connection settings for the model.
|
30
|
+
#
|
31
|
+
# Behaves similar to ActiveSupport's `class_attribute`, so it can be
|
32
|
+
# inherited and safely overwritten in subclasses.
|
33
|
+
#
|
34
|
+
# @param settings [Hash] The settings hash
|
28
35
|
#
|
29
36
|
def fmrest_config=(settings)
|
30
37
|
settings = ConnectionSettings.new(settings, skip_validation: true)
|
31
38
|
|
32
|
-
|
39
|
+
singleton_class.redefine_method(:fmrest_config) do
|
33
40
|
overlay = fmrest_config_overlay
|
34
41
|
return settings.merge(overlay, skip_validation: true) if overlay
|
35
42
|
settings
|
36
43
|
end
|
37
44
|
end
|
38
45
|
|
39
|
-
# Allows
|
46
|
+
# Allows overriding some connection settings in a thread-local
|
40
47
|
# manner. Useful in the use case where you want to connect to the
|
41
48
|
# same database using different accounts (e.g. credentials provided
|
42
|
-
# by users in a web app context)
|
49
|
+
# by users in a web app context).
|
50
|
+
#
|
51
|
+
# @param (see #fmrest_config=)
|
43
52
|
#
|
44
53
|
def fmrest_config_overlay=(settings)
|
45
54
|
Thread.current[fmrest_config_overlay_key] = settings
|
46
55
|
end
|
47
56
|
|
57
|
+
# @return [FmRest::ConnectionSettings] the connection settings
|
58
|
+
# overlay if any is in use
|
59
|
+
#
|
48
60
|
def fmrest_config_overlay
|
49
61
|
Thread.current[fmrest_config_overlay_key] || begin
|
50
62
|
superclass.fmrest_config_overlay
|
@@ -53,10 +65,23 @@ module FmRest
|
|
53
65
|
end
|
54
66
|
end
|
55
67
|
|
68
|
+
# Clears the connection settings overlay.
|
69
|
+
#
|
56
70
|
def clear_fmrest_config_overlay
|
57
71
|
Thread.current[fmrest_config_overlay_key] = nil
|
58
72
|
end
|
59
73
|
|
74
|
+
# Runs a block of code in the context of the given connection
|
75
|
+
# settings without affecting the connection settings outside said
|
76
|
+
# block.
|
77
|
+
#
|
78
|
+
# @param (see #fmrest_config=)
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# Honeybee.with_overlay(username: "...", password: "...") do
|
82
|
+
# Honeybee.query(...)
|
83
|
+
# end
|
84
|
+
#
|
60
85
|
def with_overlay(settings, &block)
|
61
86
|
Fiber.new do
|
62
87
|
begin
|
@@ -68,19 +93,22 @@ module FmRest
|
|
68
93
|
end.resume
|
69
94
|
end
|
70
95
|
|
96
|
+
# Spyke override -- Defaults to `fmrest_connection`
|
97
|
+
#
|
71
98
|
def connection
|
72
99
|
super || fmrest_connection
|
73
100
|
end
|
74
101
|
|
75
102
|
# Sets a block for injecting custom middleware into the Faraday
|
76
|
-
# connection.
|
103
|
+
# connection.
|
77
104
|
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
105
|
+
# @example
|
106
|
+
# class MyModel < FmRest::Spyke::Base
|
107
|
+
# faraday do |conn|
|
108
|
+
# # Set up a custom logger for the model
|
109
|
+
# conn.response :logger, MyApp.logger, bodies: true
|
83
110
|
# end
|
111
|
+
# end
|
84
112
|
#
|
85
113
|
def faraday(&block)
|
86
114
|
self.faraday_block = block
|
@@ -5,10 +5,25 @@ require "fmrest/spyke/container_field"
|
|
5
5
|
module FmRest
|
6
6
|
module Spyke
|
7
7
|
module Model
|
8
|
+
# This module adds support for container fields.
|
9
|
+
#
|
8
10
|
module ContainerFields
|
9
11
|
extend ::ActiveSupport::Concern
|
10
12
|
|
11
13
|
class_methods do
|
14
|
+
# Defines a container field on the model.
|
15
|
+
#
|
16
|
+
# @param name [Symbol] the name of the container field
|
17
|
+
#
|
18
|
+
# @option options [String] :field_name (nil) the name of the container
|
19
|
+
# field in the FileMaker layout (only needed if it doesn't match
|
20
|
+
# the name given)
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# class Honeybee < FmRest::Spyke::Base
|
24
|
+
# container :photo, field_name: "Beehive Photo ID"
|
25
|
+
# end
|
26
|
+
#
|
12
27
|
def container(name, options = {})
|
13
28
|
field_name = options[:field_name] || name
|
14
29
|
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "fmrest/spyke/relation"
|
4
|
-
|
5
3
|
module FmRest
|
6
4
|
module Spyke
|
7
5
|
module Model
|
@@ -15,6 +13,8 @@ module FmRest
|
|
15
13
|
# execution results after a save, etc.
|
16
14
|
|
17
15
|
|
16
|
+
# Spyke override -- Keeps metadata in thread-local class variable.
|
17
|
+
#
|
18
18
|
def request(*args)
|
19
19
|
super.tap do |r|
|
20
20
|
Thread.current[last_request_metadata_key] = r.metadata
|
@@ -32,6 +32,46 @@ module FmRest
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
# Spyke override -- Uses `__record_id` for building the record URI.
|
37
|
+
#
|
38
|
+
def uri
|
39
|
+
::Spyke::Path.new(@uri_template, fmrest_uri_attributes) if @uri_template
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Spyke override (private) -- Use `__record_id` instead of `id`
|
45
|
+
#
|
46
|
+
def resolve_path_from_action(action)
|
47
|
+
case action
|
48
|
+
when Symbol then uri.join(action)
|
49
|
+
when String then ::Spyke::Path.new(action, fmrest_uri_attributes)
|
50
|
+
else uri
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def fmrest_uri_attributes
|
55
|
+
if persisted?
|
56
|
+
{ __record_id: __record_id }
|
57
|
+
else
|
58
|
+
# NOTE: it seems silly to be calling attributes.slice(:__record_id)
|
59
|
+
# when the record is supposed to not have a record_id set (since
|
60
|
+
# persisted? is false here), but it makes sense in the context of how
|
61
|
+
# Spyke works:
|
62
|
+
#
|
63
|
+
# When calling Model.find(id), Spyke will internally create a scope
|
64
|
+
# with .where(primary_key => id) and call .find_one on it. Then,
|
65
|
+
# somewhere down the line Spyke creates a new empty instance of the
|
66
|
+
# current model class to get its .uri property (the one we're
|
67
|
+
# partially building through this method and which contains these URI
|
68
|
+
# attributes). When initializing a record Spyke first forcefully
|
69
|
+
# assigns the .where()-set attributes from the current scope onto
|
70
|
+
# that instance's attributes hash, which then leads us right here,
|
71
|
+
# where we might have __record_id assigned as a scope attribute:
|
72
|
+
attributes.slice(:__record_id)
|
73
|
+
end
|
74
|
+
end
|
35
75
|
end
|
36
76
|
end
|
37
77
|
end
|
@@ -6,6 +6,10 @@ require "fmrest/spyke/validation_error"
|
|
6
6
|
module FmRest
|
7
7
|
module Spyke
|
8
8
|
module Model
|
9
|
+
# This module adds and extends various ORM features in Spyke models,
|
10
|
+
# including custom query methods, remote script execution and
|
11
|
+
# exception-raising persistence methods.
|
12
|
+
#
|
9
13
|
module Orm
|
10
14
|
extend ::ActiveSupport::Concern
|
11
15
|
|
@@ -21,22 +25,25 @@ module FmRest
|
|
21
25
|
end
|
22
26
|
|
23
27
|
class_methods do
|
24
|
-
# Methods delegated to FmRest::Spyke::Relation
|
28
|
+
# Methods delegated to `FmRest::Spyke::Relation`
|
25
29
|
delegate :limit, :offset, :sort, :order, :query, :omit, :portal,
|
26
30
|
:portals, :includes, :with_all_portals, :without_portals,
|
27
31
|
:script, :find_one, :first, :any, :find_some,
|
28
32
|
:find_in_batches, :find_each, to: :all
|
29
33
|
|
34
|
+
# Spyke override -- Use FmRest's Relation instead of Spyke's vanilla
|
35
|
+
# one
|
36
|
+
#
|
30
37
|
def all
|
31
|
-
# Use FmRest's Relation instead of Spyke's vanilla one
|
32
38
|
current_scope || Relation.new(self, uri: uri)
|
33
39
|
end
|
34
40
|
|
35
|
-
#
|
41
|
+
# Spyke override -- allows properly setting limit, offset and other
|
36
42
|
# options, as well as using the appropriate HTTP method/URL depending
|
37
|
-
# on whether there's a query present in the current scope
|
43
|
+
# on whether there's a query present in the current scope.
|
38
44
|
#
|
39
|
-
#
|
45
|
+
# @example
|
46
|
+
# Person.query(first_name: "Stefan").fetch # -> POST .../_find
|
40
47
|
#
|
41
48
|
def fetch
|
42
49
|
if current_scope.has_query?
|
@@ -62,12 +69,21 @@ module FmRest
|
|
62
69
|
self.current_scope = previous
|
63
70
|
end
|
64
71
|
|
65
|
-
#
|
72
|
+
# Exception-raising version of `#create`
|
73
|
+
#
|
74
|
+
# @param attributes [Hash] the attributes to initialize the
|
75
|
+
# record with
|
66
76
|
#
|
67
77
|
def create!(attributes = {})
|
68
78
|
new(attributes).tap(&:save!)
|
69
79
|
end
|
70
80
|
|
81
|
+
# Requests execution of a FileMaker script.
|
82
|
+
#
|
83
|
+
# @param script_name [String] the name of the FileMaker script to
|
84
|
+
# execute
|
85
|
+
# @param param [String] an optional paramater for the script
|
86
|
+
#
|
71
87
|
def execute_script(script_name, param: nil)
|
72
88
|
params = {}
|
73
89
|
params = {"script.param" => param} unless param.nil?
|
@@ -108,12 +124,20 @@ module FmRest
|
|
108
124
|
end
|
109
125
|
end
|
110
126
|
|
111
|
-
#
|
127
|
+
# Spyke override -- Adds a number of features to original `#save`:
|
112
128
|
#
|
113
129
|
# * Validations
|
114
130
|
# * Data API scripts execution
|
115
131
|
# * Refresh of dirty attributes
|
116
132
|
#
|
133
|
+
# @option options [String] :script the name of a FileMaker script to execute
|
134
|
+
# upon saving
|
135
|
+
# @option options [Boolean] :raise_validation_errors whether to raise an
|
136
|
+
# exception if validations fail
|
137
|
+
#
|
138
|
+
# @return [true] if saved successfully
|
139
|
+
# @return [false] if validations or persistence failed
|
140
|
+
#
|
117
141
|
def save(options = {})
|
118
142
|
callback = persisted? ? :update : :create
|
119
143
|
|
@@ -123,11 +147,34 @@ module FmRest
|
|
123
147
|
true
|
124
148
|
end
|
125
149
|
|
150
|
+
# Exception-raising version of `#save`.
|
151
|
+
#
|
152
|
+
# @option (see #save)
|
153
|
+
#
|
154
|
+
# @return [true] if saved successfully
|
155
|
+
#
|
156
|
+
# @raise if validations or presistence failed
|
157
|
+
#
|
126
158
|
def save!(options = {})
|
127
159
|
save(options.merge(raise_validation_errors: true))
|
128
160
|
end
|
129
161
|
|
130
|
-
#
|
162
|
+
# Exception-raising version of `#update`.
|
163
|
+
#
|
164
|
+
# @param new_attributes [Hash] a hash of record attributes to update
|
165
|
+
# the record with
|
166
|
+
#
|
167
|
+
# @option (see #save)
|
168
|
+
#
|
169
|
+
def update!(new_attributes, options = {})
|
170
|
+
self.attributes = new_attributes
|
171
|
+
save!(options)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Spyke override -- Adds support for Data API script execution.
|
175
|
+
#
|
176
|
+
# @option options [String] :script the name of a FileMaker script to execute
|
177
|
+
# upon deletion
|
131
178
|
#
|
132
179
|
def destroy(options = {})
|
133
180
|
# For whatever reason the Data API wants the script params as query
|
@@ -142,22 +189,19 @@ module FmRest
|
|
142
189
|
self.attributes = delete(uri.to_s + script_query_string)
|
143
190
|
end
|
144
191
|
|
145
|
-
#
|
192
|
+
# (see #destroy)
|
193
|
+
#
|
194
|
+
# @option (see #destroy)
|
146
195
|
#
|
147
|
-
def update!(new_attributes, options = {})
|
148
|
-
self.attributes = new_attributes
|
149
|
-
save!(options)
|
150
|
-
end
|
151
|
-
|
152
196
|
def reload(options = {})
|
153
197
|
scope = self.class
|
154
198
|
scope = scope.script(options[:script]) if options.has_key?(:script)
|
155
|
-
reloaded = scope.find(
|
199
|
+
reloaded = scope.find(__record_id)
|
156
200
|
self.attributes = reloaded.attributes
|
157
|
-
self.
|
201
|
+
self.__mod_id = reloaded.mod_id
|
158
202
|
end
|
159
203
|
|
160
|
-
# ActiveModel 5+ implements this method, so we only
|
204
|
+
# ActiveModel 5+ implements this method, so we only need it if we're in
|
161
205
|
# the older AM4
|
162
206
|
if ActiveModel::VERSION::MAJOR == 4
|
163
207
|
def validate!(context = nil)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FmRest
|
4
|
+
module Spyke
|
5
|
+
module Model
|
6
|
+
# Modifies Spyke models to use `__record_id` instead of `id` as the
|
7
|
+
# "primary key" method, so that we can map a model class to a FM layout
|
8
|
+
# with a field named `id` without clobbering it.
|
9
|
+
#
|
10
|
+
# The `id` reader method still maps to the record ID for backwards
|
11
|
+
# compatibility and because Spyke hardcodes its use at various points
|
12
|
+
# through its codebase, but it can be safely overwritten (e.g. to map to
|
13
|
+
# a FM field).
|
14
|
+
#
|
15
|
+
# The recommended way to deal with a layout that maps an `id` attribute
|
16
|
+
# is to remap it in the model to something else, e.g. `unique_id`.
|
17
|
+
#
|
18
|
+
module RecordID
|
19
|
+
extend ::ActiveSupport::Concern
|
20
|
+
|
21
|
+
included do
|
22
|
+
# @return [Integer] the record's recordId
|
23
|
+
attr_accessor :__record_id
|
24
|
+
alias_method :record_id, :__record_id
|
25
|
+
alias_method :id, :__record_id
|
26
|
+
|
27
|
+
# @return [Integer] the record's modId
|
28
|
+
attr_accessor :__mod_id
|
29
|
+
alias_method :mod_id, :__mod_id
|
30
|
+
|
31
|
+
# Get rid of Spyke's id= setter method, as we'll be using __record_id=
|
32
|
+
# instead
|
33
|
+
undef_method :id=
|
34
|
+
|
35
|
+
# Tell Spyke that we want __record_id as the PK
|
36
|
+
self.primary_key = :__record_id
|
37
|
+
end
|
38
|
+
|
39
|
+
def __record_id?
|
40
|
+
__record_id.present?
|
41
|
+
end
|
42
|
+
alias_method :record_id?, :__record_id?
|
43
|
+
alias_method :persisted?, :__record_id?
|
44
|
+
|
45
|
+
# Spyke override -- Use `__record_id` instead of `id`
|
46
|
+
#
|
47
|
+
def hash
|
48
|
+
__record_id.hash
|
49
|
+
end
|
50
|
+
|
51
|
+
# Spyke override -- Renders class string with layout name and
|
52
|
+
# `record_id`.
|
53
|
+
#
|
54
|
+
# @return [String] A string representation of the class
|
55
|
+
#
|
56
|
+
def inspect
|
57
|
+
"#<#{self.class}(layout: #{self.class.layout}) record_id: #{__record_id.inspect} #{inspect_attributes}>"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Spyke override -- Use `__record_id` instead of `id`
|
61
|
+
#
|
62
|
+
# @param id [Integer] The id of the record to destroy
|
63
|
+
#
|
64
|
+
def destroy(id = nil)
|
65
|
+
new(__record_id: id).destroy
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Spyke override (private)
|
71
|
+
#
|
72
|
+
def conflicting_ids?(attributes)
|
73
|
+
false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -7,8 +7,8 @@ module FmRest
|
|
7
7
|
FM_DATE_FORMAT = "%m/%d/%Y"
|
8
8
|
FM_DATETIME_FORMAT = "#{FM_DATE_FORMAT} %H:%M:%S"
|
9
9
|
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# Spyke override -- Return FM Data API's expected JSON format,
|
11
|
+
# including only modified fields.
|
12
12
|
#
|
13
13
|
def to_params
|
14
14
|
params = {
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module FmRest
|
4
4
|
module Spyke
|
5
5
|
module Model
|
6
|
-
module
|
6
|
+
module URI
|
7
7
|
extend ::ActiveSupport::Concern
|
8
8
|
|
9
9
|
class_methods do
|
@@ -14,13 +14,12 @@ module FmRest
|
|
14
14
|
@layout ||= model_name.name
|
15
15
|
end
|
16
16
|
|
17
|
-
#
|
17
|
+
# Spyke override -- Extends `uri` to default to FM Data's URI schema
|
18
18
|
#
|
19
19
|
def uri(uri_template = nil)
|
20
20
|
if @uri.nil? && uri_template.nil?
|
21
|
-
return FmRest::V1.record_path(layout) + "(
|
21
|
+
return FmRest::V1.record_path(layout) + "(/:#{primary_key})"
|
22
22
|
end
|
23
|
-
|
24
23
|
super
|
25
24
|
end
|
26
25
|
end
|
@@ -63,8 +63,8 @@ module FmRest
|
|
63
63
|
response = json[:response]
|
64
64
|
|
65
65
|
data = {}
|
66
|
-
data[:
|
67
|
-
data[:
|
66
|
+
data[:__mod_id] = response[:modId] if response[:modId]
|
67
|
+
data[:__record_id] = response[:recordId].to_i if response[:recordId]
|
68
68
|
|
69
69
|
build_base_hash(json, true).merge!(data: data)
|
70
70
|
end
|
@@ -188,7 +188,7 @@ module FmRest
|
|
188
188
|
# @param json_data [Hash]
|
189
189
|
# @return [Hash] the record data in Spyke format
|
190
190
|
def prepare_record_data(json_data)
|
191
|
-
out = {
|
191
|
+
out = { __record_id: json_data[:recordId].to_i, __mod_id: json_data[:modId] }
|
192
192
|
out.merge!(json_data[:fieldData])
|
193
193
|
out.merge!(prepare_portal_data(json_data[:portalData])) if json_data[:portalData]
|
194
194
|
out
|
@@ -213,8 +213,8 @@ module FmRest
|
|
213
213
|
|
214
214
|
out[portal_name] =
|
215
215
|
portal_records.map do |portal_fields|
|
216
|
-
attributes = {
|
217
|
-
attributes[:
|
216
|
+
attributes = { __record_id: portal_fields[:recordId].to_i }
|
217
|
+
attributes[:__mod_id] = portal_fields[:modId] if portal_fields[:modId]
|
218
218
|
|
219
219
|
prefix = portal_options[:attribute_prefix] || portal_name
|
220
220
|
prefix_matcher = /\A#{prefix}::/
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
|
5
|
+
module FmRest
|
6
|
+
module TokenStore
|
7
|
+
module Null < Base
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def delete(key)
|
11
|
+
end
|
12
|
+
|
13
|
+
def load(key)
|
14
|
+
end
|
15
|
+
|
16
|
+
def store(key, value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/fmrest/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fmrest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pedro Carbajal
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -98,14 +98,14 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - ">="
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
101
|
+
version: 5.3.3
|
102
102
|
type: :development
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
105
|
requirements:
|
106
106
|
- - ">="
|
107
107
|
- !ruby/object:Gem::Version
|
108
|
-
version:
|
108
|
+
version: 5.3.3
|
109
109
|
- !ruby/object:Gem::Dependency
|
110
110
|
name: webmock
|
111
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -152,16 +152,16 @@ dependencies:
|
|
152
152
|
name: sqlite3
|
153
153
|
requirement: !ruby/object:Gem::Requirement
|
154
154
|
requirements:
|
155
|
-
- - "
|
155
|
+
- - "~>"
|
156
156
|
- !ruby/object:Gem::Version
|
157
|
-
version:
|
157
|
+
version: 1.4.0
|
158
158
|
type: :development
|
159
159
|
prerelease: false
|
160
160
|
version_requirements: !ruby/object:Gem::Requirement
|
161
161
|
requirements:
|
162
|
-
- - "
|
162
|
+
- - "~>"
|
163
163
|
- !ruby/object:Gem::Version
|
164
|
-
version:
|
164
|
+
version: 1.4.0
|
165
165
|
- !ruby/object:Gem::Dependency
|
166
166
|
name: mock_redis
|
167
167
|
requirement: !ruby/object:Gem::Requirement
|
@@ -226,7 +226,6 @@ executables: []
|
|
226
226
|
extensions: []
|
227
227
|
extra_rdoc_files: []
|
228
228
|
files:
|
229
|
-
- ".github/workflows/ci.yml"
|
230
229
|
- ".gitignore"
|
231
230
|
- ".rspec"
|
232
231
|
- ".travis.yml"
|
@@ -236,6 +235,7 @@ files:
|
|
236
235
|
- LICENSE.txt
|
237
236
|
- README.md
|
238
237
|
- Rakefile
|
238
|
+
- UPGRADING
|
239
239
|
- fmrest.gemspec
|
240
240
|
- lib/fmrest.rb
|
241
241
|
- lib/fmrest/connection_settings.rb
|
@@ -252,6 +252,7 @@ files:
|
|
252
252
|
- lib/fmrest/spyke/model/global_fields.rb
|
253
253
|
- lib/fmrest/spyke/model/http.rb
|
254
254
|
- lib/fmrest/spyke/model/orm.rb
|
255
|
+
- lib/fmrest/spyke/model/record_id.rb
|
255
256
|
- lib/fmrest/spyke/model/serialization.rb
|
256
257
|
- lib/fmrest/spyke/model/uri.rb
|
257
258
|
- lib/fmrest/spyke/portal.rb
|
@@ -264,6 +265,7 @@ files:
|
|
264
265
|
- lib/fmrest/token_store/base.rb
|
265
266
|
- lib/fmrest/token_store/memory.rb
|
266
267
|
- lib/fmrest/token_store/moneta.rb
|
268
|
+
- lib/fmrest/token_store/null.rb
|
267
269
|
- lib/fmrest/token_store/redis.rb
|
268
270
|
- lib/fmrest/v1.rb
|
269
271
|
- lib/fmrest/v1/auth.rb
|
@@ -282,7 +284,22 @@ homepage: https://github.com/beezwax/fmrest-ruby
|
|
282
284
|
licenses:
|
283
285
|
- MIT
|
284
286
|
metadata: {}
|
285
|
-
post_install_message:
|
287
|
+
post_install_message: |+
|
288
|
+
=======================================
|
289
|
+
Notes on upgrading from fmrest < 0.12
|
290
|
+
=======================================
|
291
|
+
|
292
|
+
There's a small breaking change in fmrest 0.12 that will most likely not affect
|
293
|
+
you, but you may want to be aware of:
|
294
|
+
|
295
|
+
Previous to this version the record ID on an FmRest::Spyke::Base instance could
|
296
|
+
be set with `id=`. This caused problems in cases where a FileMaker layout had a
|
297
|
+
field named `id`, so `id=` got renamed to `__record_id=`. Setting the record ID
|
298
|
+
by hand however isn't something useful or that should be done at all, so it's
|
299
|
+
very unlikely that this change will affect your existing code at all.
|
300
|
+
|
301
|
+
Thanks for using fmrest-ruby!
|
302
|
+
|
286
303
|
rdoc_options: []
|
287
304
|
require_paths:
|
288
305
|
- lib
|
@@ -302,3 +319,4 @@ signing_key:
|
|
302
319
|
specification_version: 4
|
303
320
|
summary: FileMaker Data API client using Faraday
|
304
321
|
test_files: []
|
322
|
+
...
|
data/.github/workflows/ci.yml
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# This workflow uses actions that are not certified by GitHub.
|
2
|
-
# They are provided by a third-party and are governed by
|
3
|
-
# separate terms of service, privacy policy, and support
|
4
|
-
# documentation.
|
5
|
-
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
-
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
-
|
8
|
-
name: CI
|
9
|
-
|
10
|
-
on:
|
11
|
-
push:
|
12
|
-
branches: [ master ]
|
13
|
-
pull_request:
|
14
|
-
branches: [ master ]
|
15
|
-
|
16
|
-
jobs:
|
17
|
-
test:
|
18
|
-
|
19
|
-
runs-on: ubuntu-latest
|
20
|
-
|
21
|
-
steps:
|
22
|
-
- uses: actions/checkout@v2
|
23
|
-
- name: Set up Ruby
|
24
|
-
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
25
|
-
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
26
|
-
# uses: ruby/setup-ruby@v1
|
27
|
-
uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
|
28
|
-
with:
|
29
|
-
ruby-version: 2.6
|
30
|
-
- name: Install dependencies
|
31
|
-
run: bundle install
|
32
|
-
- name: Run specs
|
33
|
-
run: bundle exec rspec spec
|