fmrest 0.12.0 → 0.13.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/.yardopts +2 -0
- data/CHANGELOG.md +11 -0
- data/README.md +175 -809
- metadata +61 -109
- 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
|