fmrest 0.12.0 → 0.15.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -1,124 +0,0 @@
|
|
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
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FmRest
|
4
|
-
class Error < StandardError; end
|
5
|
-
|
6
|
-
class APIError < Error
|
7
|
-
attr_reader :code
|
8
|
-
|
9
|
-
def initialize(code, message = nil)
|
10
|
-
@code = code
|
11
|
-
super("FileMaker Data API responded with error #{code}: #{message}")
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class APIError::UnknownError < APIError; end # error code -1
|
16
|
-
class APIError::ResourceMissingError < APIError; end # error codes 100..199
|
17
|
-
class APIError::RecordMissingError < APIError::ResourceMissingError; end
|
18
|
-
class APIError::AccountError < APIError; end # error codes 200..299
|
19
|
-
class APIError::LockError < APIError; end # error codes 300..399
|
20
|
-
class APIError::ParameterError < APIError; end # error codes 400..499
|
21
|
-
class APIError::NoMatchingRecordsError < APIError::ParameterError; end
|
22
|
-
class APIError::ValidationError < APIError; end # error codes 500..599
|
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
|
26
|
-
class APIError::ScriptError < APIError; end # error codes 1200..1299
|
27
|
-
class APIError::ODBCError < APIError; end # error codes 1400..1499
|
28
|
-
|
29
|
-
class ContainerFieldError < Error; end
|
30
|
-
end
|
data/lib/fmrest/spyke.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
begin
|
4
|
-
require "spyke"
|
5
|
-
rescue LoadError => e
|
6
|
-
e.message << " (Did you include Spyke in your Gemfile?)" unless e.message.frozen?
|
7
|
-
raise e
|
8
|
-
end
|
9
|
-
|
10
|
-
require "fmrest"
|
11
|
-
require "fmrest/spyke/spyke_formatter"
|
12
|
-
require "fmrest/spyke/model"
|
13
|
-
require "fmrest/spyke/base"
|
14
|
-
|
15
|
-
module FmRest
|
16
|
-
module Spyke
|
17
|
-
def self.included(base)
|
18
|
-
base.include Model
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/lib/fmrest/spyke/base.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FmRest
|
4
|
-
module Spyke
|
5
|
-
class ContainerField
|
6
|
-
|
7
|
-
# @return [String] the name of the container field
|
8
|
-
attr_reader :name
|
9
|
-
|
10
|
-
# @param base [FmRest::Spyke::Base] the record this container belongs to
|
11
|
-
# @param name [Symbol] the name of the container field
|
12
|
-
def initialize(base, name)
|
13
|
-
@base = base
|
14
|
-
@name = name
|
15
|
-
end
|
16
|
-
|
17
|
-
# @return [String] the URL for the container
|
18
|
-
def url
|
19
|
-
@base.attributes[name]
|
20
|
-
end
|
21
|
-
|
22
|
-
# @return (see FmRest::V1::ContainerFields#fetch_container_data)
|
23
|
-
def download
|
24
|
-
FmRest::V1.fetch_container_data(url, @base.class.connection)
|
25
|
-
end
|
26
|
-
|
27
|
-
# @param filename_or_io [String, IO] a path to the file to upload or an
|
28
|
-
# IO object
|
29
|
-
# @param options [Hash]
|
30
|
-
# @option options [Integer] :repetition (1) The repetition to pass to the
|
31
|
-
# upload URL
|
32
|
-
# @option (see FmRest::V1::ContainerFields#upload_container_data)
|
33
|
-
def upload(filename_or_io, options = {})
|
34
|
-
raise ArgumentError, "Record needs to be saved before uploading to a container field" unless @base.persisted?
|
35
|
-
|
36
|
-
response =
|
37
|
-
FmRest::V1.upload_container_data(
|
38
|
-
@base.class.connection,
|
39
|
-
upload_path(options[:repetition] || 1),
|
40
|
-
filename_or_io,
|
41
|
-
options
|
42
|
-
)
|
43
|
-
|
44
|
-
# Update mod id on record
|
45
|
-
@base.__mod_id = response.body[:data][:__mod_id]
|
46
|
-
|
47
|
-
true
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
# @param repetition [Integer]
|
53
|
-
# @return [String] the path for uploading a file to the container
|
54
|
-
def upload_path(repetition)
|
55
|
-
FmRest::V1.container_field_path(@base.class.layout, @base.__record_id, name, repetition)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/lib/fmrest/spyke/model.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fmrest/spyke/model/connection"
|
4
|
-
require "fmrest/spyke/model/uri"
|
5
|
-
require "fmrest/spyke/model/record_id"
|
6
|
-
require "fmrest/spyke/model/attributes"
|
7
|
-
require "fmrest/spyke/model/serialization"
|
8
|
-
require "fmrest/spyke/model/associations"
|
9
|
-
require "fmrest/spyke/model/orm"
|
10
|
-
require "fmrest/spyke/model/container_fields"
|
11
|
-
require "fmrest/spyke/model/global_fields"
|
12
|
-
require "fmrest/spyke/model/http"
|
13
|
-
require "fmrest/spyke/model/auth"
|
14
|
-
|
15
|
-
module FmRest
|
16
|
-
module Spyke
|
17
|
-
module Model
|
18
|
-
extend ::ActiveSupport::Concern
|
19
|
-
|
20
|
-
include Connection
|
21
|
-
include URI
|
22
|
-
include RecordID
|
23
|
-
include Attributes
|
24
|
-
include Serialization
|
25
|
-
include Associations
|
26
|
-
include Orm
|
27
|
-
include ContainerFields
|
28
|
-
include GlobalFields
|
29
|
-
include Http
|
30
|
-
include Auth
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fmrest/spyke/portal"
|
4
|
-
|
5
|
-
module FmRest
|
6
|
-
module Spyke
|
7
|
-
module Model
|
8
|
-
# This module adds portal support to Spyke models.
|
9
|
-
#
|
10
|
-
module Associations
|
11
|
-
extend ::ActiveSupport::Concern
|
12
|
-
|
13
|
-
included do
|
14
|
-
# Keep track of portal options by their FM keys as we could need it
|
15
|
-
# to parse the portalData JSON in SpykeFormatter
|
16
|
-
class_attribute :portal_options, instance_accessor: false, instance_predicate: false
|
17
|
-
|
18
|
-
# class_attribute supports a :default option since ActiveSupport 5.2,
|
19
|
-
# but we want to support previous versions too so we set the default
|
20
|
-
# manually instead
|
21
|
-
self.portal_options = {}.freeze
|
22
|
-
|
23
|
-
class << self; private :portal_options=; end
|
24
|
-
end
|
25
|
-
|
26
|
-
class_methods do
|
27
|
-
# Based on `has_many`, but creates a special Portal association
|
28
|
-
# instead.
|
29
|
-
#
|
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
|
34
|
-
#
|
35
|
-
# @example
|
36
|
-
# class Person < FmRest::Spyke::Base
|
37
|
-
# has_portal :jobs, portal_key: "JobsTable", attribute_prefix: "Job"
|
38
|
-
# end
|
39
|
-
#
|
40
|
-
def has_portal(name, options = {})
|
41
|
-
create_association(name, Portal, options)
|
42
|
-
|
43
|
-
# Store options for SpykeFormatter to use if needed
|
44
|
-
portal_key = options[:portal_key] || name
|
45
|
-
self.portal_options = portal_options.merge(portal_key.to_s => options.dup.merge(name: name.to_s)).freeze
|
46
|
-
|
47
|
-
define_method "#{name.to_s.singularize}_ids" do
|
48
|
-
association(name).map(&:id)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Spyke override -- Keep a cache of loaded portals. Spyke's default
|
54
|
-
# behavior is to reload the association each time.
|
55
|
-
#
|
56
|
-
def association(name)
|
57
|
-
@loaded_portals ||= {}
|
58
|
-
|
59
|
-
if @loaded_portals.has_key?(name.to_sym)
|
60
|
-
return @loaded_portals[name.to_sym]
|
61
|
-
end
|
62
|
-
|
63
|
-
super.tap do |assoc|
|
64
|
-
next unless assoc.kind_of?(FmRest::Spyke::Portal)
|
65
|
-
@loaded_portals[name.to_sym] = assoc
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Spyke override -- Add portals awareness
|
70
|
-
#
|
71
|
-
def reload(*_)
|
72
|
-
super.tap { @loaded_portals = nil }
|
73
|
-
end
|
74
|
-
|
75
|
-
def portals
|
76
|
-
self.class.associations.each_with_object([]) do |(key, _), portals|
|
77
|
-
candidate = association(key)
|
78
|
-
next unless candidate.kind_of?(FmRest::Spyke::Portal)
|
79
|
-
portals << candidate
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
@@ -1,163 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fmrest/spyke/model/orm"
|
4
|
-
|
5
|
-
module FmRest
|
6
|
-
module Spyke
|
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
|
-
#
|
12
|
-
module Attributes
|
13
|
-
extend ::ActiveSupport::Concern
|
14
|
-
|
15
|
-
include Orm # Needed to extend custom save and reload
|
16
|
-
|
17
|
-
include ::ActiveModel::Dirty
|
18
|
-
include ::ActiveModel::ForbiddenAttributesProtection
|
19
|
-
|
20
|
-
included do
|
21
|
-
# Prevent the creation of plain (no prefix/suffix) attribute methods
|
22
|
-
# when calling ActiveModels' define_attribute_method, otherwise it
|
23
|
-
# will define an `attribute` method which overrides the one provided
|
24
|
-
# by Spyke
|
25
|
-
self.attribute_method_matchers.shift
|
26
|
-
|
27
|
-
# Keep track of attribute mappings so we can get the FM field names
|
28
|
-
# for changed attributes
|
29
|
-
class_attribute :mapped_attributes, instance_writer: false, instance_predicate: false
|
30
|
-
|
31
|
-
# class_attribute supports a :default option since ActiveSupport 5.2,
|
32
|
-
# but we want to support previous versions too so we set the default
|
33
|
-
# manually instead
|
34
|
-
self.mapped_attributes = ::ActiveSupport::HashWithIndifferentAccess.new.freeze
|
35
|
-
|
36
|
-
class << self; private :mapped_attributes=; end
|
37
|
-
end
|
38
|
-
|
39
|
-
class_methods do
|
40
|
-
# Spyke override
|
41
|
-
#
|
42
|
-
# Similar to Spyke::Base.attributes, but allows defining attribute
|
43
|
-
# methods that map to FM attributes with different names.
|
44
|
-
#
|
45
|
-
# @example
|
46
|
-
#
|
47
|
-
# class Person < Spyke::Base
|
48
|
-
# include FmRest::Spyke::Model
|
49
|
-
#
|
50
|
-
# attributes first_name: "FstName", last_name: "LstName"
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
# p = Person.new
|
54
|
-
# p.first_name = "Jojo"
|
55
|
-
# p.attributes # => { "FstName" => "Jojo" }
|
56
|
-
#
|
57
|
-
def attributes(*attrs)
|
58
|
-
if attrs.length == 1 && attrs.first.kind_of?(Hash)
|
59
|
-
attrs.first.each { |from, to| _fmrest_define_attribute(from, to) }
|
60
|
-
else
|
61
|
-
attrs.each { |attr| _fmrest_define_attribute(attr, attr) }
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
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
|
71
|
-
#
|
72
|
-
# See: https://github.com/balvig/spyke/blob/master/lib/spyke/http.rb
|
73
|
-
#
|
74
|
-
def new_or_return(attributes_or_object, *_)
|
75
|
-
# In case of an existing Spyke object return it as is so that we
|
76
|
-
# don't accidentally remove dirty data from associations
|
77
|
-
return super if attributes_or_object.is_a?(::Spyke::Base)
|
78
|
-
super.tap do |record|
|
79
|
-
# In ActiveModel 4.x #clear_changes_information is a private
|
80
|
-
# method, so we need to call it with send() in that case, but
|
81
|
-
# keep calling it normally for AM5+
|
82
|
-
if record.respond_to?(:clear_changes_information)
|
83
|
-
record.clear_changes_information
|
84
|
-
else
|
85
|
-
record.send(:clear_changes_information)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def _fmrest_attribute_methods_container
|
91
|
-
@fmrest_attribute_methods_container ||= Module.new.tap { |mod| include mod }
|
92
|
-
end
|
93
|
-
|
94
|
-
def _fmrest_define_attribute(from, to)
|
95
|
-
# We use a setter here instead of injecting the hash key/value pair
|
96
|
-
# directly with #[]= so that we don't change the mapped_attributes
|
97
|
-
# hash on the parent class. The resulting hash is frozen for the
|
98
|
-
# same reason.
|
99
|
-
self.mapped_attributes = mapped_attributes.merge(from => to).freeze
|
100
|
-
|
101
|
-
_fmrest_attribute_methods_container.module_eval do
|
102
|
-
define_method(from) do
|
103
|
-
attribute(to)
|
104
|
-
end
|
105
|
-
|
106
|
-
define_method(:"#{from}=") do |value|
|
107
|
-
send("#{from}_will_change!") unless value == send(from)
|
108
|
-
set_attribute(to, value)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# Define ActiveModel::Dirty's methods
|
113
|
-
define_attribute_method(from)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# Spyke override -- Adds AM::Dirty support
|
118
|
-
#
|
119
|
-
def reload(*args)
|
120
|
-
super.tap { |r| clear_changes_information }
|
121
|
-
end
|
122
|
-
|
123
|
-
# Spyke override -- Adds AM::Dirty support
|
124
|
-
#
|
125
|
-
def save(*args)
|
126
|
-
super.tap { |r| changes_applied_after_save if r }
|
127
|
-
end
|
128
|
-
|
129
|
-
# Spyke override -- Adds support for forbidden attributes (i.e. Rails'
|
130
|
-
# `params.permit`, etc.)
|
131
|
-
#
|
132
|
-
def attributes=(new_attributes)
|
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))
|
136
|
-
end
|
137
|
-
|
138
|
-
private
|
139
|
-
|
140
|
-
def changed_params
|
141
|
-
attributes.to_params.slice(*mapped_changed)
|
142
|
-
end
|
143
|
-
|
144
|
-
def mapped_changed
|
145
|
-
mapped_attributes.values_at(*changed)
|
146
|
-
end
|
147
|
-
|
148
|
-
# Spyke override (private) -- Use known mapped_attributes for inspect
|
149
|
-
#
|
150
|
-
def inspect_attributes
|
151
|
-
mapped_attributes.except(primary_key).map do |k, v|
|
152
|
-
"#{k}: #{attribute(v).inspect}"
|
153
|
-
end.join(', ')
|
154
|
-
end
|
155
|
-
|
156
|
-
def changes_applied_after_save
|
157
|
-
changes_applied
|
158
|
-
portals.each(&:parent_changes_applied)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|