praxis 2.0.pre.5 → 2.0.pre.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rspec +0 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +1 -1
- data/Guardfile +2 -1
- data/Rakefile +1 -7
- data/TODO.md +28 -0
- data/lib/api_browser/package-lock.json +7110 -0
- data/lib/praxis.rb +6 -4
- data/lib/praxis/action_definition.rb +9 -16
- data/lib/praxis/application.rb +1 -2
- data/lib/praxis/bootloader_stages/routing.rb +2 -4
- data/lib/praxis/extensions/attribute_filtering.rb +2 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +148 -157
- data/lib/praxis/extensions/attribute_filtering/active_record_patches.rb +15 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/5x.rb +90 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_0.rb +68 -0
- data/lib/praxis/extensions/attribute_filtering/active_record_patches/6_1_plus.rb +58 -0
- data/lib/praxis/extensions/attribute_filtering/filter_tree_node.rb +35 -0
- data/lib/praxis/extensions/attribute_filtering/filtering_params.rb +9 -12
- data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +3 -2
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +7 -9
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +6 -9
- data/lib/praxis/extensions/pagination.rb +130 -0
- data/lib/praxis/extensions/pagination/active_record_pagination_handler.rb +42 -0
- data/lib/praxis/extensions/pagination/header_generator.rb +70 -0
- data/lib/praxis/extensions/pagination/ordering_params.rb +234 -0
- data/lib/praxis/extensions/pagination/pagination_handler.rb +68 -0
- data/lib/praxis/extensions/pagination/pagination_params.rb +374 -0
- data/lib/praxis/extensions/pagination/sequel_pagination_handler.rb +45 -0
- data/lib/praxis/handlers/json.rb +2 -0
- data/lib/praxis/handlers/www_form.rb +5 -0
- data/lib/praxis/handlers/{xml.rb → xml-sample.rb} +6 -0
- data/lib/praxis/mapper/active_model_compat.rb +23 -5
- data/lib/praxis/mapper/resource.rb +16 -9
- data/lib/praxis/mapper/sequel_compat.rb +1 -0
- data/lib/praxis/media_type.rb +1 -56
- data/lib/praxis/plugins/mapper_plugin.rb +1 -1
- data/lib/praxis/plugins/pagination_plugin.rb +71 -0
- data/lib/praxis/resource_definition.rb +4 -12
- data/lib/praxis/route.rb +2 -4
- data/lib/praxis/routing_config.rb +4 -8
- data/lib/praxis/tasks/routes.rb +9 -14
- data/lib/praxis/validation_handler.rb +1 -2
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +2 -3
- data/spec/functional_spec.rb +9 -6
- data/spec/praxis/action_definition_spec.rb +4 -16
- data/spec/praxis/api_general_info_spec.rb +6 -6
- data/spec/praxis/extensions/attribute_filtering/active_record_filter_query_builder_spec.rb +304 -0
- data/spec/praxis/extensions/attribute_filtering/filter_tree_node_spec.rb +39 -0
- data/spec/praxis/extensions/attribute_filtering/filtering_params_spec.rb +34 -0
- data/spec/praxis/extensions/field_expansion_spec.rb +6 -24
- data/spec/praxis/extensions/field_selection/active_record_query_selector_spec.rb +15 -11
- data/spec/praxis/extensions/field_selection/sequel_query_selector_spec.rb +4 -3
- data/spec/praxis/extensions/pagination/active_record_pagination_handler_spec.rb +130 -0
- data/spec/praxis/extensions/{field_selection/support → support}/spec_resources_active_model.rb +45 -2
- data/spec/praxis/extensions/{field_selection/support → support}/spec_resources_sequel.rb +0 -0
- data/spec/praxis/media_type_spec.rb +5 -129
- data/spec/praxis/request_spec.rb +3 -22
- data/spec/praxis/resource_definition_spec.rb +1 -1
- data/spec/praxis/response_definition_spec.rb +1 -5
- data/spec/praxis/route_spec.rb +2 -9
- data/spec/praxis/routing_config_spec.rb +4 -13
- data/spec/praxis/types/multipart_array_spec.rb +4 -21
- data/spec/spec_app/config/environment.rb +0 -2
- data/spec/spec_app/design/api.rb +1 -1
- data/spec/spec_app/design/media_types/instance.rb +0 -8
- data/spec/spec_app/design/media_types/volume.rb +0 -12
- data/spec/spec_app/design/resources/instances.rb +1 -2
- data/spec/spec_helper.rb +6 -0
- data/spec/support/spec_media_types.rb +0 -73
- metadata +35 -45
- data/spec/praxis/handlers/xml_spec.rb +0 -177
- data/spec/praxis/links_spec.rb +0 -68
data/lib/praxis.rb
CHANGED
@@ -50,7 +50,6 @@ module Praxis
|
|
50
50
|
autoload :RestfulDocGenerator, 'praxis/restful_doc_generator'
|
51
51
|
module Docs
|
52
52
|
autoload :Generator, 'praxis/docs/generator'
|
53
|
-
autoload :LinkBuilder, 'praxis/docs/link_builder'
|
54
53
|
autoload :OpenApiGenerator, 'praxis/docs/open_api_generator'
|
55
54
|
end
|
56
55
|
|
@@ -61,7 +60,6 @@ module Praxis
|
|
61
60
|
autoload :MultipartArray, 'praxis/types/multipart_array'
|
62
61
|
end
|
63
62
|
|
64
|
-
autoload :Links, 'praxis/links'
|
65
63
|
autoload :MediaType, 'praxis/media_type'
|
66
64
|
autoload :MediaTypeIdentifier, 'praxis/media_type_identifier'
|
67
65
|
autoload :Multipart, 'praxis/types/multipart'
|
@@ -89,15 +87,19 @@ module Praxis
|
|
89
87
|
autoload :MapperSelectors, 'praxis/extensions/mapper_selectors'
|
90
88
|
autoload :Rendering, 'praxis/extensions/rendering'
|
91
89
|
autoload :FieldExpansion, 'praxis/extensions/field_expansion'
|
90
|
+
autoload :AttributeFiltering, 'praxis/extensions/attribute_filtering'
|
92
91
|
autoload :ActiveRecordFilterQueryBuilder, 'praxis/extensions/attribute_filtering/active_record_filter_query_builder'
|
93
92
|
autoload :SequelFilterQueryBuilder, 'praxis/extensions/attribute_filtering/sequel_filter_query_builder'
|
93
|
+
autoload :Pagination, 'praxis/extensions/pagination'
|
94
|
+
module Pagination
|
95
|
+
autoload :ActiveRecordPaginationHandler, 'praxis/extensions/pagination/active_record_pagination_handler'
|
96
|
+
autoload :SequelPaginationHandler, 'praxis/extensions/pagination/sequel_pagination_handler'
|
97
|
+
end
|
94
98
|
end
|
95
99
|
|
96
100
|
module Handlers
|
97
101
|
autoload :Plain, 'praxis/handlers/plain'
|
98
102
|
autoload :JSON, 'praxis/handlers/json'
|
99
|
-
autoload :WWWForm, 'praxis/handlers/www_form'
|
100
|
-
autoload :XML, 'praxis/handlers/xml'
|
101
103
|
end
|
102
104
|
|
103
105
|
module BootloaderStages
|
@@ -12,9 +12,8 @@ module Praxis
|
|
12
12
|
|
13
13
|
attr_reader :name
|
14
14
|
attr_reader :resource_definition
|
15
|
-
attr_reader :
|
16
|
-
attr_reader :
|
17
|
-
attr_reader :named_routes
|
15
|
+
attr_reader :api_definition
|
16
|
+
attr_reader :route
|
18
17
|
attr_reader :responses
|
19
18
|
attr_reader :traits
|
20
19
|
|
@@ -37,7 +36,7 @@ module Praxis
|
|
37
36
|
@resource_definition = resource_definition
|
38
37
|
@responses = Hash.new
|
39
38
|
@metadata = Hash.new
|
40
|
-
@
|
39
|
+
@route = nil
|
41
40
|
@traits = []
|
42
41
|
|
43
42
|
if (media_type = resource_definition.media_type)
|
@@ -179,12 +178,7 @@ module Praxis
|
|
179
178
|
def routing(&block)
|
180
179
|
@routing_config.instance_eval &block
|
181
180
|
|
182
|
-
@
|
183
|
-
@primary_route = @routing_config.routes.first
|
184
|
-
@named_routes = @routing_config.routes.each_with_object({}) do |route, hash|
|
185
|
-
next if route.name.nil?
|
186
|
-
hash[route.name] = route
|
187
|
-
end
|
181
|
+
@route = @routing_config.route
|
188
182
|
end
|
189
183
|
|
190
184
|
|
@@ -232,9 +226,8 @@ module Praxis
|
|
232
226
|
end
|
233
227
|
hash[:traits] = traits if traits.any?
|
234
228
|
# FIXME: change to :routes along with api browser
|
235
|
-
|
236
|
-
|
237
|
-
end.compact
|
229
|
+
# FIXME: change urls to url ... (along with the browser)
|
230
|
+
hash[:urls] = [ ActionDefinition.url_description(route: route, params: self.params, params_example: params_example) ]
|
238
231
|
self.class.doc_decorations.each do |callback|
|
239
232
|
callback.call(self, hash)
|
240
233
|
end
|
@@ -252,10 +245,10 @@ module Praxis
|
|
252
245
|
|
253
246
|
def params_description(example:)
|
254
247
|
route_params = []
|
255
|
-
if
|
256
|
-
warn "Warning: No
|
248
|
+
if route.nil?
|
249
|
+
warn "Warning: No route defined for #{resource_definition.name}##{name}."
|
257
250
|
else
|
258
|
-
route_params =
|
251
|
+
route_params = route.path.
|
259
252
|
named_captures.
|
260
253
|
keys.
|
261
254
|
collect(&:to_sym)
|
data/lib/praxis/application.rb
CHANGED
@@ -61,8 +61,7 @@ module Praxis
|
|
61
61
|
|
62
62
|
builtin_handlers = {
|
63
63
|
'plain' => Praxis::Handlers::Plain,
|
64
|
-
'json' => Praxis::Handlers::JSON
|
65
|
-
'x-www-form-urlencoded' => Praxis::Handlers::WWWForm
|
64
|
+
'json' => Praxis::Handlers::JSON
|
66
65
|
}
|
67
66
|
# Register built-in handlers unless the app already provided its own
|
68
67
|
builtin_handlers.each_pair do |name, handler|
|
@@ -23,10 +23,8 @@ module Praxis
|
|
23
23
|
def execute
|
24
24
|
application.controllers.each do |controller|
|
25
25
|
controller.definition.actions.each do |action_name, action|
|
26
|
-
|
27
|
-
|
28
|
-
application.router.add_route target, route
|
29
|
-
end
|
26
|
+
target = target_factory(controller, action_name)
|
27
|
+
application.router.add_route target, action.route
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
@@ -1,178 +1,169 @@
|
|
1
|
+
|
2
|
+
|
1
3
|
module Praxis
|
2
4
|
module Extensions
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
5
|
+
module AttributeFiltering
|
6
|
+
ALIAS_TABLE_PREFIX = ''
|
7
|
+
require_relative 'active_record_patches'
|
8
|
+
|
9
|
+
class ActiveRecordFilterQueryBuilder
|
10
|
+
attr_reader :query, :model, :attr_to_column
|
11
|
+
|
12
|
+
# Base query to build upon
|
13
|
+
def initialize(query: , model:, filters_map:, debug: false)
|
14
|
+
@query = query
|
15
|
+
@model = model
|
16
|
+
@attr_to_column = filters_map
|
17
|
+
@logger = debug ? Logger.new(STDOUT) : nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def debug(msg)
|
21
|
+
@logger && @logger.puts(msg)
|
22
22
|
end
|
23
|
-
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
def generate(filters)
|
25
|
+
# Resolve the names and values first, based on filters_map
|
26
|
+
root_node = _convert_to_treenode(filters)
|
27
|
+
craft_filter_query(root_node, for_model: @model)
|
28
|
+
debug("SQL due to filters: #{@query.all.to_sql}")
|
29
|
+
@query
|
30
|
+
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
32
|
+
def craft_filter_query(nodetree, for_model:)
|
33
|
+
result = _compute_joins_and_conditions_data(nodetree, model: for_model)
|
34
|
+
@query = query.joins(result[:associations_hash]) unless result[:associations_hash].empty?
|
37
35
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
bindings = column_name.call(spec)
|
46
|
-
# A hash of bindings, consisting of a key with column name and a value to the query value
|
47
|
-
bindings.each do|col,val|
|
48
|
-
assoc_or_field, *rest = col.to_s.split('.')
|
49
|
-
expand_binding(column_name: assoc_or_field, rest: rest, op: spec[:op], value: val, use_this_name_for_clause: @last_join_alias)
|
50
|
-
end
|
51
|
-
else
|
52
|
-
assoc_or_field, *rest = column_name.to_s.split('.')
|
53
|
-
expand_binding(column_name: assoc_or_field, rest: rest, **spec, use_this_name_for_clause: @last_join_alias)
|
36
|
+
result[:conditions].each do |condition|
|
37
|
+
filter_name = condition[:name]
|
38
|
+
filter_value = condition[:value]
|
39
|
+
column_prefix = condition[:column_prefix]
|
40
|
+
|
41
|
+
colo = condition[:model].columns_hash[filter_name.to_s]
|
42
|
+
add_clause(column_prefix: column_prefix, column_object: colo, op: condition[:op], value: filter_value)
|
54
43
|
end
|
55
44
|
end
|
56
|
-
query
|
57
|
-
end
|
58
45
|
|
59
|
-
|
60
|
-
def do_join(query, assoc , source_alias, table_alias)
|
61
|
-
reflection = query.reflections[assoc.to_s]
|
62
|
-
do_join_reflection( query, reflection, source_alias, table_alias )
|
63
|
-
end
|
46
|
+
private
|
64
47
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
[c.quote_table_name(reflection.klass.table_name),
|
83
|
-
c.quote_table_name(table_alias),
|
84
|
-
c.quote_table_name(source_alias),
|
85
|
-
c.quote_column_name(reflection.active_record.primary_key),
|
86
|
-
c.quote_table_name(table_alias),
|
87
|
-
c.quote_column_name(reflection.foreign_key)
|
88
|
-
]
|
89
|
-
|
90
|
-
if reflection.type # && reflection.options[:as]....
|
91
|
-
# addition = " AND \"#{table_alias}\".\"#{reflection.type}\" = \'#{reflection.active_record.class_name}\'"
|
92
|
-
addition = " AND %s.%s = %s" % \
|
93
|
-
[ c.quote_table_name(table_alias),
|
94
|
-
c.quote_table_name(reflection.type),
|
95
|
-
c.quote(reflection.active_record.class_name)]
|
96
|
-
|
97
|
-
join_clause += addition
|
48
|
+
# Resolve and convert from filters, to a more manageable and param-type-independent structure
|
49
|
+
def _convert_to_treenode(filters)
|
50
|
+
# Resolve the names and values first, based on filters_map
|
51
|
+
resolved_array = []
|
52
|
+
filters.parsed_array.each do |filter|
|
53
|
+
mapped_value = attr_to_column[filter[:name]]
|
54
|
+
raise "Filtering by #{filter[:name]} not allowed (no mapping found)" unless mapped_value
|
55
|
+
bindings_array = \
|
56
|
+
if mapped_value.is_a?(Proc)
|
57
|
+
result = mapped_value.call(filter)
|
58
|
+
# Result could be an array of hashes (each hash has name/op/value to identify a condition)
|
59
|
+
result.is_a?(Array) ? result : [result]
|
60
|
+
else
|
61
|
+
# For non-procs there's only 1 filter and 1 value (we're just overriding the mapped value)
|
62
|
+
[filter.merge( name: mapped_value)]
|
63
|
+
end
|
64
|
+
resolved_array = resolved_array + bindings_array
|
98
65
|
end
|
99
|
-
|
100
|
-
when ActiveRecord::Reflection::ThroughReflection
|
101
|
-
#puts "TODO: choose different alias (based on matching table type...)"
|
102
|
-
talias = pick_alias(reflection.through_reflection.table_name)
|
103
|
-
salias = source_alias
|
104
|
-
|
105
|
-
query = do_join_reflection(query, reflection.through_reflection, salias, talias)
|
106
|
-
#puts "TODO: choose different alias ?????????"
|
107
|
-
salias = talias
|
108
|
-
|
109
|
-
through_model = reflection.through_reflection.klass
|
110
|
-
through_assoc = reflection.name
|
111
|
-
final_reflection = reflection.source_reflection
|
112
|
-
|
113
|
-
do_join_reflection(query, final_reflection, salias, table_alias)
|
114
|
-
else
|
115
|
-
raise "Joins for this association type are currently UNSUPPORTED: #{reflection.inspect}"
|
66
|
+
FilterTreeNode.new(resolved_array, path: [ALIAS_TABLE_PREFIX])
|
116
67
|
end
|
117
|
-
end
|
118
68
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
69
|
+
# Calculate join tree and conditions array for the nodetree object and its children
|
70
|
+
def _compute_joins_and_conditions_data(nodetree, model:)
|
71
|
+
h = {}
|
72
|
+
conditions = []
|
73
|
+
nodetree.children.each do |name, child|
|
74
|
+
child_model = model.reflections[name.to_s].klass
|
75
|
+
result = _compute_joins_and_conditions_data(child, model: child_model)
|
76
|
+
h[name] = result[:associations_hash]
|
77
|
+
conditions += result[:conditions]
|
78
|
+
end
|
79
|
+
column_prefix = nodetree.path == [ALIAS_TABLE_PREFIX] ? model.table_name : nodetree.path.join('/')
|
80
|
+
#column_prefix = nodetree.path == [ALIAS_TABLE_PREFIX] ? nil : nodetree.path.join('/')
|
81
|
+
nodetree.conditions.each do |condition|
|
82
|
+
conditions += [condition.merge(column_prefix: column_prefix, model: model)]
|
83
|
+
end
|
84
|
+
{associations_hash: h, conditions: conditions}
|
131
85
|
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def attr_to_column
|
135
|
-
# Class method defined by the subclassing Class (using .for)
|
136
|
-
self.class.attr_to_column
|
137
|
-
end
|
138
86
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
87
|
+
def add_clause(column_prefix:, column_object:, op:, value:)
|
88
|
+
@query = @query.references(column_prefix) #Mark where clause referencing the appropriate alias
|
89
|
+
likeval = get_like_value(value)
|
90
|
+
case op
|
91
|
+
when '!' # name! means => name IS NOT NULL (and the incoming value is nil)
|
92
|
+
op = '!='
|
93
|
+
value = nil # Enforce it is indeed nil (should be)
|
94
|
+
when '!!'
|
95
|
+
op = '='
|
96
|
+
value = nil # Enforce it is indeed nil (should be)
|
97
|
+
end
|
98
|
+
@query = case op
|
99
|
+
when '='
|
100
|
+
if likeval
|
101
|
+
add_safe_where(tab: column_prefix, col: column_object, op: 'LIKE', value: likeval)
|
102
|
+
else
|
103
|
+
quoted_right = quote_right_part(value: value, column_object: column_object, negative: false)
|
104
|
+
query.where("#{quote_column_path(column_prefix, column_object)} #{quoted_right}")
|
105
|
+
end
|
106
|
+
when '!='
|
107
|
+
if likeval
|
108
|
+
add_safe_where(tab: column_prefix, col: column_object, op: 'NOT LIKE', value: likeval)
|
109
|
+
else
|
110
|
+
quoted_right = quote_right_part(value: value, column_object: column_object, negative: true)
|
111
|
+
query.where("#{quote_column_path(column_prefix, column_object)} #{quoted_right}")
|
112
|
+
end
|
113
|
+
when '>'
|
114
|
+
add_safe_where(tab: column_prefix, col: column_object, op: '>', value: value)
|
115
|
+
when '<'
|
116
|
+
add_safe_where(tab: column_prefix, col: column_object, op: '<', value: value)
|
117
|
+
when '>='
|
118
|
+
add_safe_where(tab: column_prefix, col: column_object, op: '>=', value: value)
|
119
|
+
when '<='
|
120
|
+
add_safe_where(tab: column_prefix, col: column_object, op: '<=', value: value)
|
153
121
|
else
|
154
|
-
|
122
|
+
raise "Unsupported Operator!!! #{op}"
|
155
123
|
end
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
when '<='
|
163
|
-
query.where("#{column_name} <= ?", value)
|
164
|
-
else
|
165
|
-
raise "Unsupported Operator!!! #{op}"
|
166
|
-
end
|
167
|
-
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def add_safe_where(tab:, col:, op:, value:)
|
127
|
+
quoted_value = query.connection.quote_default_expression(value,col)
|
128
|
+
query.where("#{quote_column_path(tab, col)} #{op} #{quoted_value}")
|
129
|
+
end
|
168
130
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
131
|
+
def quote_column_path(prefix, column_object)
|
132
|
+
c = query.connection
|
133
|
+
quoted_column = c.quote_column_name(column_object.name)
|
134
|
+
if prefix
|
135
|
+
quoted_table = c.quote_table_name(prefix)
|
136
|
+
"#{quoted_table}.#{quoted_column}"
|
137
|
+
else
|
138
|
+
quoted_column
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def quote_right_part(value:, column_object:, negative:)
|
143
|
+
conn = query.connection
|
144
|
+
if value.nil?
|
145
|
+
no = negative ? ' NOT' : ''
|
146
|
+
"IS#{no} #{conn.quote_default_expression(value,column_object)}"
|
147
|
+
elsif value.is_a?(Array)
|
148
|
+
no = negative ? 'NOT ' : ''
|
149
|
+
list = value.map{|v| conn.quote_default_expression(v,column_object)}
|
150
|
+
"#{no}IN (#{list.join(',')})"
|
151
|
+
elsif value && value.is_a?(Range)
|
152
|
+
raise "TODO!"
|
153
|
+
else
|
154
|
+
op = negative ? '<>' : '='
|
155
|
+
"#{op} #{conn.quote_default_expression(value,column_object)}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns nil if the value was not a fuzzzy pattern
|
160
|
+
def get_like_value(value)
|
161
|
+
if value.is_a?(String) && (value[-1] == '*' || value[0] == '*')
|
162
|
+
likeval = value.dup
|
163
|
+
likeval[-1] = '%' if value[-1] == '*'
|
164
|
+
likeval[0] = '%' if value[0] == '*'
|
165
|
+
likeval
|
166
|
+
end
|
176
167
|
end
|
177
168
|
end
|
178
169
|
end
|