filemaker 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +129 -17
- data/filemaker.gemspec +1 -0
- data/lib/filemaker.rb +47 -0
- data/lib/filemaker/api/query_commands/findquery.rb +20 -3
- data/lib/filemaker/configuration.rb +1 -1
- data/lib/filemaker/core_ext/hash.rb +19 -15
- data/lib/filemaker/error.rb +5 -0
- data/lib/filemaker/metadata/field.rb +21 -5
- data/lib/filemaker/model.rb +132 -0
- data/lib/filemaker/model/builder.rb +52 -0
- data/lib/filemaker/model/components.rb +25 -0
- data/lib/filemaker/model/criteria.rb +101 -0
- data/lib/filemaker/model/field.rb +38 -0
- data/lib/filemaker/model/fields.rb +80 -0
- data/lib/filemaker/model/findable.rb +35 -0
- data/lib/filemaker/model/optional.rb +69 -0
- data/lib/filemaker/model/pagination.rb +41 -0
- data/lib/filemaker/model/persistable.rb +96 -0
- data/lib/filemaker/model/relations.rb +72 -0
- data/lib/filemaker/model/relations/belongs_to.rb +30 -0
- data/lib/filemaker/model/relations/has_many.rb +79 -0
- data/lib/filemaker/model/relations/proxy.rb +35 -0
- data/lib/filemaker/model/selectable.rb +146 -0
- data/lib/filemaker/railtie.rb +17 -0
- data/lib/filemaker/record.rb +25 -0
- data/lib/filemaker/resultset.rb +12 -4
- data/lib/filemaker/server.rb +7 -5
- data/lib/filemaker/version.rb +1 -1
- data/spec/filemaker/api/query_commands/compound_find_spec.rb +13 -1
- data/spec/filemaker/configuration_spec.rb +23 -0
- data/spec/filemaker/layout_spec.rb +0 -1
- data/spec/filemaker/model/criteria_spec.rb +304 -0
- data/spec/filemaker/model/relations_spec.rb +85 -0
- data/spec/filemaker/model_spec.rb +73 -0
- data/spec/filemaker/record_spec.rb +12 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/filemaker.yml +13 -0
- data/spec/support/models.rb +38 -0
- 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
|
data/lib/filemaker/record.rb
CHANGED
@@ -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
|
data/lib/filemaker/resultset.rb
CHANGED
@@ -38,9 +38,9 @@ module Filemaker
|
|
38
38
|
# @return [String] the raw XML for inspection
|
39
39
|
attr_reader :xml
|
40
40
|
|
41
|
-
# @param
|
42
|
-
# @param
|
43
|
-
# @param
|
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
|
data/lib/filemaker/server.rb
CHANGED
@@ -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
|
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.
|
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)
|
data/lib/filemaker/version.rb
CHANGED
@@ -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
|