File: core_refinements.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (244 lines) | stat: -rw-r--r-- 8,918 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# frozen-string-literal: true
#
# These are refinements to core classes that allow the Sequel
# DSL to be used without modifying the core classes directly.
# After loading the extension via:
#
#   Sequel.extension :core_refinements
#
# you can enable the refinements for particular files:
#
#   using Sequel::CoreRefinements

# :nocov:
raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
# :nocov:

module Sequel::CoreRefinements
  # :nocov:
  include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include
  # :nocov:
  INCLUDE_METH = include_meth
  private_constant :INCLUDE_METH

  refine Array do
    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, not matching all of the
    # conditions.
    #
    #   ~[[:a, true]] # SQL: (a IS NOT TRUE)
    #   ~[[:a, 1], [:b, [2, 3]]] # SQL: ((a != 1) OR (b NOT IN (2, 3)))
    def ~
      Sequel.~(self)
    end

    # Return a <tt>Sequel::SQL::CaseExpression</tt> with this array as the conditions and the given
    # default value and expression.
    #
    #   [[{a: [2,3]}, 1]].case(0) # SQL: CASE WHEN (a IN (2, 3)) THEN 1 ELSE 0 END
    #   [[:a, 1], [:b, 2]].case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
    def case(*args)
      ::Sequel::SQL::CaseExpression.new(self, *args)
    end

    # Return a <tt>Sequel::SQL::ValueList</tt> created from this array.  Used if this array contains
    # all two element arrays and you want it treated as an SQL value list (IN predicate) 
    # instead of as a conditions specifier (similar to a hash).  This is not necessary if you are using
    # this array as a value in a filter, but may be necessary if you are using it as a
    # value with placeholder SQL:
    #
    #   DB[:a].where([:a, :b]=>[[1, 2], [3, 4]]) # SQL: ((a, b) IN ((1, 2), (3, 4)))
    #   DB[:a].where('(a, b) IN ?', [[1, 2], [3, 4]]) # SQL: ((a, b) IN ((1 = 2) AND (3 = 4)))
    #   DB[:a].where('(a, b) IN ?', [[1, 2], [3, 4]].sql_value_list) # SQL: ((a, b) IN ((1, 2), (3, 4)))
    def sql_value_list
      ::Sequel::SQL::ValueList.new(self)
    end
    
    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, matching all of the
    # conditions.  Rarely do you need to call this explicitly, as Sequel generally
    # assumes that arrays of two element arrays specify this type of condition.  One case where
    # it can be necessary to use this is if you are using the object as a value in a filter hash
    # and want to use the = operator instead of the IN operator (which is used by default for
    # arrays of two element arrays).
    #
    #   [[:a, true]].sql_expr # SQL: (a IS TRUE)
    #   [[:a, 1], [:b, [2, 3]]].sql_expr # SQL: ((a = 1) AND (b IN (2, 3)))
    def sql_expr
      Sequel[self]
    end

    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, matching none
    # of the conditions.
    #
    #   [[:a, true]].sql_negate # SQL: (a IS NOT TRUE)
    #   [[:a, 1], [:b, [2, 3]]].sql_negate # SQL: ((a != 1) AND (b NOT IN (2, 3)))
    def sql_negate
      Sequel.negate(self)
    end

    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, matching any of the
    # conditions.
    #
    #   [[:a, true]].sql_or # SQL: (a IS TRUE)
    #   [[:a, 1], [:b, [2, 3]]].sql_or # SQL: ((a = 1) OR (b IN (2, 3)))
    def sql_or
      Sequel.or(self)
    end

    # Return a <tt>Sequel::SQL::StringExpression</tt> representing an SQL string made up of the
    # concatenation of this array's elements.  If an argument is passed
    # it is used in between each element of the array in the SQL
    # concatenation.
    #
    #   [:a].sql_string_join # SQL: a
    #   [:a, :b].sql_string_join # SQL: (a || b)
    #   [:a, 'b'].sql_string_join # SQL: (a || 'b')
    #   ['a', :b].sql_string_join(' ') # SQL: ('a' || ' ' || b)
    def sql_string_join(joiner=nil)
      Sequel.join(self, joiner)
    end
  end

  refine Hash do
    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching
    # all of the conditions in this hash and the condition specified by
    # the given argument.
    #
    #   {a: 1} & :b # SQL: ((a = 1) AND b)
    #   {a: true} & ~:b # SQL: ((a IS TRUE) AND NOT b)
    def &(ce)
      ::Sequel::SQL::BooleanExpression.new(:AND, self, ce)
    end

    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching
    # all of the conditions in this hash or the condition specified by
    # the given argument.
    #
    #   {a: 1} | :b # SQL: ((a = 1) OR b)
    #   {a: true} | ~:b # SQL: ((a IS TRUE) OR NOT b)
    def |(ce)
      ::Sequel::SQL::BooleanExpression.new(:OR, self, ce)
    end

    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, not matching all of the
    # conditions.
    #
    #   ~{a: true} # SQL: (a IS NOT TRUE)
    #   ~{a: 1, b: [2, 3]} # SQL: ((a != 1) OR (b NOT IN (2, 3)))
    def ~
      ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR, true)
    end

    # Return a <tt>Sequel::SQL::CaseExpression</tt> with this hash as the conditions and the given
    # default value.
    #
    #   {{a: [2,3]}=>1}.case(0) # SQL: CASE WHEN (a IN (2, 3)) THEN 1 ELSE 0 END
    #   {a: 1, b: 2}.case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
    def case(*args)
      ::Sequel::SQL::CaseExpression.new(to_a, *args)
    end

    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching all of the
    # conditions.  Rarely do you need to call this explicitly, as Sequel generally
    # assumes that hashes specify this type of condition.
    #
    #   {a: true}.sql_expr # SQL: (a IS TRUE)
    #   {a: 1, b: [2, 3]}.sql_expr # SQL: ((a = 1) AND (b IN (2, 3)))
    def sql_expr
      ::Sequel::SQL::BooleanExpression.from_value_pairs(self)
    end

    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching none
    # of the conditions.
    #
    #   {a: true}.sql_negate # SQL: (a IS NOT TRUE)
    #   {a: 1, b: [2, 3]}.sql_negate # SQL: ((a != 1) AND (b NOT IN (2, 3)))
    def sql_negate
      ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :AND, true)
    end

    # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching any of the
    # conditions.
    #
    #   {a: true}.sql_or # SQL: (a IS TRUE)
    #   {a: 1, b: [2, 3]}.sql_or # SQL: ((a = 1) OR (b IN (2, 3)))
    def sql_or
      ::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR)
    end
  end

  refine String do
    send include_meth, Sequel::SQL::AliasMethods
    send include_meth, Sequel::SQL::CastMethods

    # Converts a string into a <tt>Sequel::LiteralString</tt>, in order to override string
    # literalization, e.g.:
    #
    #   DB[:items].where(abc: 'def')
    #   # "SELECT * FROM items WHERE (abc = 'def')"
    #
    #   DB[:items].where(abc: 'def'.lit)
    #   # "SELECT * FROM items WHERE (abc = def)"
    #
    # You can also provide arguments, to create a <tt>Sequel::SQL::PlaceholderLiteralString</tt>:
    #
    #   DB[:items].select{|o| o.count('DISTINCT ?'.lit(:a))}
    #   # "SELECT count(DISTINCT a) FROM items"
    def lit(*args)
      args.empty? ? Sequel::LiteralString.new(self) : Sequel::SQL::PlaceholderLiteralString.new(self, args)
    end
    
    # Returns a <tt>Sequel::SQL::Blob</tt> that holds the same data as this string. Blobs provide proper
    # escaping of binary data.
    def to_sequel_blob
      ::Sequel::SQL::Blob.new(self)
    end
  end

  refine Symbol do
    send include_meth, Sequel::SQL::AliasMethods
    send include_meth, Sequel::SQL::CastMethods
    send include_meth, Sequel::SQL::OrderMethods
    send include_meth, Sequel::SQL::BooleanMethods
    send include_meth, Sequel::SQL::NumericMethods

    # :nocov:
    remove_method :* if RUBY_VERSION >= '3.1'
    # :nocov:

    send include_meth, Sequel::SQL::QualifyingMethods
    send include_meth, Sequel::SQL::StringMethods
    send include_meth, Sequel::SQL::SubscriptMethods
    send include_meth, Sequel::SQL::ComplexExpressionMethods

    # :nocov:
    if RUBY_VERSION >= '3.1'
      remove_method :*
      def *(ce=(arg=false;nil))
        if arg == false
          Sequel::SQL::ColumnAll.new(self)
        else
          Sequel::SQL::NumericExpression.new(:*, self, ce)
        end
      end

    end
    # :nocov:

    # Returns receiver wrapped in an <tt>Sequel::SQL::Identifier</tt>.
    #
    #   :ab.identifier # SQL: "a"
    def identifier
      Sequel::SQL::Identifier.new(self)
    end

    # Returns a <tt>Sequel::SQL::Function</tt> with this as the function name,
    # and the given arguments.
    #
    #   :now.sql_function # SQL: now()
    #   :sum.sql_function(:a) # SQL: sum(a)
    #   :concat.sql_function(:a, :b) # SQL: concat(a, b)
    def sql_function(*args)
      Sequel::SQL::Function.new(self, *args)
    end
  end
end