fmrest 0.10.1 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -0
  3. data/CHANGELOG.md +36 -0
  4. data/README.md +193 -761
  5. metadata +71 -98
  6. data/.gitignore +0 -26
  7. data/.rspec +0 -3
  8. data/.travis.yml +0 -5
  9. data/Gemfile +0 -3
  10. data/Rakefile +0 -6
  11. data/fmrest.gemspec +0 -38
  12. data/lib/fmrest.rb +0 -29
  13. data/lib/fmrest/errors.rb +0 -28
  14. data/lib/fmrest/spyke.rb +0 -21
  15. data/lib/fmrest/spyke/base.rb +0 -23
  16. data/lib/fmrest/spyke/container_field.rb +0 -59
  17. data/lib/fmrest/spyke/model.rb +0 -36
  18. data/lib/fmrest/spyke/model/associations.rb +0 -82
  19. data/lib/fmrest/spyke/model/attributes.rb +0 -171
  20. data/lib/fmrest/spyke/model/auth.rb +0 -35
  21. data/lib/fmrest/spyke/model/connection.rb +0 -74
  22. data/lib/fmrest/spyke/model/container_fields.rb +0 -25
  23. data/lib/fmrest/spyke/model/global_fields.rb +0 -40
  24. data/lib/fmrest/spyke/model/http.rb +0 -37
  25. data/lib/fmrest/spyke/model/orm.rb +0 -212
  26. data/lib/fmrest/spyke/model/serialization.rb +0 -91
  27. data/lib/fmrest/spyke/model/uri.rb +0 -30
  28. data/lib/fmrest/spyke/portal.rb +0 -55
  29. data/lib/fmrest/spyke/relation.rb +0 -359
  30. data/lib/fmrest/spyke/spyke_formatter.rb +0 -273
  31. data/lib/fmrest/spyke/validation_error.rb +0 -25
  32. data/lib/fmrest/string_date.rb +0 -220
  33. data/lib/fmrest/token_store.rb +0 -6
  34. data/lib/fmrest/token_store/active_record.rb +0 -74
  35. data/lib/fmrest/token_store/base.rb +0 -25
  36. data/lib/fmrest/token_store/memory.rb +0 -26
  37. data/lib/fmrest/token_store/moneta.rb +0 -41
  38. data/lib/fmrest/token_store/redis.rb +0 -45
  39. data/lib/fmrest/v1.rb +0 -21
  40. data/lib/fmrest/v1/connection.rb +0 -91
  41. data/lib/fmrest/v1/container_fields.rb +0 -114
  42. data/lib/fmrest/v1/dates.rb +0 -81
  43. data/lib/fmrest/v1/paths.rb +0 -47
  44. data/lib/fmrest/v1/raise_errors.rb +0 -57
  45. data/lib/fmrest/v1/token_session.rb +0 -142
  46. data/lib/fmrest/v1/token_store/active_record.rb +0 -13
  47. data/lib/fmrest/v1/token_store/memory.rb +0 -13
  48. data/lib/fmrest/v1/type_coercer.rb +0 -192
  49. data/lib/fmrest/v1/utils.rb +0 -95
  50. data/lib/fmrest/version.rb +0 -5
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fmrest/spyke/container_field"
4
-
5
- module FmRest
6
- module Spyke
7
- module Model
8
- module ContainerFields
9
- extend ::ActiveSupport::Concern
10
-
11
- class_methods do
12
- def container(name, options = {})
13
- field_name = options[:field_name] || name
14
-
15
- define_method(name) do
16
- @container_fields ||= {}
17
- @container_fields[name.to_sym] ||= ContainerField.new(self, field_name)
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
25
-
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FmRest
4
- module Spyke
5
- module Model
6
- module GlobalFields
7
- extend ::ActiveSupport::Concern
8
-
9
- FULLY_QUALIFIED_FIELD_NAME_MATCHER = /\A[^:]+::[^:]+\Z/.freeze
10
-
11
- class_methods do
12
- def set_globals(values_hash)
13
- connection.patch(FmRest::V1.globals_path, {
14
- globalFields: normalize_globals_hash(values_hash)
15
- })
16
- end
17
-
18
- private
19
-
20
- def normalize_globals_hash(hash)
21
- hash.each_with_object({}) do |(k, v), normalized|
22
- if v.kind_of?(Hash)
23
- v.each do |k2, v2|
24
- normalized["#{k}::#{k2}"] = v2
25
- end
26
- next
27
- end
28
-
29
- unless FULLY_QUALIFIED_FIELD_NAME_MATCHER === k.to_s
30
- raise ArgumentError, "global fields must be given in fully qualified format (table name::field name)"
31
- end
32
-
33
- normalized[k] = v
34
- end
35
- end
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fmrest/spyke/relation"
4
-
5
- module FmRest
6
- module Spyke
7
- module Model
8
- module Http
9
- extend ::ActiveSupport::Concern
10
-
11
- class_methods do
12
-
13
- # Override Spyke's request method to keep a thread-local copy of the
14
- # last request's metadata, so that we can access things like script
15
- # execution results after a save, etc.
16
-
17
-
18
- def request(*args)
19
- super.tap do |r|
20
- Thread.current[last_request_metadata_key] = r.metadata
21
- end
22
- end
23
-
24
- def last_request_metadata(key: last_request_metadata_key)
25
- Thread.current[key]
26
- end
27
-
28
- private
29
-
30
- def last_request_metadata_key
31
- "#{to_s}.last_request_metadata"
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,212 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fmrest/spyke/relation"
4
- require "fmrest/spyke/validation_error"
5
-
6
- module FmRest
7
- module Spyke
8
- module Model
9
- module Orm
10
- extend ::ActiveSupport::Concern
11
-
12
- included do
13
- # Allow overriding FM's default limit (by default it's 100)
14
- class_attribute :default_limit, instance_accessor: false, instance_predicate: false
15
-
16
- class_attribute :default_sort, instance_accessor: false, instance_predicate: false
17
-
18
- # Whether to raise an FmRest::APIError::NoMatchingRecordsError when a
19
- # _find request has no results
20
- class_attribute :raise_on_no_matching_records, instance_accessor: false, instance_predicate: false
21
- end
22
-
23
- class_methods do
24
- # Methods delegated to FmRest::Spyke::Relation
25
- delegate :limit, :offset, :sort, :order, :query, :omit, :portal,
26
- :portals, :includes, :with_all_portals, :without_portals,
27
- :script, :find_one, :first, :any, :find_some,
28
- :find_in_batches, :find_each, to: :all
29
-
30
- def all
31
- # Use FmRest's Relation instead of Spyke's vanilla one
32
- current_scope || Relation.new(self, uri: uri)
33
- end
34
-
35
- # Extended fetch to allow properly setting limit, offset and other
36
- # options, as well as using the appropriate HTTP method/URL depending
37
- # on whether there's a query present in the current scope, e.g.:
38
- #
39
- # Person.query(first_name: "Stefan").fetch # POST .../_find
40
- #
41
- def fetch
42
- if current_scope.has_query?
43
- scope = extend_scope_with_fm_params(current_scope, prefixed: false)
44
- scope = scope.where(query: scope.query_params)
45
- scope = scope.with(FmRest::V1::find_path(layout))
46
- else
47
- scope = extend_scope_with_fm_params(current_scope, prefixed: true)
48
- end
49
-
50
- previous, self.current_scope = current_scope, scope
51
-
52
- # The DAPI returns a 401 "No records match the request" error when
53
- # nothing matches a _find request, so we need to catch it in order
54
- # to provide sane behavior (i.e. return an empty resultset)
55
- begin
56
- current_scope.has_query? ? scoped_request(:post) : super
57
- rescue FmRest::APIError::NoMatchingRecordsError => e
58
- raise e if raise_on_no_matching_records
59
- ::Spyke::Result.new({})
60
- end
61
- ensure
62
- self.current_scope = previous
63
- end
64
-
65
- # API-error-raising version of #create
66
- #
67
- def create!(attributes = {})
68
- new(attributes).tap(&:save!)
69
- end
70
-
71
- def execute_script(script_name, param: nil)
72
- params = {}
73
- params = {"script.param" => param} unless param.nil?
74
- request(:get, FmRest::V1::script_path(layout, script_name), params)
75
- end
76
-
77
- private
78
-
79
- def extend_scope_with_fm_params(scope, prefixed: false)
80
- prefix = prefixed ? "_" : nil
81
-
82
- where_options = {}
83
-
84
- where_options["#{prefix}limit"] = scope.limit_value if scope.limit_value
85
- where_options["#{prefix}offset"] = scope.offset_value if scope.offset_value
86
-
87
- if scope.sort_params.present?
88
- where_options["#{prefix}sort"] =
89
- prefixed ? scope.sort_params.to_json : scope.sort_params
90
- end
91
-
92
- unless scope.included_portals.nil?
93
- where_options["portal"] =
94
- prefixed ? scope.included_portals.to_json : scope.included_portals
95
- end
96
-
97
- if scope.portal_params.present?
98
- scope.portal_params.each do |portal_param, value|
99
- where_options["#{prefix}#{portal_param}"] = value
100
- end
101
- end
102
-
103
- if scope.script_params.present?
104
- where_options.merge!(scope.script_params)
105
- end
106
-
107
- scope.where(where_options)
108
- end
109
- end
110
-
111
- # Overwrite Spyke's save to provide a number of features:
112
- #
113
- # * Validations
114
- # * Data API scripts execution
115
- # * Refresh of dirty attributes
116
- #
117
- def save(options = {})
118
- callback = persisted? ? :update : :create
119
-
120
- return false unless perform_save_validations(callback, options)
121
- return false unless perform_save_persistence(callback, options)
122
-
123
- true
124
- end
125
-
126
- def save!(options = {})
127
- save(options.merge(raise_validation_errors: true))
128
- end
129
-
130
- # Overwrite Spyke's destroy to provide Data API script execution
131
- #
132
- def destroy(options = {})
133
- # For whatever reason the Data API wants the script params as query
134
- # string params for DELETE requests, making this more complicated
135
- # than it should be
136
- script_query_string = if options.has_key?(:script)
137
- "?" + Faraday::Utils.build_query(FmRest::V1.convert_script_params(options[:script]))
138
- else
139
- ""
140
- end
141
-
142
- self.attributes = delete(uri.to_s + script_query_string)
143
- end
144
-
145
- # API-error-raising version of #update
146
- #
147
- def update!(new_attributes, options = {})
148
- self.attributes = new_attributes
149
- save!(options)
150
- end
151
-
152
- def reload(options = {})
153
- scope = self.class
154
- scope = scope.script(options[:script]) if options.has_key?(:script)
155
- reloaded = scope.find(id)
156
- self.attributes = reloaded.attributes
157
- self.mod_id = reloaded.mod_id
158
- end
159
-
160
- # ActiveModel 5+ implements this method, so we only needed if we're in
161
- # the older AM4
162
- if ActiveModel::VERSION::MAJOR == 4
163
- def validate!(context = nil)
164
- valid?(context) || raise_validation_error
165
- end
166
- end
167
-
168
- private
169
-
170
- def perform_save_validations(context, options)
171
- return true if options[:validate] == false
172
- options[:raise_validation_errors] ? validate!(context) : validate(context)
173
- end
174
-
175
- def perform_save_persistence(callback, options)
176
- run_callbacks :save do
177
- run_callbacks(callback) do
178
-
179
- begin
180
- send self.class.method_for(callback), build_params_for_save(options)
181
-
182
- rescue APIError::ValidationError => e
183
- if options[:raise_validation_errors]
184
- raise e
185
- else
186
- return false
187
- end
188
- end
189
-
190
- end
191
- end
192
-
193
- true
194
- end
195
-
196
- def build_params_for_save(options)
197
- to_params.tap do |params|
198
- if options.has_key?(:script)
199
- params.merge!(FmRest::V1.convert_script_params(options[:script]))
200
- end
201
- end
202
- end
203
-
204
- # Overwrite ActiveModel's raise_validation_error to use our own class
205
- #
206
- def raise_validation_error # :doc:
207
- raise(ValidationError.new(self))
208
- end
209
- end
210
- end
211
- end
212
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FmRest
4
- module Spyke
5
- module Model
6
- module Serialization
7
- FM_DATE_FORMAT = "%m/%d/%Y"
8
- FM_DATETIME_FORMAT = "#{FM_DATE_FORMAT} %H:%M:%S"
9
-
10
- # Override Spyke's to_params to return FM Data API's expected JSON
11
- # format, and including only modified fields
12
- #
13
- def to_params
14
- params = {
15
- fieldData: serialize_values!(changed_params_not_embedded_in_url)
16
- }
17
-
18
- params[:modId] = mod_id if mod_id
19
-
20
- portal_data = serialize_portals
21
- params[:portalData] = portal_data unless portal_data.empty?
22
-
23
- params
24
- end
25
-
26
- protected
27
-
28
- def serialize_for_portal(portal)
29
- params =
30
- changed_params.except(:id).transform_keys do |key|
31
- "#{portal.attribute_prefix}::#{key}"
32
- end
33
-
34
- params[:recordId] = id if id
35
- params[:modId] = mod_id if mod_id
36
-
37
- serialize_values!(params)
38
- end
39
-
40
- private
41
-
42
- def serialize_portals
43
- portal_data = {}
44
-
45
- portals.each do |portal|
46
- portal.each do |portal_record|
47
- next unless portal_record.changed?
48
- portal_params = portal_data[portal.portal_key] ||= []
49
- portal_params << portal_record.serialize_for_portal(portal)
50
- end
51
- end
52
-
53
- portal_data
54
- end
55
-
56
- def changed_params_not_embedded_in_url
57
- params_not_embedded_in_url.slice(*mapped_changed)
58
- end
59
-
60
- # Modifies the given hash in-place encoding non-string values (e.g.
61
- # dates) to their string representation when appropriate.
62
- #
63
- def serialize_values!(params)
64
- params.transform_values! do |value|
65
- case value
66
- when DateTime, Time, FmRest::StringDateTime
67
- convert_datetime_timezone(value.to_datetime).strftime(FM_DATETIME_FORMAT)
68
- when Date, FmRest::StringDate
69
- value.strftime(FM_DATE_FORMAT)
70
- else
71
- value
72
- end
73
- end
74
-
75
- params
76
- end
77
-
78
- def convert_datetime_timezone(dt)
79
- case fmrest_config.fetch(:timezone, nil)
80
- when :utc, "utc"
81
- dt.new_offset(0)
82
- when :local, "local"
83
- dt.new_offset(FmRest::V1.local_offset_for_datetime(dt))
84
- when nil
85
- dt
86
- end
87
- end
88
- end
89
- end
90
- end
91
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FmRest
4
- module Spyke
5
- module Model
6
- module Uri
7
- extend ::ActiveSupport::Concern
8
-
9
- class_methods do
10
- # Accessor for FM layout (helps with building the URI)
11
- #
12
- def layout(layout = nil)
13
- @layout = layout if layout
14
- @layout ||= model_name.name
15
- end
16
-
17
- # Extend uri acccessor to default to FM Data schema
18
- #
19
- def uri(uri_template = nil)
20
- if @uri.nil? && uri_template.nil?
21
- return FmRest::V1.record_path(layout) + "(/:id)"
22
- end
23
-
24
- super
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FmRest
4
- module Spyke
5
- # Extend Spyke's HasMany association with custom options
6
- #
7
- class Portal < ::Spyke::Associations::HasMany
8
- def initialize(*args)
9
- super
10
-
11
- # Portals are always embedded, so no special URI
12
- @options[:uri] = ""
13
- end
14
-
15
- def portal_key
16
- return @options[:portal_key] if @options[:portal_key]
17
- name
18
- end
19
-
20
- def attribute_prefix
21
- @options[:attribute_prefix] || portal_key
22
- end
23
-
24
- def parent_changes_applied
25
- each do |record|
26
- record.changes_applied
27
- # Saving portal data doesn't provide new modIds for the
28
- # portal records, so we clear them instead. We can still save
29
- # portal data without a mod_id (it's optional in FM Data API)
30
- record.mod_id = nil
31
- end
32
- end
33
-
34
- private
35
-
36
- # Spyke::Associations::HasMany#initialize calls primary_key to build the
37
- # default URI, which causes a NameError, so this is here just to prevent
38
- # that. We don't care what it returns as we override the URI with nil
39
- # anyway
40
- def primary_key; end
41
-
42
- # Make sure the association doesn't try to fetch records through URI
43
- def uri; nil; end
44
-
45
- def embedded_data
46
- parent.attributes[portal_key]
47
- end
48
-
49
- def add_to_parent(record)
50
- find_some << record
51
- record
52
- end
53
- end
54
- end
55
- end