filemaker 0.0.1 → 0.0.2

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +129 -17
  3. data/filemaker.gemspec +1 -0
  4. data/lib/filemaker.rb +47 -0
  5. data/lib/filemaker/api/query_commands/findquery.rb +20 -3
  6. data/lib/filemaker/configuration.rb +1 -1
  7. data/lib/filemaker/core_ext/hash.rb +19 -15
  8. data/lib/filemaker/error.rb +5 -0
  9. data/lib/filemaker/metadata/field.rb +21 -5
  10. data/lib/filemaker/model.rb +132 -0
  11. data/lib/filemaker/model/builder.rb +52 -0
  12. data/lib/filemaker/model/components.rb +25 -0
  13. data/lib/filemaker/model/criteria.rb +101 -0
  14. data/lib/filemaker/model/field.rb +38 -0
  15. data/lib/filemaker/model/fields.rb +80 -0
  16. data/lib/filemaker/model/findable.rb +35 -0
  17. data/lib/filemaker/model/optional.rb +69 -0
  18. data/lib/filemaker/model/pagination.rb +41 -0
  19. data/lib/filemaker/model/persistable.rb +96 -0
  20. data/lib/filemaker/model/relations.rb +72 -0
  21. data/lib/filemaker/model/relations/belongs_to.rb +30 -0
  22. data/lib/filemaker/model/relations/has_many.rb +79 -0
  23. data/lib/filemaker/model/relations/proxy.rb +35 -0
  24. data/lib/filemaker/model/selectable.rb +146 -0
  25. data/lib/filemaker/railtie.rb +17 -0
  26. data/lib/filemaker/record.rb +25 -0
  27. data/lib/filemaker/resultset.rb +12 -4
  28. data/lib/filemaker/server.rb +7 -5
  29. data/lib/filemaker/version.rb +1 -1
  30. data/spec/filemaker/api/query_commands/compound_find_spec.rb +13 -1
  31. data/spec/filemaker/configuration_spec.rb +23 -0
  32. data/spec/filemaker/layout_spec.rb +0 -1
  33. data/spec/filemaker/model/criteria_spec.rb +304 -0
  34. data/spec/filemaker/model/relations_spec.rb +85 -0
  35. data/spec/filemaker/model_spec.rb +73 -0
  36. data/spec/filemaker/record_spec.rb +12 -1
  37. data/spec/spec_helper.rb +1 -0
  38. data/spec/support/filemaker.yml +13 -0
  39. data/spec/support/models.rb +38 -0
  40. metadata +44 -2
@@ -0,0 +1,17 @@
1
+ require 'rails'
2
+
3
+ module Rails
4
+ module Filemaker
5
+ class Railtie < Rails::Railtie
6
+ initializer 'filemaker-load-config-yml' do
7
+ config_file = Rails.root.join('config', 'filemaker.yml')
8
+
9
+ if config_file.file?
10
+ Filemaker.load!(config_file, Rails.env)
11
+ else
12
+ fail Error::ConfigurationError, 'No config file provided'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -9,10 +9,13 @@ module Filemaker
9
9
  # @return [Hash] additional nested records
10
10
  attr_reader :portals
11
11
 
12
+ attr_reader :dirty
13
+
12
14
  def initialize(record, resultset, portal_table_name = nil)
13
15
  @mod_id = record['mod-id']
14
16
  @record_id = record['record-id']
15
17
  @portals = HashWithIndifferentAndCaseInsensitiveAccess.new
18
+ @dirty = {} # Keep track of field modification
16
19
 
17
20
  record.xpath('field').each do |field|
18
21
  # `field` is Nokogiri::XML::Element
@@ -36,6 +39,21 @@ module Filemaker
36
39
  end
37
40
 
38
41
  build_portals(record.xpath('relatedset'), resultset)
42
+
43
+ @ready = true
44
+ end
45
+
46
+ def [](key)
47
+ fail(Filemaker::Error::InvalidFieldError, "Invalid field: #{key}") \
48
+ unless key?(key)
49
+ super
50
+ end
51
+
52
+ def []=(key, value)
53
+ return super unless @ready
54
+ fail(Filemaker::Error::InvalidFieldError, "Invalid field: #{key}") \
55
+ unless key?(key)
56
+ @dirty[key] = value
39
57
  end
40
58
 
41
59
  private
@@ -60,5 +78,12 @@ module Filemaker
60
78
  return nil if datum.empty?
61
79
  (datum.size == 1) ? datum.first : datum
62
80
  end
81
+
82
+ def method_missing(symbol, *args, &block)
83
+ method = symbol.to_s
84
+ return self[method] if key?(method)
85
+ return @dirty[$`] = args.first if method =~ /(=)$/ && key?($`)
86
+ super
87
+ end
63
88
  end
64
89
  end
@@ -38,9 +38,9 @@ module Filemaker
38
38
  # @return [String] the raw XML for inspection
39
39
  attr_reader :xml
40
40
 
41
- # @param xml [Filemaker::Server] server
42
- # @param xml [String] the XML string from response
43
- # @param xml [Hash] the request params used to construct request
41
+ # @param [Filemaker::Server] server
42
+ # @param [String] xml The XML string from response
43
+ # @param [Hash] params The request params used to construct request
44
44
  def initialize(server, xml, params = nil)
45
45
  @list = []
46
46
  @fields = {}
@@ -76,10 +76,18 @@ module Filemaker
76
76
  list.each(*args, &block)
77
77
  end
78
78
 
79
+ def size
80
+ list.size
81
+ end
82
+ alias_method :length, :size
83
+
79
84
  private
80
85
 
86
+ # For 401 (No records match the request) and 101 (Record is missing), we
87
+ # will return empty array or nil back rather than raise those errors. Is it
88
+ # a good design? We need to find out after production usage.
81
89
  def raise_potential_error!(error_code)
82
- return if error_code.zero?
90
+ return if error_code.zero? || error_code == 401 || error_code == 101
83
91
 
84
92
  Filemaker::Error.raise_error_by_code(error_code)
85
93
  end
@@ -14,13 +14,13 @@ module Filemaker
14
14
  alias_method :database, :databases
15
15
  alias_method :db, :databases
16
16
 
17
- def_delegators :@config, :host, :url, :ssl, :endpoint
17
+ def_delegators :@config, :host, :url, :ssl, :endpoint, :log
18
18
  def_delegators :@config, :account_name, :password
19
19
 
20
20
  def initialize(options = {})
21
21
  @config = Configuration.new
22
22
  yield @config if block_given?
23
- fail ArgumentError if @config.not_configurable?
23
+ fail ArgumentError, 'Missing config block' if @config.not_configurable?
24
24
 
25
25
  @databases = Store::DatabaseStore.new(self)
26
26
  @connection = get_connection(options)
@@ -39,13 +39,13 @@ module Filemaker
39
39
  .merge(expand_options(options))
40
40
  .merge({ action => '' })
41
41
 
42
- # Serialize the params for submission
43
- params = params.stringify_keys
42
+ # Serialize the params for submission??
43
+ params.stringify_keys!
44
44
 
45
45
  log_action(params)
46
46
 
47
47
  # yield params if block_given?
48
- response = @connection.__send__(method, endpoint, params)
48
+ response = @connection.public_send(method, endpoint, params)
49
49
 
50
50
  case response.status
51
51
  when 200 then [response, params]
@@ -86,6 +86,7 @@ module Filemaker
86
86
  when DateTime then args[key] = value.strftime('%m/%d/%Y %H:%M:%S')
87
87
  when Time then args[key] = value.strftime('%H:%M')
88
88
  else
89
+ # Especially for range operator (...), we want to output as String
89
90
  args[key] = value.to_s
90
91
  end
91
92
  end
@@ -161,6 +162,7 @@ module Filemaker
161
162
  expanded
162
163
  end
163
164
 
165
+ # TODO: Should we convert it to string so 'cURL' will work also?
164
166
  def log_action(params)
165
167
  case @config.log
166
168
  when :simple then log_simple(params)
@@ -1,3 +1,3 @@
1
1
  module Filemaker
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -44,7 +44,7 @@ describe Filemaker::Api::QueryCommands::CompoundFind do
44
44
  end
45
45
  end
46
46
 
47
- context 'with array' do
47
+ context 'with array of hash' do
48
48
  it '[{a: 1}, {b: 2}] to (q0);(q1)' do
49
49
  expect(Filemaker::Api::QueryCommands::CompoundFind.new(
50
50
  [{ a: 1 }, { b: 2 }]
@@ -64,6 +64,18 @@ describe Filemaker::Api::QueryCommands::CompoundFind do
64
64
  [{ a: [1, 2, 3], b: 1 }, { c: 4, '-omit' => true }]
65
65
  ).key_maps_string).to eq '(q0,q3);(q1,q3);(q2,q3);!(q4)'
66
66
  end
67
+
68
+ it '[{a: [1, 2]}, {b: 2, c: 3, "-omit" => true}] to (q0);(q1);!(q2,q3)' do
69
+ expect(Filemaker::Api::QueryCommands::CompoundFind.new(
70
+ [{ a: [1, 2] }, { b: 2, c: 3, '-omit' => true }]
71
+ ).key_maps_string).to eq '(q0);(q1);!(q2,q3)'
72
+ end
73
+
74
+ it '[{a: [1, 2] }, { b: [3, 4] }] to (q0);(q1);(q2);(q3)' do
75
+ expect(Filemaker::Api::QueryCommands::CompoundFind.new(
76
+ [{ a: [1, 2] }, { b: [3, 4] }]
77
+ ).key_maps_string).to eq '(q0);(q1);(q2);(q3)'
78
+ end
67
79
  end
68
80
 
69
81
  end
@@ -0,0 +1,23 @@
1
+ describe 'Configuration' do
2
+
3
+ context 'with yaml file' do
4
+ it 'load settings based on environment' do
5
+ path = File.expand_path('../../support/filemaker.yml', __FILE__)
6
+ Filemaker.load!(path, 'development')
7
+ expect(Filemaker.registry['default']).to be_a Filemaker::Server
8
+ expect(Filemaker.registry['default'].host).to eq 'host.com'
9
+ expect(Filemaker.registry['default'].account_name).to eq \
10
+ 'FILEMAKER_ACCOUNT_NAME'
11
+ expect(Filemaker.registry['default'].ssl).to eq({ 'verify' => false })
12
+ expect(Filemaker.registry['default'].log).to eq :curl
13
+ end
14
+
15
+ it 'raises ConfigurationError for wrong environment' do
16
+ path = File.expand_path('../../support/filemaker.yml', __FILE__)
17
+ expect do
18
+ Filemaker.load!(path, 'unknown')
19
+ end.to raise_error Filemaker::Error::ConfigurationError
20
+ end
21
+ end
22
+
23
+ end
@@ -91,7 +91,6 @@ describe Filemaker::Layout do
91
91
  it 'finds some records with criteria' do
92
92
  args = { name: 'Bob', day: Date.parse('25/8/2014') }
93
93
  resultset = @layout.find(args, max: 1)
94
-
95
94
  expect(resultset.params['name']).to eq 'Bob'
96
95
  expect(resultset.params['day']).to eq '08/25/2014'
97
96
  expect(resultset.params['-max']).to eq 1
@@ -0,0 +1,304 @@
1
+ describe Filemaker::Model::Criteria do
2
+
3
+ let(:criteria) { Filemaker::Model::Criteria.new(MyModel) }
4
+ let(:cf) { Filemaker::Api::QueryCommands::CompoundFind }
5
+
6
+ context 'selectable' do
7
+ describe 'where' do
8
+ it 'raises MixedClauseError if mixed with -findquery' do
9
+ expect do
10
+ criteria.in(status: %w(pending subscribed)).where(name: 'Bob')
11
+ end.to raise_error Filemaker::Error::MixedClauseError
12
+ end
13
+
14
+ it 'single hash criterion are recorded as is' do
15
+ criteria.where(name: 'Bob', email: 'bob@cern.org')
16
+ expect(criteria.selector).to eq(
17
+ { 'name' => 'Bob', 'email' => 'bob@cern.org' }
18
+ )
19
+ end
20
+
21
+ it 'chains where' do
22
+ criteria.where(name: 'Bob').where(email: 'bob@cern.org')
23
+ expect(criteria.selector).to eq(
24
+ { 'name' => 'Bob', 'email' => 'bob@cern.org' }
25
+ )
26
+ end
27
+
28
+ it 'accepts a block to configure additional options' do
29
+ criteria.where(name: 'Bob') do |options|
30
+ options[:script] = 'Remove Duplicates'
31
+ end
32
+
33
+ expect(criteria.options[:script]).to eq 'Remove Duplicates'
34
+ end
35
+
36
+ it 'only accepts fields from model' do
37
+ criteria.where(name: 'Bob', email: 'bob@cern.org', unit: '< 50')
38
+ expect(criteria.selector).to eq(
39
+ { 'name' => 'Bob', 'email' => 'bob@cern.org' }
40
+ )
41
+ end
42
+ end
43
+
44
+ describe 'or' do
45
+ it 'chains `or` as logical OR option' do
46
+ criteria.where(name: 'Bob').or(email: 'bob@cern.org')
47
+ expect(criteria.selector).to eq(
48
+ { 'name' => 'Bob', 'email' => 'bob@cern.org' }
49
+ )
50
+ expect(criteria.options[:lop]).to eq 'or'
51
+ end
52
+
53
+ it 'chains multiple `or`, adding more fields' do
54
+ criteria.where(name: 'Bob').or(email: 'bob@cern.org').or(salary: 5000)
55
+ expect(criteria.selector).to eq(
56
+ { 'name' => 'Bob', 'email' => 'bob@cern.org', 'salary' => 5000 }
57
+ )
58
+ expect(criteria.options[:lop]).to eq 'or'
59
+ end
60
+ end
61
+
62
+ context 'comparison operators' do
63
+ it 'only works on `where` query' do
64
+ expect do
65
+ criteria.in(status: %w(pending subscribed)).eq(name: 'Bob')
66
+ end.to raise_error Filemaker::Error::MixedClauseError
67
+ end
68
+
69
+ describe 'not' do
70
+ it 'appends neq operator on the field name' do
71
+ criteria.not(salary: 50)
72
+ expect(criteria.selector['salary']).to eq 50
73
+ expect(criteria.selector['salary.op']).to eq 'neq'
74
+ end
75
+ end
76
+
77
+ describe 'cn' do
78
+ it 'appends cn operator on the field name' do
79
+ criteria.cn(name: 'Chong') do |options|
80
+ options[:script] = 'Remove Duplicates'
81
+ end
82
+
83
+ expect(criteria.selector['name']).to eq 'Chong'
84
+ expect(criteria.selector['name.op']).to eq 'cn'
85
+ end
86
+ end
87
+
88
+ describe 'bw' do
89
+ it 'can do range operation ...' do
90
+ criteria.bw(salary: '3000...4500')
91
+ expect(criteria.selector['salary']).to eq '3000...4500'
92
+ end
93
+ end
94
+
95
+ # TODO: Do more operator tests
96
+ end
97
+
98
+ describe 'in' do
99
+ it 'raises MixedClauseError if mixed with -find' do
100
+ expect do
101
+ criteria.where(name: 'Bob').in(status: %w(pending subscribed))
102
+ end.to raise_error Filemaker::Error::MixedClauseError
103
+ end
104
+
105
+ it '{a: [1, 2]} to (q0);(q1)' do
106
+ criteria.in(name: %w(Bob Lee))
107
+ compound_find = cf.new(criteria.selector)
108
+ expect(compound_find.key_maps_string).to eq '(q0);(q1)'
109
+ expect(criteria.selector).to eq [{ 'name' => %w(Bob Lee) }]
110
+ end
111
+
112
+ it '{a: [1, 2], b: [3, 4]} to (q0,q2);(q0,q3);(q1,q2);(q1,q3)' do
113
+ criteria.in(name: %w(Bob Lee), age: ['20', 30])
114
+ compound_find = cf.new(criteria.selector)
115
+ expect(compound_find.key_maps_string).to eq \
116
+ '(q0,q2);(q0,q3);(q1,q2);(q1,q3)'
117
+ expect(criteria.selector).to eq \
118
+ [{ 'name' => %w(Bob Lee), 'passage of time' => [20, 30] }]
119
+ end
120
+
121
+ it '{a: [1, 2], b: 3} to (q0,q2);(q1,q2)' do
122
+ criteria.in(name: %w(Bob Lee), age: '30')
123
+ compound_find = cf.new(criteria.selector)
124
+ expect(compound_find.key_maps_string).to eq '(q0,q2);(q1,q2)'
125
+ expect(criteria.selector).to eq \
126
+ [{ 'name' => %w(Bob Lee), 'passage of time' => 30 }]
127
+ end
128
+
129
+ it '{a: 1, b: 2} to (q0,q1)' do
130
+ criteria.in(name: 'Bob', age: 30)
131
+ compound_find = cf.new(criteria.selector)
132
+ expect(compound_find.key_maps_string).to eq '(q0,q1)'
133
+ expect(criteria.selector).to eq \
134
+ [{ 'name' => 'Bob', 'passage of time' => 30 }]
135
+ end
136
+
137
+ it '{a: 1, b: [2, 3]} to (q0,q1);(q0,q2)' do
138
+ criteria.in(name: 'Bob', age: [20, 30])
139
+ compound_find = cf.new(criteria.selector)
140
+ expect(compound_find.key_maps_string).to eq '(q0,q1);(q0,q2)'
141
+ expect(criteria.selector).to eq \
142
+ [{ 'name' => 'Bob', 'passage of time' => [20, 30] }]
143
+ end
144
+
145
+ it '[{a: [1, 2]}, {b: [1, 2]}] to (q0);(q1);(q2);(q3)' do
146
+ criteria.in([{ name: %w(Bob Lee) }, { age: [20, 30] }])
147
+ compound_find = cf.new(criteria.selector)
148
+ expect(compound_find.key_maps_string).to eq '(q0);(q1);(q2);(q3)'
149
+ expect(criteria.selector).to eq \
150
+ [{ 'name' => %w(Bob Lee) }, { 'passage of time' => [20, 30] }]
151
+ end
152
+
153
+ it '[{a: 1}, {b: 2}] to (q0);(q1)' do
154
+ criteria.in([{ name: 'Bob' }, { age: 20 }])
155
+ compound_find = cf.new(criteria.selector)
156
+ expect(compound_find.key_maps_string).to eq '(q0);(q1)'
157
+ expect(criteria.selector).to eq \
158
+ [{ 'name' => 'Bob' }, { 'passage of time' => 20 }]
159
+ end
160
+
161
+ it '[{a: 1}, {b: [1, 2]}] to (q0);(q1);(q2)' do
162
+ criteria.in([{ name: 'Bob' }, { age: [20, 30] }])
163
+ compound_find = cf.new(criteria.selector)
164
+ expect(compound_find.key_maps_string).to eq '(q0);(q1);(q2)'
165
+ expect(criteria.selector).to eq \
166
+ [{ 'name' => 'Bob' }, { 'passage of time' => [20, 30] }]
167
+ end
168
+
169
+ it '[{a: 1}, {b: 1, c: 2}] to (q0);(q1,q2)' do
170
+ criteria.in([{ name: 'Bob' }, { age: 20, email: 'A' }])
171
+ compound_find = cf.new(criteria.selector)
172
+ expect(compound_find.key_maps_string).to eq '(q0);(q1,q2)'
173
+ expect(criteria.selector).to eq \
174
+ [{ 'name' => 'Bob' }, { 'passage of time' => 20, 'email' => 'A' }]
175
+ end
176
+ end
177
+
178
+ describe 'not_in' do
179
+ it '{a: [1, 2]} to !(q0);!(q1)' do
180
+ criteria.not_in(name: %w(Bob Lee))
181
+ compound_find = cf.new(criteria.selector)
182
+ expect(compound_find.key_maps_string).to eq '!(q0);!(q1)'
183
+ end
184
+
185
+ it '{a: [1, 2], b: [3, 4]} to !(q0,q2);!(q0,q3);!(q1,q2);!(q1,q3)' do
186
+ criteria.not_in(name: %w(Bob Lee), age: ['20', 30])
187
+ compound_find = cf.new(criteria.selector)
188
+ expect(compound_find.key_maps_string).to eq \
189
+ '!(q0,q2);!(q0,q3);!(q1,q2);!(q1,q3)'
190
+ end
191
+
192
+ it '{a: [1, 2], b: 3} to !(q0,q2);!(q1,q2)' do
193
+ criteria.not_in(name: %w(Bob Lee), age: '30')
194
+ compound_find = cf.new(criteria.selector)
195
+ expect(compound_find.key_maps_string).to eq '!(q0,q2);!(q1,q2)'
196
+ end
197
+
198
+ it '{a: 1, b: 2} to !(q0,q1)' do
199
+ criteria.not_in(name: 'Bob', age: 30)
200
+ compound_find = cf.new(criteria.selector)
201
+ expect(compound_find.key_maps_string).to eq '!(q0,q1)'
202
+ end
203
+
204
+ it '{a: 1, b: [2, 3]} to !(q0,q1);!(q0,q2)' do
205
+ criteria.not_in(name: 'Bob', age: [20, 30])
206
+ compound_find = cf.new(criteria.selector)
207
+ expect(compound_find.key_maps_string).to eq '!(q0,q1);!(q0,q2)'
208
+ end
209
+
210
+ it '[{a: [1, 2]}, {b: [1, 2]}] to !(q0);!(q1);!(q2);!(q3)' do
211
+ criteria.not_in([{ name: %w(Bob Lee) }, { age: [20, 30] }])
212
+ compound_find = cf.new(criteria.selector)
213
+ expect(compound_find.key_maps_string).to eq '!(q0);!(q1);!(q2);!(q3)'
214
+ end
215
+
216
+ it '[{a: 1}, {b: 2}] to !(q0);!(q1)' do
217
+ criteria.not_in([{ name: 'Bob' }, { age: 20 }])
218
+ compound_find = cf.new(criteria.selector)
219
+ expect(compound_find.key_maps_string).to eq '!(q0);!(q1)'
220
+ end
221
+
222
+ it '[{a: 1}, {b: [1, 2]}] to !(q0);!(q1);!(q2)' do
223
+ criteria.not_in([{ name: 'Bob' }, { age: [20, 30] }])
224
+ compound_find = cf.new(criteria.selector)
225
+ expect(compound_find.key_maps_string).to eq '!(q0);!(q1);!(q2)'
226
+ end
227
+
228
+ it '[{a: 1}, {b: 1, c: 2}] to !(q0);!(q1,q2)' do
229
+ criteria.not_in([{ name: 'Bob' }, { age: 20, email: 'A' }])
230
+ compound_find = cf.new(criteria.selector)
231
+ expect(compound_find.key_maps_string).to eq '!(q0);!(q1,q2)'
232
+ end
233
+
234
+ it 'using in and not_in at the same time' do
235
+ criteria.in(name: %w(Bob Lee)).not_in(age: 20, email: 'A')
236
+ compound_find = cf.new(criteria.selector)
237
+ expect(compound_find.key_maps_string).to eq '(q0);(q1);!(q2,q3)'
238
+ end
239
+ end
240
+ end
241
+
242
+ context 'optional' do
243
+ it 'accepts skip option' do
244
+ criteria.skip(10)
245
+ expect(criteria.options[:skip]).to eq 10
246
+ end
247
+
248
+ it 'will override skip if chain repeatedly' do
249
+ criteria.skip(10).skip(100).skip(1000)
250
+ expect(criteria.options[:skip]).to eq 1000
251
+ end
252
+
253
+ it 'accepts limit option' do
254
+ criteria.limit(10)
255
+ expect(criteria.options[:max]).to eq 10
256
+ end
257
+
258
+ it 'will override limit if chain repeatedly' do
259
+ criteria.limit(10).limit(100).limit(1000)
260
+ expect(criteria.options[:max]).to eq 1000
261
+ end
262
+
263
+ it 'accepts order option' do
264
+ criteria.order('name desc')
265
+ expect(criteria.options[:sortfield]).to eq ['name']
266
+ expect(criteria.options[:sortorder]).to eq ['descend']
267
+ end
268
+
269
+ it 'does not entertain invalid fieldname' do
270
+ criteria.order('zzz desc')
271
+ expect(criteria.options[:sortfield]).to be_nil
272
+ expect(criteria.options[:sortorder]).to be_nil
273
+ end
274
+
275
+ it 'will default to asc for missing order' do
276
+ criteria.order('name, email')
277
+ expect(criteria.options[:sortorder]).to eq %w(ascend ascend)
278
+ end
279
+
280
+ it 'will use real FileMaker fieldname' do
281
+ criteria.order('updated_at desc')
282
+ expect(criteria.options[:sortfield]).to eq ['modifieddate']
283
+ end
284
+ end
285
+
286
+ context 'pagination' do
287
+ it 'sets the limit' do
288
+ expect(criteria.per(50).options[:max]).to eq 50
289
+ end
290
+
291
+ it 'sets the page to skip' do
292
+ expect(criteria.page(2).per(50).options[:skip]).to eq 50
293
+ end
294
+
295
+ it 'sets the page to skip with larger page' do
296
+ expect(criteria.page(12).per(50).options[:skip]).to eq 550
297
+ end
298
+
299
+ it 'will not populate skip option if there is no page' do
300
+ expect(criteria.per(50).options[:skip]).to be_nil
301
+ end
302
+ end
303
+
304
+ end