File: pg_inet_ops.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (204 lines) | stat: -rw-r--r-- 6,124 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
# frozen-string-literal: true
#
# The pg_inet_ops extension adds support to Sequel's DSL to make
# it easier to call PostgreSQL inet functions and operators.
#
# To load the extension:
#
#   Sequel.extension :pg_inet_ops
#
# The most common usage is passing an expression to Sequel.pg_inet_op:
#
#   r = Sequel.pg_inet_op(:inet)
#
# Also, on most Sequel expression objects, you can call the pg_inet
# method:
#
#   r = Sequel[:ip].pg_inet
#
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
# or you have loaded the core_refinements extension
# and have activated refinements for the file, you can also use Symbol#pg_inet:
#
#   r = :inet.pg_inet
#
# This creates a Sequel::Postgres::InetOp object that can be used
# for easier querying:
#
#   ~r                                 # ~inet
#   r & other                          # inet & other
#   r | other                          # inet | other
#   r << :other                        # inet << other
#   r >> :other                        # inet >> other
#
#   r.contained_by(:other)             # inet << other
#   r.contained_by_or_equals(:other)   # inet <<= other
#   r.contains(:other)                 # inet >> other
#   r.contains_or_equals(:other)       # inet >>= other
#   r.contains_or_contained_by(:other) # inet && other
#
#   r.abbrev           # abbrev(inet)
#   r.broadcast        # broadcast(inet)
#   r.family           # family(inet)
#   r.host             # host(inet)
#   r.hostmask         # hostmask(inet)
#   r.masklen          # masklen(inet)
#   r.netmask          # netmask(inet)
#   r.network          # network(inet)
#   r.set_masklen(16)  # set_masklen(inet, 16)
#   r.text             # text(inet)
#
# If a String or IPAddr instance is passed to Sequel.pg_inet_op, it will automatically
# be cast to +inet+.  To treat the object as a +cidr+, you must cast it before passing
# it to Sequel.pg_inet_op:
#
#   r = Sequel.pg_inet_op(Sequel.cast('1.2.3.4', :cidr))
#
# See the PostgreSQL network function and operator documentation for more
# details on what these functions and operators do.
#
# Related module: Sequel::Postgres::InetOp

require 'ipaddr'

module Sequel
  module Postgres
    # The InetOp class is a simple container for a single object that
    # defines methods that yield Sequel expression objects representing
    # PostgreSQL inet operators and functions.
    #
    # Most methods in this class are defined via metaprogramming, see
    # the pg_inet_ops extension documentation for details on the API.
    class InetOp < Sequel::SQL::Wrapper
      include Sequel::SQL::BitwiseMethods

      # For String and IPAddr instances, wrap them in a cast to inet,
      # to avoid ambiguity issues when calling operator methods.
      def initialize(v)
        case v
        when ::Sequel::LiteralString
          # nothing
        when String, IPAddr
          v = Sequel.cast(v, :inet)
        end
        super
      end

      OPERATORS = {
        :contained_by_or_equals => ["(".freeze, " <<= ".freeze, ")".freeze].freeze,
        :contains_or_equals => ["(".freeze, " >>= ".freeze, ")".freeze].freeze,
        :contains_or_contained_by => ["(".freeze, " && ".freeze, ")".freeze].freeze,
      }.freeze

      OPERATORS.keys.each do |f|
        class_eval("def #{f}(v) Sequel::SQL::BooleanExpression.new(:NOOP, operator(:#{f}, v)) end", __FILE__, __LINE__)
      end

      %w'<< >>'.each do |f|
        class_eval("def #{f}(v) Sequel::SQL::BooleanExpression.new(:NOOP, super) end", __FILE__, __LINE__)
      end

      %w'& | +'.each do |f|
        class_eval("def #{f}(v) self.class.new(super) end", __FILE__, __LINE__)
      end

      %w'abbrev host text'.each do |f|
        class_eval("def #{f}() Sequel::SQL::StringExpression.new(:NOOP, function(:#{f})) end", __FILE__, __LINE__)
      end

      %w'family masklen'.each do |f|
        class_eval("def #{f}() Sequel::SQL::NumericExpression.new(:NOOP, function(:#{f})) end", __FILE__, __LINE__)
      end

      %w'broadcast hostmask netmask network'.each do |f|
        class_eval("def #{f}() self.class.new(function(:#{f})) end", __FILE__, __LINE__)
      end

      # Return the receiver.
      def pg_inet
        self
      end

      # Return an expression for the bitwise NOT of the receiver 
      def ~
        self.class.new(super)
      end

      # Return an expression for the subtraction of the argument from the receiver
      def -(v)
        case v
        when Integer
          self.class.new(super)
        else
          Sequel::SQL::NumericExpression.new(:NOOP, super)
        end
      end

      # Return an expression for the calling of the set_masklen function with the receiver and the given argument
      def set_masklen(v)
        self.class.new(Sequel::SQL::Function.new(:set_masklen, self, v))
      end

      alias contained_by <<
      alias contains >> 

      undef_method :*, :/

      private

      # Handle PostgreSQL specific operator types
      def operator(type, other)
        Sequel::SQL::PlaceholderLiteralString.new(OPERATORS[type], [value, other])
      end

      # Return a function called with the receiver.
      def function(name)
        Sequel::SQL::Function.new(name, self)
      end
    end

    module InetOpMethods
      # Wrap the receiver in an InetOp so you can easily use the PostgreSQL
      # inet functions and operators with it.
      def pg_inet
        InetOp.new(self)
      end
    end
  end

  module SQL::Builders
    # Return the expression wrapped in the Postgres::InetOp.
    def pg_inet_op(v)
      case v
      when Postgres::InetOp
        v
      else
        Postgres::InetOp.new(v)
      end
    end
  end

  class SQL::GenericExpression
    include Sequel::Postgres::InetOpMethods
  end

  class LiteralString
    include Sequel::Postgres::InetOpMethods
  end
end

# :nocov:
if Sequel.core_extensions?
  class Symbol
    include Sequel::Postgres::InetOpMethods
  end
end

if defined?(Sequel::CoreRefinements)
  module Sequel::CoreRefinements
    refine Symbol do
      send INCLUDE_METH, Sequel::Postgres::InetOpMethods
    end
  end
end
# :nocov: