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.
- 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
|