ecoportal-api 0.8.5 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +20 -20
- data/.rspec +3 -3
- data/.rubocop.yml +55 -55
- data/.travis.yml +5 -5
- data/.yardopts +10 -10
- data/CHANGELOG.md +257 -236
- data/Gemfile +6 -6
- data/LICENSE +21 -21
- data/README.md +34 -34
- data/Rakefile +27 -27
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/ecoportal-api.gemspec +36 -36
- data/lib/ecoportal/api/common/base_class.rb +33 -29
- data/lib/ecoportal/api/common/base_model.rb +195 -177
- data/lib/ecoportal/api/common/batch_operation.rb +119 -119
- data/lib/ecoportal/api/common/batch_response.rb +34 -34
- data/lib/ecoportal/api/common/client.rb +198 -196
- data/lib/ecoportal/api/common/doc_helpers.rb +29 -29
- data/lib/ecoportal/api/common/elastic_apm_integration.rb +112 -112
- data/lib/ecoportal/api/common/hash_diff.rb +41 -41
- data/lib/ecoportal/api/common/logging.rb +12 -12
- data/lib/ecoportal/api/common/response.rb +31 -31
- data/lib/ecoportal/api/common/wrapped_response.rb +54 -54
- data/lib/ecoportal/api/common.rb +18 -18
- data/lib/ecoportal/api/errors/base.rb +8 -8
- data/lib/ecoportal/api/errors/time_out.rb +8 -8
- data/lib/ecoportal/api/errors.rb +9 -9
- data/lib/ecoportal/api/internal/account.rb +99 -100
- data/lib/ecoportal/api/internal/login_provider.rb +9 -9
- data/lib/ecoportal/api/internal/login_providers.rb +33 -33
- data/lib/ecoportal/api/internal/people.rb +14 -14
- data/lib/ecoportal/api/internal/permissions.rb +14 -13
- data/lib/ecoportal/api/internal/person.rb +101 -53
- data/lib/ecoportal/api/internal/person_details.rb +9 -9
- data/lib/ecoportal/api/internal/person_schema.rb +10 -10
- data/lib/ecoportal/api/internal/person_schemas.rb +11 -11
- data/lib/ecoportal/api/internal/policy_group.rb +9 -9
- data/lib/ecoportal/api/internal/policy_groups.rb +32 -32
- data/lib/ecoportal/api/internal/preferences.rb +31 -31
- data/lib/ecoportal/api/internal/schema_field.rb +8 -8
- data/lib/ecoportal/api/internal/schema_field_value.rb +8 -8
- data/lib/ecoportal/api/internal.rb +31 -31
- data/lib/ecoportal/api/logger.rb +62 -62
- data/lib/ecoportal/api/v1/people.rb +218 -218
- data/lib/ecoportal/api/v1/person.rb +138 -135
- data/lib/ecoportal/api/v1/person_details.rb +94 -82
- data/lib/ecoportal/api/v1/person_schema.rb +53 -53
- data/lib/ecoportal/api/v1/person_schemas.rb +48 -48
- data/lib/ecoportal/api/v1/schema_field.rb +34 -34
- data/lib/ecoportal/api/v1/schema_field_value.rb +65 -65
- data/lib/ecoportal/api/v1.rb +49 -49
- data/lib/ecoportal/api/version.rb +5 -5
- data/lib/ecoportal/api.rb +16 -16
- metadata +3 -3
data/Rakefile
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
2
|
-
require "rspec/core/rake_task"
|
3
|
-
require "yard"
|
4
|
-
require "redcarpet"
|
5
|
-
|
6
|
-
desc "run the specs"
|
7
|
-
RSpec::Core::RakeTask.new(:spec)
|
8
|
-
|
9
|
-
desc "run rspec showing backtrace"
|
10
|
-
RSpec::Core::RakeTask.new(:spec_trace) do |task|
|
11
|
-
task.rspec_opts = ['--backtrace']
|
12
|
-
end
|
13
|
-
|
14
|
-
desc "run rspec stopping on first fail, and show backtrace"
|
15
|
-
RSpec::Core::RakeTask.new(:spec_fast) do |task|
|
16
|
-
task.rspec_opts = ['--fail-fast', '--backtrace']
|
17
|
-
end
|
18
|
-
|
19
|
-
# default task name is yard
|
20
|
-
desc "Yard: generate all the documentation"
|
21
|
-
YARD::Rake::YardocTask.new(:doc) do |t|
|
22
|
-
#t.files = ['lib/**/*.rb']
|
23
|
-
end
|
24
|
-
|
25
|
-
task :default => [:spec]
|
26
|
-
task :rspec_trace => :spec_trace
|
27
|
-
task :rspec_fast => :spec_fast
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require "yard"
|
4
|
+
require "redcarpet"
|
5
|
+
|
6
|
+
desc "run the specs"
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
|
9
|
+
desc "run rspec showing backtrace"
|
10
|
+
RSpec::Core::RakeTask.new(:spec_trace) do |task|
|
11
|
+
task.rspec_opts = ['--backtrace']
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "run rspec stopping on first fail, and show backtrace"
|
15
|
+
RSpec::Core::RakeTask.new(:spec_fast) do |task|
|
16
|
+
task.rspec_opts = ['--fail-fast', '--backtrace']
|
17
|
+
end
|
18
|
+
|
19
|
+
# default task name is yard
|
20
|
+
desc "Yard: generate all the documentation"
|
21
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
22
|
+
#t.files = ['lib/**/*.rb']
|
23
|
+
end
|
24
|
+
|
25
|
+
task :default => [:spec]
|
26
|
+
task :rspec_trace => :spec_trace
|
27
|
+
task :rspec_fast => :spec_fast
|
data/bin/console
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "ecoportal/api"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "pry"
|
14
|
-
Pry.start(__FILE__)
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ecoportal/api"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "pry"
|
14
|
+
Pry.start(__FILE__)
|
data/bin/setup
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
#!/usr/bin/env bash
|
2
|
-
set -euo pipefail
|
3
|
-
IFS=$'\n\t'
|
4
|
-
set -vx
|
5
|
-
|
6
|
-
bundle install
|
7
|
-
|
8
|
-
# Do any other automated setup that you need to do here
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
set -euo pipefail
|
3
|
+
IFS=$'\n\t'
|
4
|
+
set -vx
|
5
|
+
|
6
|
+
bundle install
|
7
|
+
|
8
|
+
# Do any other automated setup that you need to do here
|
data/ecoportal-api.gemspec
CHANGED
@@ -1,36 +1,36 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "ecoportal/api/version"
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "ecoportal-api"
|
8
|
-
spec.version = Ecoportal::API::VERSION
|
9
|
-
spec.authors = ["Tapio Saarinen"]
|
10
|
-
spec.email = ["tapio@ecoportal.co.nz", "rien@ecoportal.co.nz", "oscar@ecoportal.co.nz", "bozydar@ecoportal.co.nz"]
|
11
|
-
|
12
|
-
spec.summary = %q{A collection of helpers for interacting with the ecoPortal MS's various APIs}
|
13
|
-
spec.homepage = "https://www.ecoportal.com"
|
14
|
-
spec.licenses = %w[MIT]
|
15
|
-
|
16
|
-
spec.required_ruby_version = '>= 2.4.4'
|
17
|
-
|
18
|
-
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
-
f.match(%r{^(test|spec|features)/})
|
20
|
-
end
|
21
|
-
spec.bindir = "exe"
|
22
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
-
spec.require_paths = ["lib"]
|
24
|
-
|
25
|
-
spec.add_development_dependency "bundler", ">= 2.2.17", "< 2.3"
|
26
|
-
spec.add_development_dependency "rspec", ">= 3.10.0", "< 3.11"
|
27
|
-
spec.add_development_dependency "rake", ">= 13.0.3", "< 13.1"
|
28
|
-
spec.add_development_dependency "yard", ">= 0.9.26", "< 0.10"
|
29
|
-
spec.add_development_dependency "redcarpet", ">= 3.5.1", "< 3.6"
|
30
|
-
spec.add_development_dependency "pry" , "~> 0.14"
|
31
|
-
|
32
|
-
spec.add_dependency 'http', '~> 4.4.1', "< 5"
|
33
|
-
spec.add_dependency 'dotenv', '>= 2.7.6', "< 2.8"
|
34
|
-
spec.add_dependency 'elastic-apm', '>= 4.0.0', "< 4.1"
|
35
|
-
spec.add_dependency 'hash-polyfill', '~> 0'
|
36
|
-
end
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "ecoportal/api/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ecoportal-api"
|
8
|
+
spec.version = Ecoportal::API::VERSION
|
9
|
+
spec.authors = ["Tapio Saarinen"]
|
10
|
+
spec.email = ["tapio@ecoportal.co.nz", "rien@ecoportal.co.nz", "oscar@ecoportal.co.nz", "bozydar@ecoportal.co.nz"]
|
11
|
+
|
12
|
+
spec.summary = %q{A collection of helpers for interacting with the ecoPortal MS's various APIs}
|
13
|
+
spec.homepage = "https://www.ecoportal.com"
|
14
|
+
spec.licenses = %w[MIT]
|
15
|
+
|
16
|
+
spec.required_ruby_version = '>= 2.4.4'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", ">= 2.2.17", "< 2.3"
|
26
|
+
spec.add_development_dependency "rspec", ">= 3.10.0", "< 3.11"
|
27
|
+
spec.add_development_dependency "rake", ">= 13.0.3", "< 13.1"
|
28
|
+
spec.add_development_dependency "yard", ">= 0.9.26", "< 0.10"
|
29
|
+
spec.add_development_dependency "redcarpet", ">= 3.5.1", "< 3.6"
|
30
|
+
spec.add_development_dependency "pry" , "~> 0.14"
|
31
|
+
|
32
|
+
spec.add_dependency 'http', '~> 4.4.1', "< 5"
|
33
|
+
spec.add_dependency 'dotenv', '>= 2.7.6', "< 2.8"
|
34
|
+
spec.add_dependency 'elastic-apm', '>= 4.0.0', "< 4.1"
|
35
|
+
spec.add_dependency 'hash-polyfill', '~> 0'
|
36
|
+
end
|
@@ -1,29 +1,33 @@
|
|
1
|
-
module Ecoportal
|
2
|
-
module API
|
3
|
-
module Common
|
4
|
-
module BaseClass
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
when
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
1
|
+
module Ecoportal
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module BaseClass
|
5
|
+
def redef_without_warning(const, value)
|
6
|
+
self.class.send(:remove_const, const) if self.class.const_defined?(const)
|
7
|
+
self.class.const_set(const, value)
|
8
|
+
end
|
9
|
+
|
10
|
+
def class_resolver(name, klass)
|
11
|
+
define_singleton_method(name) { resolve_class(klass) }
|
12
|
+
define_method(name) { self.class.resolve_class(klass) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def resolve_class(klass)
|
16
|
+
@resolved ||= {}
|
17
|
+
@resolved[klass] ||=
|
18
|
+
case klass
|
19
|
+
when Class
|
20
|
+
klass
|
21
|
+
when String
|
22
|
+
Kernel.const_get(klass)
|
23
|
+
when Symbol
|
24
|
+
resolve_class(self.send(klass))
|
25
|
+
else
|
26
|
+
raise "Unknown class: #{klass}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,177 +1,195 @@
|
|
1
|
-
module Ecoportal
|
2
|
-
module API
|
3
|
-
module Common
|
4
|
-
class BaseModel
|
5
|
-
class UnlinkedModel < Exception
|
6
|
-
def initialize (msg = "Something went wrong when linking the document.", from: nil, key: nil)
|
7
|
-
msg += " From: #{from}." if from
|
8
|
-
msg += " key: #{key}." if key
|
9
|
-
super(msg)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
extend BaseClass
|
14
|
-
|
15
|
-
class << self
|
16
|
-
def passthrough(*methods, to: :doc)
|
17
|
-
methods.each do |method|
|
18
|
-
method = method.to_s
|
19
|
-
define_method method do
|
20
|
-
send(to)[method]
|
21
|
-
end
|
22
|
-
define_method "#{method}=" do |value|
|
23
|
-
send(to)[method] = value
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def embeds_one(method, key: method, nullable: false, klass:)
|
29
|
-
method = method.to_s.freeze
|
30
|
-
var = "@#{method}".freeze
|
31
|
-
key = key.to_s.freeze
|
32
|
-
define_method(method) do
|
33
|
-
if instance_variable_defined?(var)
|
34
|
-
value = instance_variable_get(var)
|
35
|
-
return value unless nullable
|
36
|
-
return value if (value && doc[key]) || (!value && !doc[key])
|
37
|
-
remove_instance_variable(var)
|
38
|
-
end
|
39
|
-
doc[key] ||= {} unless nullable
|
40
|
-
return instance_variable_set(var, nil) unless doc[key]
|
41
|
-
|
42
|
-
self.class.resolve_class(klass).new(
|
43
|
-
doc[key], parent: self, key: key
|
44
|
-
).tap {|obj| instance_variable_set(var, obj)}
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
attr_reader :_parent, :_key
|
51
|
-
|
52
|
-
def initialize(doc = {}, parent: self, key: nil)
|
53
|
-
@_parent = parent
|
54
|
-
@_key = key
|
55
|
-
if !_parent || !_key
|
56
|
-
@doc = doc
|
57
|
-
@original_doc = JSON.parse(@doc.to_json)
|
58
|
-
@initial_doc = JSON.parse(@doc.to_json)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def doc
|
63
|
-
raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
|
64
|
-
return @doc if is_root?
|
65
|
-
_parent.doc.dig(*[_key].flatten)
|
66
|
-
end
|
67
|
-
|
68
|
-
def original_doc
|
69
|
-
raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked?
|
70
|
-
return @original_doc if is_root?
|
71
|
-
_parent.original_doc&.dig(*[_key].flatten)
|
72
|
-
end
|
73
|
-
|
74
|
-
def initial_doc
|
75
|
-
raise UnlinkedModel.new(from: "#{self.class}#initial_doc", key: _key) unless linked?
|
76
|
-
return @initial_doc if is_root?
|
77
|
-
_parent.initial_doc&.dig(*[_key].flatten)
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
def
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
1
|
+
module Ecoportal
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
class BaseModel
|
5
|
+
class UnlinkedModel < Exception
|
6
|
+
def initialize (msg = "Something went wrong when linking the document.", from: nil, key: nil)
|
7
|
+
msg += " From: #{from}." if from
|
8
|
+
msg += " key: #{key}." if key
|
9
|
+
super(msg)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
extend BaseClass
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def passthrough(*methods, to: :doc)
|
17
|
+
methods.each do |method|
|
18
|
+
method = method.to_s
|
19
|
+
define_method method do
|
20
|
+
send(to)[method]
|
21
|
+
end
|
22
|
+
define_method "#{method}=" do |value|
|
23
|
+
send(to)[method] = value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def embeds_one(method, key: method, nullable: false, klass:)
|
29
|
+
method = method.to_s.freeze
|
30
|
+
var = "@#{method}".freeze
|
31
|
+
key = key.to_s.freeze
|
32
|
+
define_method(method) do
|
33
|
+
if instance_variable_defined?(var)
|
34
|
+
value = instance_variable_get(var)
|
35
|
+
return value unless nullable
|
36
|
+
return value if (value && doc[key]) || (!value && !doc[key])
|
37
|
+
remove_instance_variable(var)
|
38
|
+
end
|
39
|
+
doc[key] ||= {} unless nullable
|
40
|
+
return instance_variable_set(var, nil) unless doc[key]
|
41
|
+
|
42
|
+
self.class.resolve_class(klass).new(
|
43
|
+
doc[key], parent: self, key: key
|
44
|
+
).tap {|obj| instance_variable_set(var, obj)}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :_parent, :_key
|
51
|
+
|
52
|
+
def initialize(doc = {}, parent: self, key: nil)
|
53
|
+
@_parent = parent
|
54
|
+
@_key = key
|
55
|
+
if !_parent || !_key
|
56
|
+
@doc = doc
|
57
|
+
@original_doc = JSON.parse(@doc.to_json)
|
58
|
+
@initial_doc = JSON.parse(@doc.to_json)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def doc
|
63
|
+
raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
|
64
|
+
return @doc if is_root?
|
65
|
+
_parent.doc.dig(*[_key].flatten)
|
66
|
+
end
|
67
|
+
|
68
|
+
def original_doc
|
69
|
+
raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked?
|
70
|
+
return @original_doc if is_root?
|
71
|
+
_parent.original_doc&.dig(*[_key].flatten)
|
72
|
+
end
|
73
|
+
|
74
|
+
def initial_doc
|
75
|
+
raise UnlinkedModel.new(from: "#{self.class}#initial_doc", key: _key) unless linked?
|
76
|
+
return @initial_doc if is_root?
|
77
|
+
_parent.initial_doc&.dig(*[_key].flatten)
|
78
|
+
end
|
79
|
+
|
80
|
+
# It replaces `doc` by `new_doc`
|
81
|
+
# @return [Hash] `doc` before change
|
82
|
+
def replace_doc!(new_doc)
|
83
|
+
raise UnlinkedModel.new(from: "#{self.class}#replace_doc", key: _key) unless linked?
|
84
|
+
@doc.tap do
|
85
|
+
@doc = new_doc
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# It replaces `original_doc` by `new_doc`
|
90
|
+
# @return [Hash] `original_doc` before change
|
91
|
+
def replace_original_doc!(new_doc)
|
92
|
+
raise UnlinkedModel.new(from: "#{self.class}#replace_original_doc", key: _key) unless linked?
|
93
|
+
@original_doc.tap do
|
94
|
+
@original_doc = new_doc
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def as_json
|
99
|
+
doc
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_json(*args)
|
103
|
+
doc.to_json(*args)
|
104
|
+
end
|
105
|
+
|
106
|
+
def as_update(ref = :last, ignore: [])
|
107
|
+
new_doc = as_json
|
108
|
+
ref_doc = ref == :total ? initial_doc : original_doc
|
109
|
+
Common::HashDiff.diff(new_doc, ref_doc, ignore: ignore)
|
110
|
+
end
|
111
|
+
|
112
|
+
def dirty?
|
113
|
+
as_update != {}
|
114
|
+
end
|
115
|
+
|
116
|
+
# It consolidates all the changes carried by `doc` by setting it as `original_doc`.
|
117
|
+
def consolidate!
|
118
|
+
raise UnlinkedModel.new(from: "#{self.class}#consolidate!", key: _key) unless linked?
|
119
|
+
new_doc = JSON.parse(doc.to_json)
|
120
|
+
if is_root?
|
121
|
+
@original_doc = new_doc
|
122
|
+
else
|
123
|
+
dig_set(_parent.original_doc, [_key].flatten, new_doc)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# It removes all the changes carried by `doc` by restoring `original_doc` into `doc`.
|
128
|
+
# @note
|
129
|
+
# 1. When there are nullable properties, it may be required to apply `reset!` from the parent
|
130
|
+
# i.e. `parent.reset!("child")` # when parent.child is `nil`
|
131
|
+
# 2. In such a case, only immediate childs are allowed to be reset
|
132
|
+
# @param key [String, Array<String>, nil] if given, it only resets the specified property
|
133
|
+
def reset!(key = nil)
|
134
|
+
raise "'key' should be a String. Given #{key}" unless !key || key.is_a?(String)
|
135
|
+
raise UnlinkedModel.new(from: "#{self.class}#reset!", key: _key) unless linked?
|
136
|
+
|
137
|
+
if key
|
138
|
+
if self.respond_to?(key) && child = self.send(key) && child.is_a?(Ecoportal::API::Common::BaseModel)
|
139
|
+
child.reset!
|
140
|
+
else
|
141
|
+
new_doc = original_doc && original_doc[key]
|
142
|
+
dig_set(doc, [key], new_doc && JSON.parse(new_doc.to_json))
|
143
|
+
# regenerate object if new_doc is null
|
144
|
+
self.send(key) if !new_doc && self.respond_to?(key)
|
145
|
+
end
|
146
|
+
else
|
147
|
+
new_doc = JSON.parse(original_doc.to_json)
|
148
|
+
if is_root?
|
149
|
+
@doc = new_doc
|
150
|
+
else
|
151
|
+
dig_set(_parent.doc, [_key].flatten, new_doc)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def print_pretty
|
157
|
+
puts JSON.pretty_generate(as_json)
|
158
|
+
self
|
159
|
+
end
|
160
|
+
|
161
|
+
protected
|
162
|
+
|
163
|
+
def is_root?
|
164
|
+
_parent == self && !!defined?(@doc)
|
165
|
+
end
|
166
|
+
|
167
|
+
def linked?
|
168
|
+
is_root? || !!_parent.doc.dig(*[_key].flatten)
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def dig_set(obj, keys, value)
|
174
|
+
if keys.length == 1
|
175
|
+
obj[keys.first] = value
|
176
|
+
else
|
177
|
+
dig_set(obj[keys.first], keys.slice(1..-1), value)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def set_uniq_array_keep_order(key, value)
|
182
|
+
unless value.is_a?(Array)
|
183
|
+
raise "#{key}= needs to be passed an Array, got #{value.class}"
|
184
|
+
end
|
185
|
+
ini_vals = (original_doc && original_doc[key]) || []
|
186
|
+
|
187
|
+
value = value.uniq
|
188
|
+
# preserve original order to avoid false updates
|
189
|
+
doc[key] = ((ini_vals & value) + (value - ini_vals)).compact
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|