fmrest 0.9.0 → 0.12.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.
@@ -5,17 +5,18 @@ require "fmrest/string_date"
5
5
  module FmRest
6
6
  module V1
7
7
  class TypeCoercer < Faraday::Response::Middleware
8
- # We use this date to represent a time for consistency with ginjo-rfm
8
+ # We use this date to represent a FileMaker time for consistency with
9
+ # ginjo-rfm
9
10
  JULIAN_ZERO_DAY = "-4712/1/1"
10
11
 
11
12
  COERCE_HYBRID = [:hybrid, "hybrid", true].freeze
12
13
  COERCE_FULL = [:full, "full"].freeze
13
14
 
14
15
  # @param app [#call]
15
- # @param options [Hash]
16
- def initialize(app, options = FmRest.default_connection_settings)
16
+ # @param settings [FmRest::ConnectionSettings]
17
+ def initialize(app, settings)
17
18
  super(app)
18
- @options = options
19
+ @settings = settings
19
20
  end
20
21
 
21
22
  def on_complete(env)
@@ -30,15 +31,13 @@ module FmRest
30
31
  field_data = record["fieldData"] || record[:fieldData]
31
32
  portal_data = record["portalData"] || record[:portalData]
32
33
 
33
- # Build an enumerator that iterates over hashes of fields
34
- enum = Enumerator.new { |y| y << field_data }
35
- if portal_data
36
- portal_data.each_value do |portal_records|
37
- enum += portal_records.to_enum
34
+ coerce_fields(field_data)
35
+
36
+ portal_data.try(:each_value) do |portal_records|
37
+ portal_records.each do |pr|
38
+ coerce_fields(pr)
38
39
  end
39
40
  end
40
-
41
- enum.each { |hash| coerce_fields(hash) }
42
41
  end
43
42
  end
44
43
 
@@ -49,29 +48,49 @@ module FmRest
49
48
  next unless v.is_a?(String)
50
49
  next if k == "recordId" || k == :recordId || k == "modId" || k == :modId
51
50
 
52
- begin
53
- str_timestamp = datetime_class.strptime(v, datetime_format)
54
- hash[k] = str_timestamp
55
- next
56
- rescue ArgumentError
51
+ if quick_check_timestamp(v)
52
+ begin
53
+ hash[k] = coerce_timestamp(v)
54
+ next
55
+ rescue ArgumentError
56
+ end
57
57
  end
58
58
 
59
- begin
60
- str_date = date_class.strptime(v, date_format)
61
- hash[k] = str_date
62
- next
63
- rescue ArgumentError
59
+ if quick_check_date(v)
60
+ begin
61
+ hash[k] = date_class.strptime(v, date_strptime_format)
62
+ next
63
+ rescue ArgumentError
64
+ end
64
65
  end
65
66
 
66
- begin
67
- str_time = datetime_class.strptime("#{JULIAN_ZERO_DAY} #{v}", time_format)
68
- hash[k] = str_time
69
- next
70
- rescue ArgumentError
67
+ if quick_check_time(v)
68
+ begin
69
+ hash[k] = datetime_class.strptime("#{JULIAN_ZERO_DAY} #{v}", time_strptime_format)
70
+ next
71
+ rescue ArgumentError
72
+ end
71
73
  end
72
74
  end
73
75
  end
74
76
 
77
+ def coerce_timestamp(str)
78
+ str_timestamp = DateTime.strptime(str, datetime_strptime_format)
79
+
80
+ if local_timezone?
81
+ # Change the DateTime to the local timezone, keeping the same
82
+ # time and just modifying the timezone
83
+ offset = FmRest::V1.local_offset_for_datetime(str_timestamp)
84
+ str_timestamp = str_timestamp.new_offset(offset) - offset
85
+ end
86
+
87
+ if datetime_class == StringDateTime
88
+ str_timestamp = StringDateTime.new(str, str_timestamp)
89
+ end
90
+
91
+ str_timestamp
92
+ end
93
+
75
94
  def date_class
76
95
  @date_class ||=
77
96
  case coerce_dates
@@ -92,23 +111,79 @@ module FmRest
92
111
  end
93
112
  end
94
113
 
95
- def date_format
96
- @date_format ||=
97
- FmRest::V1.convert_date_time_format(@options[:date_format] || DEFAULT_DATE_FORMAT)
114
+ def date_fm_format
115
+ @settings.date_format
116
+ end
117
+
118
+ def timestamp_fm_format
119
+ @settings.timestamp_format
120
+ end
121
+
122
+ def time_fm_format
123
+ @settings.time_format
98
124
  end
99
125
 
100
- def datetime_format
101
- @datetime_format ||=
102
- FmRest::V1.convert_date_time_format(@options[:timestamp_format] || DEFAULT_TIMESTAMP_FORMAT)
126
+ def date_strptime_format
127
+ FmRest::V1.fm_date_to_strptime_format(date_fm_format)
128
+ end
129
+
130
+ def datetime_strptime_format
131
+ FmRest::V1.fm_date_to_strptime_format(timestamp_fm_format)
132
+ end
133
+
134
+ def time_strptime_format
135
+ @time_strptime_format ||=
136
+ "%Y/%m/%d " + FmRest::V1.fm_date_to_strptime_format(time_fm_format)
137
+ end
138
+
139
+ # We use a string length test, followed by regexp match test to try to
140
+ # identify date fields. Benchmarking shows this should be between 1 and 3
141
+ # orders of magnitude faster for fails (i.e. non-dates) than just using
142
+ # Date.strptime.
143
+ #
144
+ # user system total real
145
+ # strptime: 0.268496 0.000962 0.269458 ( 0.270865)
146
+ # re=~: 0.024872 0.000070 0.024942 ( 0.025057)
147
+ # re.match?: 0.019745 0.000095 0.019840 ( 0.020058)
148
+ # strptime fail: 0.141309 0.000354 0.141663 ( 0.142266)
149
+ # re=~ fail: 0.031637 0.000095 0.031732 ( 0.031872)
150
+ # re.match? fail: 0.011249 0.000056 0.011305 ( 0.011375)
151
+ # length fail: 0.007177 0.000024 0.007201 ( 0.007222)
152
+ #
153
+ # NOTE: The faster Regexp#match? was introduced in Ruby 2.4.0, so we
154
+ # can't really rely on it being available
155
+ if //.respond_to?(:match?)
156
+ def quick_check_timestamp(v)
157
+ v.length == timestamp_fm_format.length && FmRest::V1::fm_date_to_regexp(timestamp_fm_format).match?(v)
158
+ end
159
+
160
+ def quick_check_date(v)
161
+ v.length == date_fm_format.length && FmRest::V1::fm_date_to_regexp(date_fm_format).match?(v)
162
+ end
163
+
164
+ def quick_check_time(v)
165
+ v.length == time_fm_format.length && FmRest::V1::fm_date_to_regexp(time_fm_format).match?(v)
166
+ end
167
+ else
168
+ def quick_check_timestamp(v)
169
+ v.length == timestamp_fm_format.length && FmRest::V1::fm_date_to_regexp(timestamp_fm_format) =~ v
170
+ end
171
+
172
+ def quick_check_date(v)
173
+ v.length == date_fm_format.length && FmRest::V1::fm_date_to_regexp(date_fm_format) =~ v
174
+ end
175
+
176
+ def quick_check_time(v)
177
+ v.length == time_fm_format.length && FmRest::V1::fm_date_to_regexp(time_fm_format) =~ v
178
+ end
103
179
  end
104
180
 
105
- def time_format
106
- @time_format ||=
107
- "%Y/%m/%d " + FmRest::V1.convert_date_time_format(@options[:time_format] || DEFAULT_TIME_FORMAT)
181
+ def local_timezone?
182
+ @local_timezone ||= @settings.timezone.try(:to_sym) == :local
108
183
  end
109
184
 
110
185
  def coerce_dates
111
- @options.fetch(:coerce_dates, false)
186
+ @settings.coerce_dates
112
187
  end
113
188
 
114
189
  alias_method :enabled?, :coerce_dates
@@ -5,17 +5,6 @@ module FmRest
5
5
  module Utils
6
6
  VALID_SCRIPT_KEYS = [:prerequest, :presort, :after].freeze
7
7
 
8
- FM_DATETIME_FORMAT_MATCHER = /MM|mm|dd|HH|ss|yyyy/.freeze
9
-
10
- FM_DATETIME_FORMAT_SUBSTITUTIONS = {
11
- "MM" => "%m",
12
- "dd" => "%d",
13
- "yyyy" => "%Y",
14
- "HH" => "%H",
15
- "mm" => "%M",
16
- "ss" => "%S"
17
- }.freeze
18
-
19
8
  # Converts custom script options to a hash with the Data API's expected
20
9
  # JSON script format.
21
10
  #
@@ -83,12 +72,6 @@ module FmRest
83
72
  params
84
73
  end
85
74
 
86
- # Converts a FM date-time format to `Date.strptime` format
87
- #
88
- def convert_date_time_format(fm_format)
89
- fm_format.gsub(FM_DATETIME_FORMAT_MATCHER, FM_DATETIME_FORMAT_SUBSTITUTIONS)
90
- end
91
-
92
75
  private
93
76
 
94
77
  def convert_script_arguments(script_arguments, suffix = nil)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FmRest
4
- VERSION = "0.9.0"
4
+ VERSION = "0.12.0"
5
5
  end
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.9.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pedro Carbajal
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-17 00:00:00.000000000 Z
11
+ date: 2020-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -54,30 +54,30 @@ dependencies:
54
54
  name: bundler
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
- - - "~>"
57
+ - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: '1.16'
59
+ version: '0'
60
60
  type: :development
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
- - - "~>"
64
+ - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: '1.16'
66
+ version: '0'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: rake
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
- - - "~>"
71
+ - - ">="
72
72
  - !ruby/object:Gem::Version
73
- version: '10.0'
73
+ version: '0'
74
74
  type: :development
75
75
  prerelease: false
76
76
  version_requirements: !ruby/object:Gem::Requirement
77
77
  requirements:
78
- - - "~>"
78
+ - - ">="
79
79
  - !ruby/object:Gem::Version
80
- version: '10.0'
80
+ version: '0'
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: rspec
83
83
  requirement: !ruby/object:Gem::Requirement
@@ -98,14 +98,14 @@ dependencies:
98
98
  requirements:
99
99
  - - ">="
100
100
  - !ruby/object:Gem::Version
101
- version: '0'
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: '0'
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: '0'
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: '0'
164
+ version: 1.4.0
165
165
  - !ruby/object:Gem::Dependency
166
166
  name: mock_redis
167
167
  requirement: !ruby/object:Gem::Requirement
@@ -235,8 +235,10 @@ files:
235
235
  - LICENSE.txt
236
236
  - README.md
237
237
  - Rakefile
238
+ - UPGRADING
238
239
  - fmrest.gemspec
239
240
  - lib/fmrest.rb
241
+ - lib/fmrest/connection_settings.rb
240
242
  - lib/fmrest/errors.rb
241
243
  - lib/fmrest/spyke.rb
242
244
  - lib/fmrest/spyke/base.rb
@@ -250,6 +252,7 @@ files:
250
252
  - lib/fmrest/spyke/model/global_fields.rb
251
253
  - lib/fmrest/spyke/model/http.rb
252
254
  - lib/fmrest/spyke/model/orm.rb
255
+ - lib/fmrest/spyke/model/record_id.rb
253
256
  - lib/fmrest/spyke/model/serialization.rb
254
257
  - lib/fmrest/spyke/model/uri.rb
255
258
  - lib/fmrest/spyke/portal.rb
@@ -262,10 +265,13 @@ files:
262
265
  - lib/fmrest/token_store/base.rb
263
266
  - lib/fmrest/token_store/memory.rb
264
267
  - lib/fmrest/token_store/moneta.rb
268
+ - lib/fmrest/token_store/null.rb
265
269
  - lib/fmrest/token_store/redis.rb
266
270
  - lib/fmrest/v1.rb
271
+ - lib/fmrest/v1/auth.rb
267
272
  - lib/fmrest/v1/connection.rb
268
273
  - lib/fmrest/v1/container_fields.rb
274
+ - lib/fmrest/v1/dates.rb
269
275
  - lib/fmrest/v1/paths.rb
270
276
  - lib/fmrest/v1/raise_errors.rb
271
277
  - lib/fmrest/v1/token_session.rb
@@ -278,7 +284,22 @@ homepage: https://github.com/beezwax/fmrest-ruby
278
284
  licenses:
279
285
  - MIT
280
286
  metadata: {}
281
- 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
+
282
303
  rdoc_options: []
283
304
  require_paths:
284
305
  - lib
@@ -294,7 +315,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
294
315
  version: '0'
295
316
  requirements: []
296
317
  rubygems_version: 3.0.6
297
- signing_key:
318
+ signing_key:
298
319
  specification_version: 4
299
320
  summary: FileMaker Data API client using Faraday
300
321
  test_files: []
322
+ ...