super 0.0.16 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/super/application.js +146 -13
- data/app/assets/stylesheets/super/application.css +0 -1
- data/app/controllers/super/application_controller.rb +15 -2
- data/app/controllers/super/substructure_controller.rb +19 -8
- data/app/views/super/application/_display_actions.html.erb +1 -1
- data/app/views/super/application/_filter.html.erb +62 -2
- data/app/views/super/application/_member_header.html.erb +1 -1
- data/app/views/super/application/_sort_expression.html.erb +2 -2
- data/frontend/super-frontend/dist/application.css +0 -1
- data/frontend/super-frontend/dist/application.js +146 -13
- data/lib/super.rb +1 -0
- data/lib/super/display/guesser.rb +1 -1
- data/lib/super/display/schema_types.rb +0 -1
- data/lib/super/error.rb +2 -0
- data/lib/super/filter.rb +1 -1
- data/lib/super/filter/form_object.rb +74 -48
- data/lib/super/filter/guesser.rb +2 -0
- data/lib/super/filter/operator.rb +90 -64
- data/lib/super/filter/schema_types.rb +63 -80
- data/lib/super/form/builder.rb +6 -3
- data/lib/super/form/field_transcript.rb +43 -0
- data/lib/super/form/guesser.rb +1 -1
- data/lib/super/form/schema_types.rb +11 -20
- data/lib/super/link.rb +1 -1
- data/lib/super/schema.rb +4 -0
- data/lib/super/version.rb +1 -1
- metadata +4 -7
- data/app/views/super/application/_filter_type_select.html.erb +0 -21
- data/app/views/super/application/_filter_type_text.html.erb +0 -18
- data/app/views/super/application/_filter_type_timestamp.html.erb +0 -24
- data/app/views/super/application/_form_field_select.html.erb +0 -1
@@ -413,7 +413,7 @@ var Super = (function (exports) {
|
|
413
413
|
}).call(this);
|
414
414
|
(function () {
|
415
415
|
var AcceptHeaders, CSRFProtection, createXHR, cspNonce, prepareOptions, processResponse;
|
416
|
-
cspNonce = Rails.cspNonce, CSRFProtection = Rails.CSRFProtection
|
416
|
+
cspNonce = Rails.cspNonce, CSRFProtection = Rails.CSRFProtection;
|
417
417
|
AcceptHeaders = {
|
418
418
|
'*': '*/*',
|
419
419
|
text: 'text/plain',
|
@@ -3975,7 +3975,7 @@ var Super = (function (exports) {
|
|
3975
3975
|
};
|
3976
3976
|
}
|
3977
3977
|
|
3978
|
-
var _default$
|
3978
|
+
var _default$8 = /*#__PURE__*/function (_Controller) {
|
3979
3979
|
_inherits(_default, _Controller);
|
3980
3980
|
|
3981
3981
|
var _super = _createSuper(_default);
|
@@ -4004,7 +4004,7 @@ var Super = (function (exports) {
|
|
4004
4004
|
return _default;
|
4005
4005
|
}(Controller);
|
4006
4006
|
|
4007
|
-
var _default$
|
4007
|
+
var _default$7 = /*#__PURE__*/function (_Controller) {
|
4008
4008
|
_inherits(_default, _Controller);
|
4009
4009
|
|
4010
4010
|
var _super = _createSuper(_default);
|
@@ -4040,7 +4040,7 @@ var Super = (function (exports) {
|
|
4040
4040
|
return _default;
|
4041
4041
|
}(Controller);
|
4042
4042
|
|
4043
|
-
var _default$
|
4043
|
+
var _default$6 = /*#__PURE__*/function (_Controller) {
|
4044
4044
|
_inherits(_default, _Controller);
|
4045
4045
|
|
4046
4046
|
var _super = _createSuper(_default);
|
@@ -4064,7 +4064,7 @@ var Super = (function (exports) {
|
|
4064
4064
|
return _default;
|
4065
4065
|
}(Controller);
|
4066
4066
|
|
4067
|
-
var _default$
|
4067
|
+
var _default$5 = /*#__PURE__*/function (_Controller) {
|
4068
4068
|
_inherits(_default, _Controller);
|
4069
4069
|
|
4070
4070
|
var _super = _createSuper(_default);
|
@@ -4094,7 +4094,7 @@ var Super = (function (exports) {
|
|
4094
4094
|
return _default;
|
4095
4095
|
}(Controller);
|
4096
4096
|
|
4097
|
-
var _default$
|
4097
|
+
var _default$4 = /*#__PURE__*/function (_Controller) {
|
4098
4098
|
_inherits(_default, _Controller);
|
4099
4099
|
|
4100
4100
|
var _super = _createSuper(_default);
|
@@ -6354,7 +6354,7 @@ var Super = (function (exports) {
|
|
6354
6354
|
window.flatpickr = flatpickr;
|
6355
6355
|
}
|
6356
6356
|
|
6357
|
-
var _default$
|
6357
|
+
var _default$3 = /*#__PURE__*/function (_Controller) {
|
6358
6358
|
_inherits(_default, _Controller);
|
6359
6359
|
|
6360
6360
|
var _super = _createSuper(_default);
|
@@ -6382,6 +6382,137 @@ var Super = (function (exports) {
|
|
6382
6382
|
return _default;
|
6383
6383
|
}(Controller);
|
6384
6384
|
|
6385
|
+
var _default$2 = /*#__PURE__*/function (_Controller) {
|
6386
|
+
_inherits(_default, _Controller);
|
6387
|
+
|
6388
|
+
var _super = _createSuper(_default);
|
6389
|
+
|
6390
|
+
function _default() {
|
6391
|
+
_classCallCheck(this, _default);
|
6392
|
+
|
6393
|
+
return _super.apply(this, arguments);
|
6394
|
+
}
|
6395
|
+
|
6396
|
+
_createClass(_default, [{
|
6397
|
+
key: "connect",
|
6398
|
+
value: function connect() {
|
6399
|
+
var _this = this;
|
6400
|
+
|
6401
|
+
this.tabTargets.forEach(function (tab) {
|
6402
|
+
tab.tabContainer = _this;
|
6403
|
+
});
|
6404
|
+
}
|
6405
|
+
}, {
|
6406
|
+
key: "activeTabIdentifier",
|
6407
|
+
get: function get() {
|
6408
|
+
return this.controlTarget.value;
|
6409
|
+
}
|
6410
|
+
}, {
|
6411
|
+
key: "change",
|
6412
|
+
value: function change(event) {
|
6413
|
+
this.update(event.target.value);
|
6414
|
+
}
|
6415
|
+
}, {
|
6416
|
+
key: "update",
|
6417
|
+
value: function update(newActiveTabIdentifier) {
|
6418
|
+
var _this2 = this;
|
6419
|
+
|
6420
|
+
this.tabTargets.forEach(function (tab) {
|
6421
|
+
var tabController = _this2.application.getControllerForElementAndIdentifier(tab, _this2.tabControllerNameValue);
|
6422
|
+
|
6423
|
+
if (tab.dataset[_this2.tabIdentifierGetterValue] == newActiveTabIdentifier) {
|
6424
|
+
tabController.show();
|
6425
|
+
} else {
|
6426
|
+
tabController.hide();
|
6427
|
+
}
|
6428
|
+
});
|
6429
|
+
}
|
6430
|
+
}], [{
|
6431
|
+
key: "targets",
|
6432
|
+
get: function get() {
|
6433
|
+
return ["control", "tab"];
|
6434
|
+
}
|
6435
|
+
}, {
|
6436
|
+
key: "values",
|
6437
|
+
get: function get() {
|
6438
|
+
return {
|
6439
|
+
tabIdentifierGetter: String,
|
6440
|
+
tabControllerName: String
|
6441
|
+
};
|
6442
|
+
}
|
6443
|
+
}]);
|
6444
|
+
|
6445
|
+
return _default;
|
6446
|
+
}(Controller);
|
6447
|
+
|
6448
|
+
var _default$1 = /*#__PURE__*/function (_Controller) {
|
6449
|
+
_inherits(_default, _Controller);
|
6450
|
+
|
6451
|
+
var _super = _createSuper(_default);
|
6452
|
+
|
6453
|
+
function _default() {
|
6454
|
+
_classCallCheck(this, _default);
|
6455
|
+
|
6456
|
+
return _super.apply(this, arguments);
|
6457
|
+
}
|
6458
|
+
|
6459
|
+
_createClass(_default, [{
|
6460
|
+
key: "connect",
|
6461
|
+
value: function connect() {
|
6462
|
+
var tabContainer = this.element[this.tabContainerGetterValue];
|
6463
|
+
|
6464
|
+
if (tabContainer.activeTabIdentifier === this.identifierValue) {
|
6465
|
+
this.show();
|
6466
|
+
} else {
|
6467
|
+
this.hide();
|
6468
|
+
}
|
6469
|
+
}
|
6470
|
+
}, {
|
6471
|
+
key: "toggle",
|
6472
|
+
value: function toggle() {
|
6473
|
+
if (this.hasContentTarget) {
|
6474
|
+
this.hide();
|
6475
|
+
} else {
|
6476
|
+
this.show();
|
6477
|
+
}
|
6478
|
+
}
|
6479
|
+
}, {
|
6480
|
+
key: "show",
|
6481
|
+
value: function show() {
|
6482
|
+
if (this.hasContentTarget) {
|
6483
|
+
return;
|
6484
|
+
}
|
6485
|
+
|
6486
|
+
var pocketContent = this.pocketTarget.content.cloneNode(true);
|
6487
|
+
this.element.appendChild(pocketContent);
|
6488
|
+
}
|
6489
|
+
}, {
|
6490
|
+
key: "hide",
|
6491
|
+
value: function hide() {
|
6492
|
+
if (!this.hasContentTarget) {
|
6493
|
+
return;
|
6494
|
+
}
|
6495
|
+
|
6496
|
+
this.contentTarget.remove();
|
6497
|
+
}
|
6498
|
+
}], [{
|
6499
|
+
key: "targets",
|
6500
|
+
get: function get() {
|
6501
|
+
return ["pocket", "content"];
|
6502
|
+
}
|
6503
|
+
}, {
|
6504
|
+
key: "values",
|
6505
|
+
get: function get() {
|
6506
|
+
return {
|
6507
|
+
identifier: String,
|
6508
|
+
tabContainerGetter: String
|
6509
|
+
};
|
6510
|
+
}
|
6511
|
+
}]);
|
6512
|
+
|
6513
|
+
return _default;
|
6514
|
+
}(Controller);
|
6515
|
+
|
6385
6516
|
var _default = /*#__PURE__*/function (_Controller) {
|
6386
6517
|
_inherits(_default, _Controller);
|
6387
6518
|
|
@@ -6412,12 +6543,14 @@ var Super = (function (exports) {
|
|
6412
6543
|
}(Controller);
|
6413
6544
|
|
6414
6545
|
var StimulusApplication = Application.start();
|
6415
|
-
StimulusApplication.register("apply-template", _default$
|
6416
|
-
StimulusApplication.register("clean-filter-param", _default$
|
6417
|
-
StimulusApplication.register("clean-filter-params", _default$
|
6418
|
-
StimulusApplication.register("click-outside-to-close", _default$
|
6419
|
-
StimulusApplication.register("delete", _default$
|
6420
|
-
StimulusApplication.register("flatpickr", _default$
|
6546
|
+
StimulusApplication.register("apply-template", _default$8);
|
6547
|
+
StimulusApplication.register("clean-filter-param", _default$7);
|
6548
|
+
StimulusApplication.register("clean-filter-params", _default$6);
|
6549
|
+
StimulusApplication.register("click-outside-to-close", _default$5);
|
6550
|
+
StimulusApplication.register("delete", _default$4);
|
6551
|
+
StimulusApplication.register("flatpickr", _default$3);
|
6552
|
+
StimulusApplication.register("tab-container", _default$2);
|
6553
|
+
StimulusApplication.register("tab", _default$1);
|
6421
6554
|
StimulusApplication.register("toggle-pending-destruction", _default);
|
6422
6555
|
|
6423
6556
|
exports.StimulusApplication = StimulusApplication;
|
data/lib/super.rb
CHANGED
@@ -25,6 +25,7 @@ require "super/filter/operator"
|
|
25
25
|
require "super/filter/schema_types"
|
26
26
|
require "super/form"
|
27
27
|
require "super/form/builder"
|
28
|
+
require "super/form/field_transcript"
|
28
29
|
require "super/form/guesser"
|
29
30
|
require "super/form/inline_errors"
|
30
31
|
require "super/form/schema_types"
|
data/lib/super/error.rb
CHANGED
data/lib/super/filter.rb
CHANGED
@@ -3,58 +3,95 @@
|
|
3
3
|
module Super
|
4
4
|
class Filter
|
5
5
|
class FormObject
|
6
|
-
class
|
7
|
-
def initialize(
|
8
|
-
@
|
6
|
+
class AttributeForm
|
7
|
+
def initialize(model:, field_name:, operators:, params:)
|
8
|
+
@model = model
|
9
9
|
@field_name = field_name
|
10
|
-
@
|
11
|
-
@params = params
|
10
|
+
@operators = operators
|
11
|
+
@params = params || {}
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :model
|
15
|
+
attr_reader :field_name
|
16
|
+
attr_reader :operators
|
17
|
+
attr_reader :params
|
18
|
+
|
19
|
+
def each_operator
|
20
|
+
return enum_for(:each_operator) if !block_given?
|
21
|
+
|
22
|
+
@operators.each do |operator|
|
23
|
+
operator_form = OperatorForm.new(
|
24
|
+
operator: operator,
|
25
|
+
params: @params[operator.identifier]
|
26
|
+
)
|
27
|
+
|
28
|
+
yield(operator_form)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def humanized_attribute_name
|
33
|
+
@model.human_attribute_name(@field_name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class OperatorForm
|
38
|
+
NULLARY = :_apply
|
39
|
+
|
40
|
+
def initialize(operator:, params:)
|
41
|
+
@operator = operator
|
42
|
+
@params = params || {}
|
43
|
+
query_parameter_keys = operator.query_parameter_keys
|
44
|
+
query_parameter_keys = [NULLARY] if query_parameter_keys.empty?
|
12
45
|
@specified_values =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
query_field_name,
|
17
|
-
(params || {})[query_field_name],
|
18
|
-
]
|
19
|
-
end
|
20
|
-
.to_h
|
46
|
+
query_parameter_keys
|
47
|
+
.map { |key| [key, @params[key].presence&.strip] }
|
48
|
+
.to_h
|
21
49
|
|
22
50
|
@specified_values.each do |key, value|
|
23
51
|
define_singleton_method(key) { value }
|
24
52
|
end
|
25
53
|
end
|
26
54
|
|
27
|
-
|
28
|
-
|
29
|
-
|
55
|
+
def specified?
|
56
|
+
@specified_values.any? { |_key, value| value }
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_reader :operator
|
30
60
|
attr_reader :specified_values
|
31
61
|
|
32
|
-
def
|
33
|
-
|
62
|
+
def identifier
|
63
|
+
@operator.identifier
|
34
64
|
end
|
35
65
|
|
36
|
-
def
|
37
|
-
|
38
|
-
.map { |o| [o.name, o.identifier] }
|
39
|
-
.to_h
|
40
|
-
end
|
66
|
+
def each_field
|
67
|
+
return enum_for(:each_field) if !block_given?
|
41
68
|
|
42
|
-
|
43
|
-
|
69
|
+
@specified_values.each do |key, _value|
|
70
|
+
yield(key)
|
71
|
+
end
|
44
72
|
end
|
45
73
|
end
|
46
74
|
|
47
75
|
def initialize(model:, params:, schema:)
|
48
76
|
@model = model
|
49
|
-
@params = params
|
77
|
+
@params = params || {}
|
50
78
|
@schema = schema
|
51
79
|
|
52
80
|
@form_fields = {}
|
53
81
|
end
|
54
82
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
83
|
+
def each_attribute
|
84
|
+
return enum_for(:each_attribute) if !block_given?
|
85
|
+
|
86
|
+
@schema.fields.each do |field_name, field_operators|
|
87
|
+
attribute_form = AttributeForm.new(
|
88
|
+
model: @model,
|
89
|
+
field_name: field_name,
|
90
|
+
operators: field_operators,
|
91
|
+
params: @params[field_name]
|
92
|
+
)
|
93
|
+
|
94
|
+
yield(attribute_form)
|
58
95
|
end
|
59
96
|
end
|
60
97
|
|
@@ -63,32 +100,21 @@ module Super
|
|
63
100
|
end
|
64
101
|
|
65
102
|
def apply_changes(relation)
|
66
|
-
|
67
|
-
|
68
|
-
|
103
|
+
each_attribute do |attribute_form|
|
104
|
+
attribute_form.each_operator do |operator_form|
|
105
|
+
next if operator_form.specified_values.values.map(&:to_s).map(&:presence).none?
|
69
106
|
|
70
|
-
|
71
|
-
|
107
|
+
operator_behavior = operator_form.operator.behavior
|
108
|
+
updated_relation = operator_behavior.call(relation, attribute_form.field_name, **operator_form.specified_values)
|
72
109
|
|
73
|
-
|
74
|
-
|
110
|
+
if updated_relation.is_a?(ActiveRecord::Relation)
|
111
|
+
relation = updated_relation
|
112
|
+
end
|
75
113
|
end
|
76
114
|
end
|
77
115
|
|
78
116
|
relation
|
79
117
|
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def form_field_for(field_name)
|
84
|
-
@form_fields[field_name] ||=
|
85
|
-
FilterFormField.new(
|
86
|
-
humanized_field_name: @model.human_attribute_name(field_name),
|
87
|
-
field_name: field_name,
|
88
|
-
type: @schema.fields[field_name],
|
89
|
-
params: (@params || {})[field_name]
|
90
|
-
)
|
91
|
-
end
|
92
118
|
end
|
93
119
|
end
|
94
120
|
end
|
data/lib/super/filter/guesser.rb
CHANGED
@@ -2,100 +2,126 @@
|
|
2
2
|
|
3
3
|
module Super
|
4
4
|
class Filter
|
5
|
-
|
6
|
-
class Definition
|
7
|
-
def initialize(identifier, name, filter)
|
8
|
-
@identifier = identifier
|
9
|
-
@name = name
|
10
|
-
@filter = filter
|
11
|
-
end
|
12
|
-
|
13
|
-
attr_reader :identifier
|
14
|
-
attr_reader :name
|
15
|
-
|
16
|
-
def filter(*args)
|
17
|
-
@filter.call(args)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
5
|
+
class Operator
|
21
6
|
class << self
|
22
7
|
def registry
|
23
8
|
@registry ||= {}
|
24
9
|
end
|
25
10
|
|
26
|
-
def
|
27
|
-
|
28
|
-
registry["between"],
|
29
|
-
]
|
11
|
+
def [](key)
|
12
|
+
registry.fetch(key.to_s)
|
30
13
|
end
|
31
14
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
15
|
+
def register(identifier, operator)
|
16
|
+
identifier = identifier.to_s
|
17
|
+
if registry.key?(identifier)
|
18
|
+
raise Error::AlreadyRegistered, "Already registered: #{identifier}"
|
19
|
+
end
|
20
|
+
|
21
|
+
registry[identifier] = operator
|
37
22
|
end
|
38
23
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
registry["contain"],
|
44
|
-
registry["ncontain"],
|
45
|
-
registry["start"],
|
46
|
-
registry["end"],
|
47
|
-
]
|
24
|
+
def define(identifier, display, &block)
|
25
|
+
operator = new(identifier, display, &block).freeze
|
26
|
+
register(identifier, operator)
|
27
|
+
operator
|
48
28
|
end
|
29
|
+
end
|
49
30
|
|
50
|
-
|
51
|
-
|
52
|
-
|
31
|
+
def initialize(identifier, display, &behavior)
|
32
|
+
@identifier = identifier.to_s
|
33
|
+
@humanized_operator_name = display
|
34
|
+
self.behavior = behavior
|
35
|
+
end
|
36
|
+
|
37
|
+
def behavior=(behavior)
|
38
|
+
behavior_params = behavior.parameters
|
39
|
+
if behavior_params.size < 2
|
40
|
+
raise Error::ArgumentError, "Operator behavior must include `column_name` and `relation`"
|
41
|
+
end
|
42
|
+
if behavior_params[0][0] != :req && behavior_params[0][0] != :opt
|
43
|
+
raise Error::ArgumentError, "First argument `relation` must be a required, positional argument"
|
44
|
+
end
|
45
|
+
if behavior_params[1][0] != :req && behavior_params[1][0] != :opt
|
46
|
+
raise Error::ArgumentError, "Second argument `column_name` must be a required, positional argument"
|
47
|
+
end
|
48
|
+
if !behavior_params[2..-1].all? { |(type, _name)| type == :keyreq }
|
49
|
+
raise Error::ArgumentError, "All query parameter keys must be required, keyword arguments"
|
50
|
+
end
|
51
|
+
@behavior = behavior
|
52
|
+
@query_parameter_keys = behavior_params[2..-1].map(&:last)
|
53
|
+
end
|
53
54
|
|
54
|
-
|
55
|
+
attr_reader :identifier
|
56
|
+
attr_reader :query_parameter_keys
|
57
|
+
attr_reader :humanized_operator_name
|
55
58
|
|
56
|
-
|
59
|
+
def behavior(&block)
|
60
|
+
self.behavior = block if block_given?
|
61
|
+
@behavior
|
62
|
+
end
|
57
63
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|
64
|
+
define("eq", "Equals") do |relation, field, q:|
|
65
|
+
relation.where(field => q)
|
62
66
|
end
|
63
67
|
|
64
|
-
define("
|
65
|
-
relation.where(field =>
|
68
|
+
define("neq", "Doesn't equal") do |relation, field, q:|
|
69
|
+
relation.where.not(field => q)
|
66
70
|
end
|
67
71
|
|
68
|
-
define("
|
69
|
-
relation.where
|
72
|
+
define("null", "Is NULL") do |relation, field|
|
73
|
+
relation.where(field => nil)
|
70
74
|
end
|
71
75
|
|
72
|
-
define("
|
73
|
-
|
74
|
-
relation.where("#{field} LIKE ?", "%#{query}%")
|
76
|
+
define("nnull", "Isn't NULL") do |relation, field|
|
77
|
+
relation.where.not(field => nil)
|
75
78
|
end
|
76
79
|
|
77
|
-
define("
|
78
|
-
|
79
|
-
|
80
|
+
define("true", "Is true") do |relation, field|
|
81
|
+
relation.where(field => true)
|
82
|
+
end
|
83
|
+
|
84
|
+
define("false", "Is false") do |relation, field|
|
85
|
+
relation.where(field => false)
|
86
|
+
end
|
87
|
+
|
88
|
+
define("empty", "Is empty") do |relation, field|
|
89
|
+
relation.where(field => "")
|
80
90
|
end
|
81
91
|
|
82
|
-
define("
|
83
|
-
|
84
|
-
relation.where("#{field} LIKE ?", query)
|
92
|
+
define("nempty", "Isn't empty") do |relation, field|
|
93
|
+
relation.where.not(field => "")
|
85
94
|
end
|
86
95
|
|
87
|
-
define("
|
88
|
-
|
89
|
-
|
96
|
+
define("blank", "Is blank") do |relation, field|
|
97
|
+
relation.where(field => [nil, ""])
|
98
|
+
end
|
99
|
+
|
100
|
+
define("nblank", "Isn't blank") do |relation, field|
|
101
|
+
relation.where.not(field => [nil, ""])
|
102
|
+
end
|
103
|
+
|
104
|
+
define("contain", "Contains") do |relation, field, q:|
|
105
|
+
query = "%#{Compatability.sanitize_sql_like(q)}%"
|
106
|
+
if relation.connection.adapter_name == "PostgreSQL"
|
107
|
+
relation.where("#{field} ILIKE ?", query)
|
108
|
+
else
|
109
|
+
relation.where("#{field} LIKE ?", query)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
define("ncontain", "Doesn't contain") do |relation, field, q:|
|
114
|
+
query = "%#{Compatability.sanitize_sql_like(q)}%"
|
115
|
+
relation.where("#{field} NOT LIKE ?", query)
|
90
116
|
end
|
91
117
|
|
92
|
-
define("between", "
|
93
|
-
if
|
94
|
-
relation = relation.where("#{field} >= ?",
|
118
|
+
define("between", "Between") do |relation, field, q0:, q1:|
|
119
|
+
if q0.present?
|
120
|
+
relation = relation.where("#{field} >= ?", q0)
|
95
121
|
end
|
96
122
|
|
97
|
-
if
|
98
|
-
relation = relation.where("#{field} <= ?",
|
123
|
+
if q1.present?
|
124
|
+
relation = relation.where("#{field} <= ?", q1)
|
99
125
|
end
|
100
126
|
|
101
127
|
relation
|