fmrest 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 185e6173a3f500ea6fc8ad40df8f52ba538fdd8e3545537e947f19bfd79a3124
4
- data.tar.gz: 89f5c6774366599e1baeef57662af77a3ceb5174abd58e6fd562c2aa29b85384
3
+ metadata.gz: ab46f70bffcfea32ef2bfd5021f03fa654bb43134e22b8e22a20d9955afe9caf
4
+ data.tar.gz: 32ce4378ff291d2c41d9b255b032d4562d7544a477765748c87c49a655449405
5
5
  SHA512:
6
- metadata.gz: b7aa56e455da60fe7b571e2ce4d70ae118ca7ade57356cbdade2b0679f66d10e9d689a4bc3af5ddfb06dfc6d7395af35b0518c23077dbfb28a9e395b845e0841
7
- data.tar.gz: c764ee1dc1962b3f6264ed6a4f26f0b88dd5c69cb62bca93dd269398c3e0d350ced4177b2842ce24136d7d0b84cb9a4809ce23ca1965b923fb91fc7b37ae39e1
6
+ metadata.gz: a68af2d8e53631b1d5c4d9e5e0849d224ff3918d1138d437463e30318ae81c36ca968d226f2f3631dbaa0d21b6287c1899e3b322a716729ad0d8b77aff80fd8d
7
+ data.tar.gz: ace50f913960e6b2edd190c8c1cbb75a0ebf15c0c3f0e8343720155f1c53b78bb9fd942be0a5e9bf2a2bc1ff729277f86360ffa4bba397ba48abe86c07db02ad
@@ -1,5 +1,9 @@
1
1
  ## Changelog
2
2
 
3
+ ### 0.7.0
4
+
5
+ * Added date coercion feature
6
+
3
7
  ### 0.6.0
4
8
 
5
9
  * Implemented session logout
data/README.md CHANGED
@@ -105,6 +105,22 @@ You can also pass a `:log` option for basic request logging, see the section on
105
105
  `:username` is also aliased as `:account_name` to provide cross-compatibility
106
106
  with the ginjo-rfm gem.
107
107
 
108
+ ### Full list of available options
109
+
110
+ Option | Description | Format | Default
111
+ --------------------|--------------------------------------------|-----------------------------|--------
112
+ `:host` | Hostname with optional port, e.g. `"example.com:9000"` | String | None
113
+ `:database` | | String | None
114
+ `:username` | | String | None
115
+ `:password` | | String | None
116
+ `:ssl` | SSL options to be forwarded to Faraday | Faraday SSL options | None
117
+ `:proxy` | Proxy options to be forwarded to Faraday | Faraday proxy options | None
118
+ `:log` | Log JSON responses to STDOUT | Boolean | `false`
119
+ `:coerce_dates` | See section on [date fields](#date-fields) | Boolean \| `:hybrid` \| `:full` | `false`
120
+ `:date_format` | Date parsing format | String (FM date format) | `"MM/dd/yyyy"`
121
+ `:timestamp_format` | Timestmap parsing format | String (FM date format) | `"MM/dd/yyyy HH:mm:ss"`
122
+ `:time_format` | Time parsing format | String (FM date format) | `"HH:mm:ss"`
123
+
108
124
  ### Default connection settings
109
125
 
110
126
  If you're only connecting to a single FM database you can configure it globally
@@ -122,6 +138,7 @@ FmRest.default_connection_settings = {
122
138
  This configuration will be used by default by `FmRest::V1.build_connection` as
123
139
  well as your models whenever you don't pass a configuration hash explicitly.
124
140
 
141
+
125
142
  ## Session token store
126
143
 
127
144
  By default fmrest-ruby will use a memory-based store for the session tokens.
@@ -218,6 +235,54 @@ FmRest.token_store = FmRest::TokenStore::Moneta.new(
218
235
  **NOTE:** the moneta gem is not included as a dependency of fmrest-ruby, so
219
236
  you'll have to add it to your Gemfile.
220
237
 
238
+
239
+ ## Date fields
240
+
241
+ Since the Data API uses JSON (wich doesn't provide a native date/time object),
242
+ dates and timestamps are received in string format. By default fmrest-ruby
243
+ leaves those string fields untouched, but it provides an opt-in feature to try
244
+ to automatically "coerce" them into Ruby date objects.
245
+
246
+ The connection option `:coerce_dates` controls this feature. Possible values
247
+ are:
248
+
249
+ * `:full`: whenever a string matches the given date/timestamp/time format,
250
+ convert them to `Date` or `DateTime` objects as appropriate
251
+ * `:hybrid` or `true`: similar as above, but instead of converting to regular
252
+ `Date`/`DateTime` it converts strings to `FmRest::StringDate` and
253
+ `FmRest::StringDateTime`, "hybrid" classes provided by fmrest-ruby that
254
+ retain the functionality of `String` while also providing most the
255
+ functionality of `Date`/`DateTime` (more on this below)
256
+ * `false`: disable date coercion entirely (default), leave original string
257
+ values untouched
258
+
259
+ Enabling date coercion works with both basic fmrest-ruby connections and Spyke
260
+ models (ORM).
261
+
262
+ The connection options `:date_format`, `:timestamp_format` and `:time_format`
263
+ control how to match and parse dates. You only need to provide these if you use
264
+ a date/time localization different from American format (the default).
265
+
266
+ Future versions of fmrest-ruby will provide better (and less heuristic) ways of
267
+ specifying and/or detecting date fields (e.g. by requesting layout metadata or
268
+ a DSL in model classes).
269
+
270
+ ### Hybrid string/date objects
271
+
272
+ `FmRest::StringDate` and `FmRest::StringDateTime` are special classes that
273
+ inherit from `String`, but internally parse and store a `Date`/`DateTime`
274
+ (respectively), and delegate any methods not provided by `String` to those
275
+ objects. In other words, they quack like a duck *and* bark like a dog.
276
+
277
+ You can use these when you want fmrest-ruby to provide you with date objects,
278
+ but you don't want to worry about date coercion of false positives (i.e. a
279
+ string field that gets converted to `Date` because it just so matched the given
280
+ date format).
281
+
282
+ Be warned however that these classes come with a fair share of known gotchas
283
+ (see GitHub wiki for more info).
284
+
285
+
221
286
  ## Spyke support (ActiveRecord-like ORM)
222
287
 
223
288
  [Spyke](https://github.com/balvig/spyke) is an ActiveRecord-like gem for
@@ -830,6 +895,7 @@ same metadata hash with script execution results. Note that this does not apply
830
895
  to retrieving single records, in that case you'll have to use
831
896
  `.last_request_metadata`.
832
897
 
898
+
833
899
  ## Logging
834
900
 
835
901
  If using fmrest-ruby + Spyke in a Rails app pretty log output will be set up
@@ -883,7 +949,7 @@ FM Data API reference: https://fmhelp.filemaker.com/docs/18/en/dataapi/
883
949
  | Log in using OAuth | No | No |
884
950
  | Log in to an external data source | No | No |
885
951
  | Log in using a FileMaker ID account | No | No |
886
- | Log out | Yes | Yes
952
+ | Log out | Yes | Yes |
887
953
  | Get product information | Manual* | No |
888
954
  | Get database names | Manual* | No |
889
955
  | Get script names | Manual* | No |
@@ -904,19 +970,6 @@ FM Data API reference: https://fmhelp.filemaker.com/docs/18/en/dataapi/
904
970
 
905
971
  \* You can manually supply the URL and JSON to a `FmRest` connection.
906
972
 
907
- ## TODO
908
-
909
- - [ ] Support for FM18 features
910
- - [ ] Better/simpler-to-use core Ruby API
911
- - [ ] Better API documentation and README
912
- - [ ] Oauth support
913
- - [x] Support for portal limit and offset
914
- - [x] More options for token storage
915
- - [x] Support for container fields
916
- - [x] Optional logging
917
- - [x] FmRest::Spyke::Base class for single inheritance (as alternative for mixin)
918
- - [x] Specs
919
- - [x] Support for portal data
920
973
 
921
974
  ## Gem development
922
975
 
@@ -931,6 +984,7 @@ release a new version, update the version number in `version.rb`, and then run
931
984
  git commits and tags, and push the `.gem` file to
932
985
  [rubygems.org](https://rubygems.org).
933
986
 
987
+
934
988
  ## Contributing
935
989
 
936
990
  Bug reports and pull requests are welcome. This project is intended to be a
@@ -938,20 +992,16 @@ safe, welcoming space for collaboration, and contributors are expected to
938
992
  adhere to the [Contributor Covenant](http://contributor-covenant.org) code of
939
993
  conduct.
940
994
 
995
+
941
996
  ## License
942
997
 
943
998
  The gem is available as open source under the terms of the
944
999
  [MIT License](https://opensource.org/licenses/MIT).
945
1000
  See [LICENSE.txt](LICENSE.txt).
946
1001
 
1002
+
947
1003
  ## Disclaimer
948
1004
 
949
1005
  This project is not sponsored by or otherwise affiliated with FileMaker, Inc,
950
1006
  an Apple subsidiary. FileMaker is a trademark of FileMaker, Inc., registered in
951
1007
  the U.S. and other countries.
952
-
953
- ## Code of Conduct
954
-
955
- Everyone interacting in the fmrest-ruby project’s codebases, issue trackers,
956
- chat rooms and mailing lists is expected to follow the [code of
957
- conduct](CODE_OF_CONDUCT.md).
@@ -8,7 +8,7 @@ rescue LoadError => e
8
8
  end
9
9
 
10
10
  require "fmrest"
11
- require "fmrest/spyke/json_parser"
11
+ require "fmrest/spyke/spyke_formatter"
12
12
  require "fmrest/spyke/model"
13
13
  require "fmrest/spyke/base"
14
14
 
@@ -10,7 +10,7 @@ module FmRest
10
10
 
11
11
  included do
12
12
  # Keep track of portal options by their FM keys as we could need it
13
- # to parse the portalData JSON in JsonParser
13
+ # to parse the portalData JSON in SpykeFormatter
14
14
  class_attribute :portal_options, instance_accessor: false, instance_predicate: false
15
15
 
16
16
  # class_attribute supports a :default option since ActiveSupport 5.2,
@@ -37,7 +37,7 @@ module FmRest
37
37
  def has_portal(name, options = {})
38
38
  create_association(name, Portal, options)
39
39
 
40
- # Store options for JsonParser to use if needed
40
+ # Store options for SpykeFormatter to use if needed
41
41
  portal_key = options[:portal_key] || name
42
42
  self.portal_options = portal_options.merge(portal_key.to_s => options.dup.merge(name: name.to_s)).freeze
43
43
 
@@ -38,15 +38,25 @@ module FmRest
38
38
  private
39
39
 
40
40
  def fmrest_connection
41
- @fmrest_connection ||= FmRest::V1.build_connection(fmrest_config || FmRest.default_connection_settings) do |conn|
42
- faraday_block.call(conn) if faraday_block
43
-
44
- # Pass the class to JsonParser's initializer so it can have
45
- # access to extra context defined in the model, e.g. a portal
46
- # where name of the portal and the attributes prefix don't match
47
- # and need to be specified as options to `portal`
48
- conn.use FmRest::Spyke::JsonParser, self
49
- end
41
+ @fmrest_connection ||=
42
+ begin
43
+ config = fmrest_config || FmRest.default_connection_settings
44
+
45
+ FmRest::V1.build_connection(config) do |conn|
46
+ faraday_block.call(conn) if faraday_block
47
+
48
+ # Pass the class to SpykeFormatter's initializer so it can have
49
+ # access to extra context defined in the model, e.g. a portal
50
+ # where name of the portal and the attributes prefix don't match
51
+ # and need to be specified as options to `portal`
52
+ conn.use FmRest::Spyke::SpykeFormatter, self
53
+
54
+ conn.use FmRest::V1::TypeCoercer, config
55
+
56
+ # FmRest::Spyke::JsonParse expects symbol keys
57
+ conn.response :json, parser_options: { symbolize_names: true }
58
+ end
59
+ end
50
60
  end
51
61
  end
52
62
  end
@@ -6,7 +6,7 @@ module FmRest
6
6
  module Spyke
7
7
  # Response Faraday middleware for converting FM API's response JSON into
8
8
  # Spyke's expected format
9
- class JsonParser < ::Faraday::Response::Middleware
9
+ class SpykeFormatter < ::Faraday::Response::Middleware
10
10
  SINGLE_RECORD_RE = %r(/records/\d+\z).freeze
11
11
  MULTIPLE_RECORDS_RE = %r(/records\z).freeze
12
12
  CONTAINER_RE = %r(/records/\d+/containers/[^/]+/\d+\z).freeze
@@ -24,7 +24,9 @@ module FmRest
24
24
 
25
25
  # @param env [Faraday::Env]
26
26
  def on_complete(env)
27
- json = parse_json(env.body)
27
+ return unless env.body.is_a?(Hash)
28
+
29
+ json = env.body
28
30
 
29
31
  case
30
32
  when single_record_request?(env)
@@ -229,12 +231,6 @@ module FmRest
229
231
  def execute_script_request?(env)
230
232
  env.method == :get && env.url.path.match(SCRIPT_REQUEST_RE)
231
233
  end
232
-
233
- # @param source [String] a JSON string
234
- # @return [Hash] the parsed JSON
235
- def parse_json(source)
236
- JSON.parse(source, symbolize_names: true)
237
- end
238
234
  end
239
235
  end
240
236
  end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ module FmRest
6
+ # Gotchas:
7
+ #
8
+ # 1.
9
+ #
10
+ # Date === <StringDate instance> # => false
11
+ #
12
+ # The above can affect case conditions, as trying to match a StringDate
13
+ # with:
14
+ #
15
+ # case obj
16
+ # when Date
17
+ # ...
18
+ #
19
+ # ...will not work.
20
+ #
21
+ # Instead one must specify the FmRest::StringDate class:
22
+ #
23
+ # case obj
24
+ # when Date, FmRest::StringDate
25
+ # ...
26
+ #
27
+ # 2.
28
+ #
29
+ # StringDate#eql? only matches other strings, not dates.
30
+ #
31
+ # This could affect hash indexing when a StringDate is used as a key.
32
+ #
33
+ # TODO: Verify the above
34
+ #
35
+ # 3.
36
+ #
37
+ # StringDate#succ and StringDate#next return a String, despite Date#succ
38
+ # and Date#next also existing.
39
+ #
40
+ # Workaround: Use StringDate#next_day or strdate + 1
41
+ #
42
+ # 4.
43
+ #
44
+ # StringDate#to_s returns the original string, not the Date string
45
+ # representation.
46
+ #
47
+ # Workaround: Use strdate.to_date.to_s
48
+ #
49
+ # 5.
50
+ #
51
+ # StringDate#hash returns the hash for the string (important when using
52
+ # a StringDate as a hash key)
53
+ #
54
+ # 6.
55
+ #
56
+ # StringDate#as_json returns the string
57
+ #
58
+ # Workaround: Use strdate.to_date.as_json
59
+ #
60
+ # 7.
61
+ #
62
+ # Equality with Date is not reciprocal:
63
+ #
64
+ # str_date == date #=> true
65
+ # date == str_date #=> false
66
+ #
67
+ # NOTE: Potential workaround: Inherit StringDate from Date instead of String
68
+ #
69
+ # 8.
70
+ #
71
+ # Calling string transforming methods (e.g. .upcase) returns a StringDate
72
+ # instead of a String.
73
+ #
74
+ # NOTE: Potential workaround: Inherit StringDate from Date instead of String
75
+ #
76
+ class StringDate < String
77
+ DELEGATE_CLASS = ::Date
78
+
79
+ class InvalidDate < ArgumentError; end
80
+
81
+ class << self
82
+ alias_method :strptime, :new
83
+ end
84
+
85
+ def initialize(str, date_format, **str_args)
86
+ super(str, **str_args)
87
+
88
+ begin
89
+ @delegate = self.class::DELEGATE_CLASS.strptime(str, date_format)
90
+ rescue ArgumentError
91
+ raise InvalidDate
92
+ end
93
+
94
+ freeze
95
+ end
96
+
97
+ def is_a?(klass)
98
+ klass == ::Date || super
99
+ end
100
+ alias_method :kind_of?, :is_a?
101
+
102
+ def to_date
103
+ @delegate
104
+ end
105
+
106
+ def to_datetime
107
+ @delegate.to_datetime
108
+ end
109
+
110
+ def to_time
111
+ @delegate.to_time
112
+ end
113
+
114
+ # ActiveSupport method
115
+ def in_time_zone(*_)
116
+ @delegate.in_time_zone(*_)
117
+ end
118
+
119
+ def inspect
120
+ "#<#{self.class.name} #{@delegate.inspect} - #{super}>"
121
+ end
122
+
123
+ def <=>(oth)
124
+ return @delegate <=> oth if oth.is_a?(::Date) || oth.is_a?(Numeric)
125
+ super
126
+ end
127
+
128
+ def +(val)
129
+ return @delegate + val if val.kind_of?(Numeric)
130
+ super
131
+ end
132
+
133
+ def <<(val)
134
+ return @delegate << val if val.kind_of?(Numeric)
135
+ super
136
+ end
137
+
138
+ def ==(oth)
139
+ return @delegate == oth if oth.kind_of?(::Date) || oth.kind_of?(Numeric)
140
+ super
141
+ end
142
+ alias_method :===, :==
143
+
144
+ def upto(oth, &blk)
145
+ return @delegate.upto(oth, &blk) if oth.kind_of?(::Date) || oth.kind_of?(Numeric)
146
+ super
147
+ end
148
+
149
+ def between?(a, b)
150
+ return @delegate.between?(a, b) if [a, b].any? {|o| o.is_a?(::Date) || o.is_a?(Numeric) }
151
+ super
152
+ end
153
+
154
+ private
155
+
156
+ def respond_to_missing?(name, include_private = false)
157
+ @delegate.respond_to?(name, include_private)
158
+ end
159
+
160
+ def method_missing(method, *args, &block)
161
+ @delegate.send(method, *args, &block)
162
+ end
163
+ end
164
+
165
+ class StringDateTime < StringDate
166
+ DELEGATE_CLASS = ::DateTime
167
+
168
+ def is_a?(klass)
169
+ klass == ::DateTime || super
170
+ end
171
+ alias_method :kind_of?, :is_a?
172
+
173
+ def to_date
174
+ @delegate.to_date
175
+ end
176
+
177
+ def to_datetime
178
+ @delegate
179
+ end
180
+ end
181
+ end
@@ -7,6 +7,10 @@ require "fmrest/v1/utils"
7
7
 
8
8
  module FmRest
9
9
  module V1
10
+ DEFAULT_DATE_FORMAT = "MM/dd/yyyy"
11
+ DEFAULT_TIME_FORMAT = "HH:mm:ss"
12
+ DEFAULT_TIMESTAMP_FORMAT = "#{DEFAULT_DATE_FORMAT} #{DEFAULT_TIME_FORMAT}"
13
+
10
14
  extend Connection
11
15
  extend Paths
12
16
  extend ContainerFields
@@ -5,7 +5,7 @@ require "uri"
5
5
  module FmRest
6
6
  module V1
7
7
  module Connection
8
- BASE_PATH = "/fmi/data/v1/databases".freeze
8
+ BASE_PATH = "/fmi/data/v1/databases"
9
9
 
10
10
  # Builds a complete DAPI Faraday connection with middleware already
11
11
  # configured to handle authentication, JSON parsing, logging and DAPI
@@ -19,7 +19,6 @@ module FmRest
19
19
  # @option (see #base_connection)
20
20
  # @return (see #base_connection)
21
21
  def build_connection(options = FmRest.default_connection_settings, &block)
22
-
23
22
  base_connection(options) do |conn|
24
23
  conn.use RaiseErrors
25
24
  conn.use TokenSession, options
@@ -38,8 +37,9 @@ module FmRest
38
37
 
39
38
  # Allow overriding the default response middleware
40
39
  if block_given?
41
- yield conn
40
+ yield conn, options
42
41
  else
42
+ conn.use TypeCoercer, options
43
43
  conn.response :json
44
44
  end
45
45
 
@@ -86,3 +86,4 @@ end
86
86
 
87
87
  require "fmrest/v1/token_session"
88
88
  require "fmrest/v1/raise_errors"
89
+ require "fmrest/v1/type_coercer"
@@ -14,6 +14,8 @@ module FmRest
14
14
  TOKEN_STORE_INTERFACE = [:load, :store, :delete].freeze
15
15
  LOGOUT_PATH_MATCHER = %r{\A(#{FmRest::V1::Connection::BASE_PATH}/[^/]+/sessions/)[^/]+\Z}.freeze
16
16
 
17
+ # @param app [#call]
18
+ # @param options [Hash]
17
19
  def initialize(app, options = FmRest.default_connection_settings)
18
20
  super(app)
19
21
  @options = options
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fmrest/string_date"
4
+
5
+ module FmRest
6
+ module V1
7
+ class TypeCoercer < Faraday::Response::Middleware
8
+ # We use this date to represent a time for consistency with ginjo-rfm
9
+ JULIAN_ZERO_DAY = "-4712/1/1"
10
+
11
+ COERCE_HYBRID = [:hybrid, "hybrid", true].freeze
12
+ COERCE_FULL = [:full, "full"].freeze
13
+
14
+ # @param app [#call]
15
+ # @param options [Hash]
16
+ def initialize(app, options = FmRest.default_connection_settings)
17
+ super(app)
18
+ @options = options
19
+ end
20
+
21
+ def on_complete(env)
22
+ return unless enabled?
23
+ return unless env.body.kind_of?(Hash)
24
+
25
+ data = env.body.dig("response", "data") || env.body.dig(:response, :data)
26
+
27
+ return unless data
28
+
29
+ data.each do |record|
30
+ field_data = record["fieldData"] || record[:fieldData]
31
+ portal_data = record["portalData"] || record[:portalData]
32
+
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
38
+ end
39
+ end
40
+
41
+ enum.each { |hash| coerce_fields(hash) }
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def coerce_fields(hash)
48
+ hash.each do |k, v|
49
+ next unless v.is_a?(String)
50
+ next if k == "recordId" || k == :recordId || k == "modId" || k == :modId
51
+
52
+ begin
53
+ str_timestamp = datetime_class.strptime(v, datetime_format)
54
+ hash[k] = str_timestamp
55
+ next
56
+ rescue ArgumentError
57
+ end
58
+
59
+ begin
60
+ str_date = date_class.strptime(v, date_format)
61
+ hash[k] = str_date
62
+ next
63
+ rescue ArgumentError
64
+ end
65
+
66
+ begin
67
+ str_time = datetime_class.strptime("#{JULIAN_ZERO_DAY} #{v}", time_format)
68
+ hash[k] = str_time
69
+ next
70
+ rescue ArgumentError
71
+ end
72
+ end
73
+ end
74
+
75
+ def date_class
76
+ @date_class ||=
77
+ case coerce_dates
78
+ when *COERCE_HYBRID
79
+ StringDate
80
+ when *COERCE_FULL
81
+ Date
82
+ end
83
+ end
84
+
85
+ def datetime_class
86
+ @datetime_class ||=
87
+ case coerce_dates
88
+ when *COERCE_HYBRID
89
+ StringDateTime
90
+ when *COERCE_FULL
91
+ DateTime
92
+ end
93
+ end
94
+
95
+ def date_format
96
+ @date_format ||=
97
+ FmRest::V1.convert_date_time_format(@options[:date_format] || DEFAULT_DATE_FORMAT)
98
+ end
99
+
100
+ def datetime_format
101
+ @datetime_format ||=
102
+ FmRest::V1.convert_date_time_format(@options[:timestamp_format] || DEFAULT_TIMESTAMP_FORMAT)
103
+ end
104
+
105
+ def time_format
106
+ @time_format ||=
107
+ "%Y/%m/%d " + FmRest::V1.convert_date_time_format(@options[:time_format] || DEFAULT_TIME_FORMAT)
108
+ end
109
+
110
+ def coerce_dates
111
+ @options.fetch(:coerce_dates, false)
112
+ end
113
+
114
+ alias_method :enabled?, :coerce_dates
115
+ end
116
+ end
117
+ end
@@ -5,6 +5,17 @@ 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
+
8
19
  # Converts custom script options to a hash with the Data API's expected
9
20
  # JSON script format.
10
21
  #
@@ -72,6 +83,12 @@ module FmRest
72
83
  params
73
84
  end
74
85
 
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
+
75
92
  private
76
93
 
77
94
  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.6.0"
4
+ VERSION = "0.7.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.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pedro Carbajal
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-05 00:00:00.000000000 Z
11
+ date: 2020-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -231,7 +231,6 @@ files:
231
231
  - ".travis.yml"
232
232
  - ".yardopts"
233
233
  - CHANGELOG.md
234
- - CODE_OF_CONDUCT.md
235
234
  - Gemfile
236
235
  - LICENSE.txt
237
236
  - README.md
@@ -242,7 +241,6 @@ files:
242
241
  - lib/fmrest/spyke.rb
243
242
  - lib/fmrest/spyke/base.rb
244
243
  - lib/fmrest/spyke/container_field.rb
245
- - lib/fmrest/spyke/json_parser.rb
246
244
  - lib/fmrest/spyke/model.rb
247
245
  - lib/fmrest/spyke/model/associations.rb
248
246
  - lib/fmrest/spyke/model/attributes.rb
@@ -255,7 +253,9 @@ files:
255
253
  - lib/fmrest/spyke/model/uri.rb
256
254
  - lib/fmrest/spyke/portal.rb
257
255
  - lib/fmrest/spyke/relation.rb
256
+ - lib/fmrest/spyke/spyke_formatter.rb
258
257
  - lib/fmrest/spyke/validation_error.rb
258
+ - lib/fmrest/string_date.rb
259
259
  - lib/fmrest/token_store.rb
260
260
  - lib/fmrest/token_store/active_record.rb
261
261
  - lib/fmrest/token_store/base.rb
@@ -270,6 +270,7 @@ files:
270
270
  - lib/fmrest/v1/token_session.rb
271
271
  - lib/fmrest/v1/token_store/active_record.rb
272
272
  - lib/fmrest/v1/token_store/memory.rb
273
+ - lib/fmrest/v1/type_coercer.rb
273
274
  - lib/fmrest/v1/utils.rb
274
275
  - lib/fmrest/version.rb
275
276
  homepage: https://github.com/beezwax/fmrest-ruby
@@ -1,74 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, gender identity and expression, level of experience,
9
- nationality, personal appearance, race, religion, or sexual identity and
10
- orientation.
11
-
12
- ## Our Standards
13
-
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
16
-
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
22
-
23
- Examples of unacceptable behavior by participants include:
24
-
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
28
- * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
31
- * Other conduct which could reasonably be considered inappropriate in a
32
- professional setting
33
-
34
- ## Our Responsibilities
35
-
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
39
-
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
45
-
46
- ## Scope
47
-
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
-
55
- ## Enforcement
56
-
57
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at pedro_c@beezwax.net. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
-
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
67
-
68
- ## Attribution
69
-
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [http://contributor-covenant.org/version/1/4][version]
72
-
73
- [homepage]: http://contributor-covenant.org
74
- [version]: http://contributor-covenant.org/version/1/4/