sequel 3.35.0 → 3.36.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/CHANGELOG +78 -0
  2. data/Rakefile +3 -3
  3. data/bin/sequel +3 -1
  4. data/doc/advanced_associations.rdoc +154 -11
  5. data/doc/migration.rdoc +18 -0
  6. data/doc/object_model.rdoc +541 -0
  7. data/doc/opening_databases.rdoc +4 -1
  8. data/doc/release_notes/3.36.0.txt +245 -0
  9. data/doc/schema_modification.rdoc +0 -6
  10. data/lib/sequel/adapters/do/mysql.rb +7 -0
  11. data/lib/sequel/adapters/jdbc.rb +11 -3
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -5
  13. data/lib/sequel/adapters/jdbc/postgresql.rb +10 -8
  14. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  15. data/lib/sequel/adapters/mock.rb +2 -6
  16. data/lib/sequel/adapters/mysql.rb +3 -9
  17. data/lib/sequel/adapters/mysql2.rb +12 -11
  18. data/lib/sequel/adapters/postgres.rb +32 -40
  19. data/lib/sequel/adapters/shared/mssql.rb +15 -11
  20. data/lib/sequel/adapters/shared/mysql.rb +28 -3
  21. data/lib/sequel/adapters/shared/oracle.rb +5 -0
  22. data/lib/sequel/adapters/shared/postgres.rb +59 -5
  23. data/lib/sequel/adapters/shared/sqlite.rb +3 -13
  24. data/lib/sequel/adapters/sqlite.rb +0 -7
  25. data/lib/sequel/adapters/swift/mysql.rb +2 -5
  26. data/lib/sequel/adapters/tinytds.rb +1 -2
  27. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  28. data/lib/sequel/connection_pool/threaded.rb +9 -1
  29. data/lib/sequel/database/dataset_defaults.rb +3 -1
  30. data/lib/sequel/database/misc.rb +7 -1
  31. data/lib/sequel/database/query.rb +11 -3
  32. data/lib/sequel/database/schema_generator.rb +40 -9
  33. data/lib/sequel/database/schema_methods.rb +6 -1
  34. data/lib/sequel/dataset/actions.rb +5 -5
  35. data/lib/sequel/dataset/prepared_statements.rb +3 -1
  36. data/lib/sequel/dataset/query.rb +1 -1
  37. data/lib/sequel/extensions/migration.rb +28 -0
  38. data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -9
  39. data/lib/sequel/extensions/pg_inet.rb +89 -0
  40. data/lib/sequel/extensions/pg_json.rb +178 -0
  41. data/lib/sequel/extensions/schema_dumper.rb +24 -6
  42. data/lib/sequel/model/associations.rb +19 -15
  43. data/lib/sequel/model/base.rb +11 -12
  44. data/lib/sequel/plugins/composition.rb +1 -2
  45. data/lib/sequel/plugins/eager_each.rb +59 -0
  46. data/lib/sequel/plugins/json_serializer.rb +41 -4
  47. data/lib/sequel/plugins/nested_attributes.rb +72 -52
  48. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  49. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/postgres_spec.rb +271 -1
  52. data/spec/adapters/sqlite_spec.rb +11 -0
  53. data/spec/core/connection_pool_spec.rb +26 -1
  54. data/spec/core/database_spec.rb +19 -0
  55. data/spec/core/dataset_spec.rb +45 -5
  56. data/spec/core/expression_filters_spec.rb +31 -67
  57. data/spec/core/mock_adapter_spec.rb +4 -0
  58. data/spec/extensions/core_extensions_spec.rb +83 -0
  59. data/spec/extensions/eager_each_spec.rb +34 -0
  60. data/spec/extensions/inflector_spec.rb +0 -4
  61. data/spec/extensions/json_serializer_spec.rb +32 -1
  62. data/spec/extensions/migration_spec.rb +28 -0
  63. data/spec/extensions/nested_attributes_spec.rb +134 -1
  64. data/spec/extensions/optimistic_locking_spec.rb +15 -1
  65. data/spec/extensions/pg_hstore_spec.rb +1 -1
  66. data/spec/extensions/pg_inet_spec.rb +44 -0
  67. data/spec/extensions/pg_json_spec.rb +101 -0
  68. data/spec/extensions/prepared_statements_spec.rb +30 -0
  69. data/spec/extensions/rcte_tree_spec.rb +9 -0
  70. data/spec/extensions/schema_dumper_spec.rb +195 -7
  71. data/spec/extensions/serialization_spec.rb +4 -0
  72. data/spec/extensions/spec_helper.rb +9 -1
  73. data/spec/extensions/tactical_eager_loading_spec.rb +8 -0
  74. data/spec/integration/database_test.rb +5 -1
  75. data/spec/integration/prepared_statement_test.rb +20 -2
  76. data/spec/model/associations_spec.rb +27 -0
  77. data/spec/model/base_spec.rb +54 -0
  78. data/spec/model/model_spec.rb +6 -0
  79. data/spec/model/record_spec.rb +18 -0
  80. data/spec/rcov.opts +2 -0
  81. metadata +14 -3
@@ -21,13 +21,10 @@ module Sequel
21
21
  db_type == 'tinyint(1)' ? :boolean : super
22
22
  end
23
23
 
24
- # By default, MySQL 'where id is null' selects the last inserted id.
25
- # Turn that off unless explicitly enabled.
24
+ # Apply the connectiong setting SQLs for every new connection.
26
25
  def setup_connection(conn)
26
+ mysql_connection_setting_sqls.each{|sql| log_yield(sql){conn.execute(sql)}}
27
27
  super
28
- sql = "SET SQL_AUTO_IS_NULL=0"
29
- log_yield(sql){conn.execute(sql)} unless opts[:auto_is_null]
30
- conn
31
28
  end
32
29
  end
33
30
 
@@ -7,8 +7,7 @@ module Sequel
7
7
  include Sequel::MSSQL::DatabaseMethods
8
8
  set_adapter_scheme :tinytds
9
9
 
10
- # Transfer the :host and :user options to the
11
- # :dataserver and :username options.
10
+ # Transfer the :user option to the :username option.
12
11
  def connect(server)
13
12
  opts = server_opts(server)
14
13
  opts[:username] = opts[:user]
@@ -221,7 +221,11 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
221
221
  if @connections_to_remove.include?(conn)
222
222
  remove(thread, conn, server)
223
223
  else
224
- available_connections(server) << allocated(server).delete(thread)
224
+ if @queue
225
+ available_connections(server).unshift(allocated(server).delete(thread))
226
+ else
227
+ available_connections(server) << allocated(server).delete(thread)
228
+ end
225
229
  end
226
230
  end
227
231
 
@@ -13,6 +13,9 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
13
13
  attr_reader :allocated
14
14
 
15
15
  # The following additional options are respected:
16
+ # * :connection_handling - Set how to handle available connections. By default,
17
+ # uses a a stack for performance. Can be set to :queue to use a queue, which reduces
18
+ # the chances of connections becoming stale.
16
19
  # * :max_connections - The maximum number of connections the connection pool
17
20
  # will open (default 4)
18
21
  # * :pool_sleep_time - The amount of time to sleep before attempting to acquire
@@ -24,6 +27,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
24
27
  @max_size = Integer(opts[:max_connections] || 4)
25
28
  raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
26
29
  @mutex = Mutex.new
30
+ @queue = opts[:connection_handling] == :queue
27
31
  @available_connections = []
28
32
  @allocated = {}
29
33
  @timeout = Integer(opts[:pool_timeout] || 5)
@@ -153,7 +157,11 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
153
157
  # Releases the connection assigned to the supplied thread back to the pool.
154
158
  # The calling code should already have the mutex before calling this.
155
159
  def release(thread)
156
- @available_connections << @allocated.delete(thread)
160
+ if @queue
161
+ @available_connections.unshift(@allocated.delete(thread))
162
+ else
163
+ @available_connections << @allocated.delete(thread)
164
+ end
157
165
  end
158
166
 
159
167
  # Yield to the block while inside the mutex. The calling code should NOT
@@ -50,7 +50,9 @@ module Sequel
50
50
  # an optional options hash.
51
51
  attr_reader :dataset_class
52
52
 
53
- # The default schema to use, generally should be nil.
53
+ # The default schema to use, generally should be nil. This sets
54
+ # the default schema used for some schema modification and
55
+ # introspection queries, but does not effect most dataset code.
54
56
  attr_accessor :default_schema
55
57
 
56
58
  # If the database has any dataset modules associated with it,
@@ -26,7 +26,7 @@ module Sequel
26
26
  # options hash.
27
27
  #
28
28
  # Accepts the following options:
29
- # :default_schema :: The default schema to use, should generally be nil
29
+ # :default_schema :: The default schema to use, see #default_schema.
30
30
  # :disconnection_proc :: A proc used to disconnect the connection
31
31
  # :identifier_input_method :: A string method symbol to call on identifiers going into the database
32
32
  # :identifier_output_method :: A string method symbol to call on identifiers coming from the database
@@ -114,6 +114,12 @@ module Sequel
114
114
  Sequel.convert_output_timestamp(v, timezone)
115
115
  end
116
116
 
117
+ # Whether the database uses a global namespace for the index. If
118
+ # false, the indexes are going to be namespaced per table.
119
+ def global_index_namespace?
120
+ true
121
+ end
122
+
117
123
  # Return true if already in a transaction given the options,
118
124
  # false otherwise. Respects the :server option for selecting
119
125
  # a shard.
@@ -55,8 +55,8 @@ module Sequel
55
55
  #
56
56
  # DB[:items].filter(:id=>1).prepare(:first, :sa)
57
57
  # DB.call(:sa) # SELECT * FROM items WHERE id = 1
58
- def call(ps_name, hash={})
59
- prepared_statement(ps_name).call(hash)
58
+ def call(ps_name, hash={}, &block)
59
+ prepared_statement(ps_name).call(hash, &block)
60
60
  end
61
61
 
62
62
  # Executes the given SQL on the database. This method should be overridden in descendants.
@@ -232,7 +232,7 @@ module Sequel
232
232
  # either all statements are successful or none of the statements are
233
233
  # successful. Note that MySQL MyISAM tabels do not support transactions.
234
234
  #
235
- # The following options are respected:
235
+ # The following general options are respected:
236
236
  #
237
237
  # :isolation :: The transaction isolation level to use for this transaction,
238
238
  # should be :uncommitted, :committed, :repeatable, or :serializable,
@@ -249,6 +249,14 @@ module Sequel
249
249
  # only respected if the database/adapter supports savepoints. By
250
250
  # default Sequel will reuse an existing transaction, so if you want to
251
251
  # use a savepoint you must use this option.
252
+ #
253
+ # PostgreSQL specific options:
254
+ #
255
+ # :deferrable :: (9.1+) If present, set to DEFERRABLE if true or NOT DEFERRABLE if false.
256
+ # :read_only :: If present, set to READ ONLY if true or READ WRITE if false.
257
+ # :synchronous :: if non-nil, set synchronous_commit
258
+ # appropriately. Valid values true, :on, false, :off, :local (9.1+),
259
+ # and :remote_write (9.2+).
252
260
  def transaction(opts={}, &block)
253
261
  synchronize(opts[:server]) do |conn|
254
262
  return yield(conn) if already_in_transaction?(conn, opts)
@@ -98,14 +98,8 @@ module Sequel
98
98
  # (:restrict, :cascade, :set_null, :set_default, :no_action).
99
99
  # :primary_key :: Make the column as a single primary key column. This should only
100
100
  # be used if you have a single, nonautoincrementing primary key column.
101
- # :size :: The size of the column, generally used with string
102
- # columns to specify the maximum number of characters the column will hold.
103
- # An array of two integers can be provided to set the size and the
104
- # precision, respectively, of decimal columns.
105
101
  # :unique :: Mark the column as unique, generally has the same effect as
106
102
  # creating a unique index on the column.
107
- # :unsigned :: Make the column type unsigned, only useful for integer
108
- # columns.
109
103
  def column(name, type, opts = {})
110
104
  columns << {:name => name, :type => type}.merge(opts)
111
105
  if index_opts = opts[:index]
@@ -151,6 +145,12 @@ module Sequel
151
145
  end
152
146
 
153
147
  # Add a full text index on the given columns to the DDL.
148
+ #
149
+ # PostgreSQL specific options:
150
+ # :language :: Set a language to use for the index (default: simple).
151
+ #
152
+ # Microsoft SQL Server specific options:
153
+ # :key_index :: The KEY INDEX to use for the full text index.
154
154
  def full_text_index(columns, opts = {})
155
155
  index(columns, opts.merge(:type => :full_text))
156
156
  end
@@ -161,12 +161,26 @@ module Sequel
161
161
  end
162
162
 
163
163
  # Add an index on the given column(s) with the given options to the DDL.
164
- # The available options are:
164
+ # General options:
165
165
  #
166
+ # :name :: The name to use for the index. If not given, a default name
167
+ # based on the table and columns is used.
166
168
  # :type :: The type of index to use (only supported by some databases)
167
169
  # :unique :: Make the index unique, so duplicate values are not allowed.
168
170
  # :where :: Create a partial index (only supported by some databases)
169
171
  #
172
+ # PostgreSQL specific options:
173
+ #
174
+ # :concurrently :: Create the index concurrently, so it doesn't block
175
+ # operations on the table while the index is being
176
+ # built.
177
+ # :op_class :: Use a specific operator class in the index.
178
+ #
179
+ # Microsoft SQL Server specific options:
180
+ #
181
+ # :include :: Include additional column values in the index, without
182
+ # actually indexing on those values.
183
+ #
170
184
  # index :name
171
185
  # # CREATE INDEX table_name_index ON table (name)
172
186
  #
@@ -345,7 +359,10 @@ module Sequel
345
359
  @operations << {:op => :drop_column, :name => name}.merge(opts)
346
360
  end
347
361
 
348
- # Remove a constraint from the DDL for the table.
362
+ # Remove a constraint from the DDL for the table. MySQL/SQLite specific options:
363
+ #
364
+ # :type :: Set the type of constraint to drop, either :primary_key, :foreign_key,
365
+ # or :unique.
349
366
  #
350
367
  # drop_constraint(:unique_name) # DROP CONSTRAINT unique_name
351
368
  # drop_constraint(:unique_name, :cascade=>true) # DROP CONSTRAINT unique_name CASCADE
@@ -353,7 +370,17 @@ module Sequel
353
370
  @operations << {:op => :drop_constraint, :name => name}.merge(opts)
354
371
  end
355
372
 
356
- # Remove an index from the DDL for the table.
373
+ # Remove an index from the DDL for the table. General options:
374
+ #
375
+ # :name :: The name of the index to drop. If not given, uses the same name
376
+ # that would be used by add_index with the same columns.
377
+ #
378
+ # PostgreSQL specific options:
379
+ #
380
+ # :cascade :: Cascade the index drop to dependent objects.
381
+ # :concurrently :: Drop the index using CONCURRENTLY, which doesn't block
382
+ # operations on the table. Supported in PostgreSQL 9.2+.
383
+ # :if_exists :: Only drop the index if it already exists.
357
384
  #
358
385
  # drop_index(:artist_id) # DROP INDEX table_artist_id_index
359
386
  # drop_index([:a, :b]) # DROP INDEX table_a_b_index
@@ -379,6 +406,10 @@ module Sequel
379
406
  # Modify a column's type in the DDL for the table.
380
407
  #
381
408
  # set_column_type(:artist_name, 'char(10)') # ALTER COLUMN artist_name TYPE char(10)
409
+ #
410
+ # PostgreSQL specific options:
411
+ #
412
+ # :using :: Add a USING clause that specifies how to convert existing values to new values.
382
413
  def set_column_type(name, type, opts={})
383
414
  @operations << {:op => :set_column_type, :name => name, :type => type}.merge(opts)
384
415
  end
@@ -131,13 +131,18 @@ module Sequel
131
131
  # index :title
132
132
  # end
133
133
  #
134
- # Options:
134
+ # General options:
135
135
  # :as :: Create the table using the value, which should be either a
136
136
  # dataset or a literal SQL string. If this option is used,
137
137
  # a block should not be given to the method.
138
138
  # :ignore_index_errors :: Ignore any errors when creating indexes.
139
139
  # :temp :: Create the table as a temporary table.
140
140
  #
141
+ # MySQL specific options:
142
+ # :charset :: The character set to use for the table.
143
+ # :collate :: The collation to use for the table.
144
+ # :engine :: The table engine to use for the table.
145
+ #
141
146
  # See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
142
147
  def create_table(name, options={}, &block)
143
148
  remove_cached_schema(name)
@@ -204,12 +204,12 @@ module Sequel
204
204
  #
205
205
  # ds.get{sum(id)} # SELECT sum(id) FROM table LIMIT 1
206
206
  # # => 6
207
- def get(column=nil, &block)
208
- if column
209
- raise(Error, ARG_BLOCK_ERROR_MSG) if block
210
- select(column).single_value
211
- else
207
+ def get(column=(no_arg=true; nil), &block)
208
+ if block
209
+ raise(Error, ARG_BLOCK_ERROR_MSG) unless no_arg
212
210
  select(&block).single_value
211
+ else
212
+ select(column).single_value
213
213
  end
214
214
  end
215
215
 
@@ -80,7 +80,7 @@ module Sequel
80
80
  # the type of the statement and the prepared_modify_values.
81
81
  def prepared_sql
82
82
  case @prepared_type
83
- when :select, :all
83
+ when :select, :all, :each
84
84
  # Most common scenario, so listed first.
85
85
  select_sql
86
86
  when :first
@@ -131,6 +131,8 @@ module Sequel
131
131
  when :select, :all
132
132
  # Most common scenario, so listed first
133
133
  all(&block)
134
+ when :each
135
+ each(&block)
134
136
  when :insert_select
135
137
  with_sql(prepared_sql).first
136
138
  when :first
@@ -164,7 +164,7 @@ module Sequel
164
164
  # DB[:items].filter('price < ?', 100)
165
165
  # # SELECT * FROM items WHERE price < 100
166
166
  #
167
- # DB[:items].filter([[:id, (1,2,3)], [:id, 0..10]])
167
+ # DB[:items].filter([[:id, [1,2,3]], [:id, 0..10]])
168
168
  # # SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10)))
169
169
  #
170
170
  # DB[:items].filter('price < 100')
@@ -340,11 +340,28 @@ module Sequel
340
340
  class Error < Sequel::Error
341
341
  end
342
342
 
343
+ # Exception class raised when Migrator.check_current signals that it is
344
+ # not current.
345
+ class NotCurrentError < Error
346
+ end
347
+
343
348
  # Wrapper for +run+, maintaining backwards API compatibility
344
349
  def self.apply(db, directory, target = nil, current = nil)
345
350
  run(db, directory, :target => target, :current => current)
346
351
  end
347
352
 
353
+ # Raise a NotCurrentError unless the migrator is current, takes the same
354
+ # arguments as #run.
355
+ def self.check_current(*args)
356
+ raise(NotCurrentError, 'migrator is not current') unless is_current?(*args)
357
+ end
358
+
359
+ # Return whether the migrator is current (i.e. it does not need to make
360
+ # any changes). Takes the same arguments as #run.
361
+ def self.is_current?(db, directory, opts={})
362
+ migrator_class(directory).new(db, directory, opts).is_current?
363
+ end
364
+
348
365
  # Migrates the supplied database using the migration files in the the specified directory. Options:
349
366
  # * :column :: The column in the :table argument storing the migration version (default: :version).
350
367
  # * :current :: The current version of the database. If not given, it is retrieved from the database
@@ -484,6 +501,11 @@ module Sequel
484
501
  @migrations = get_migrations
485
502
  end
486
503
 
504
+ # The integer migrator is current if the current version is the same as the target version.
505
+ def is_current?
506
+ current_migration_version == target
507
+ end
508
+
487
509
  # Apply all migrations on the database
488
510
  def run
489
511
  migrations.zip(version_numbers).each do |m, v|
@@ -601,6 +623,12 @@ module Sequel
601
623
  @migration_tuples = get_migration_tuples
602
624
  end
603
625
 
626
+ # The timestamp migrator is current if there are no migrations to apply
627
+ # in either direction.
628
+ def is_current?
629
+ migration_tuples.empty?
630
+ end
631
+
604
632
  # Apply all migration tuples on the database
605
633
  def run
606
634
  migration_tuples.each do |m, f, direction|
@@ -98,15 +98,6 @@ module Sequel
98
98
  end
99
99
  super
100
100
  end
101
-
102
- # If the sql string has an embedded parameter array,
103
- # extract the arguments from that.
104
- def execute_insert(sql, opts={})
105
- if sql.is_a?(StringWithArray) && (args = sql.args)
106
- opts = opts.merge(:arguments=>args)
107
- end
108
- super
109
- end
110
101
  end
111
102
 
112
103
  module DatasetMethods
@@ -0,0 +1,89 @@
1
+ # The pg_inet extension adds support for Sequel to handle
2
+ # PostgreSQL's inet and cidr types using ruby's IPAddr class.
3
+ #
4
+ # This extension integrates with Sequel's native postgres adapter, so
5
+ # that when inet/cidr fields are retrieved, they are returned as
6
+ # IPAddr instances
7
+ #
8
+ # After loading the extension, you should extend your dataset
9
+ # with a module so that it correctly handles the inet/cidr type:
10
+ #
11
+ # DB.extend Sequel::Postgres::InetDatabaseMethods
12
+ #
13
+ # If you are not using the native postgres adapter, you probably
14
+ # also want to use the typecast_on_load plugin in the model, and
15
+ # set it to typecast the inet/cidr column(s) on load.
16
+ #
17
+ # This extension does not add special support for the macaddr
18
+ # type. Ruby doesn't have a stdlib class that represents mac
19
+ # addresses, so these will still be returned as strings.
20
+
21
+ require 'ipaddr'
22
+
23
+ module Sequel
24
+ module Postgres
25
+ # Methods enabling Database object integration with the inet/cidr types.
26
+ module InetDatabaseMethods
27
+
28
+ # Reset the conversion procs when extending the Database object, so
29
+ # it will pick up the inet/cidr convertor. Also, extend the datasets
30
+ # with support for literalizing the IPAddr types.
31
+ def self.extended(db)
32
+ db.reset_conversion_procs if db.respond_to?(:reset_conversion_procs)
33
+ db.extend_datasets(InetDatasetMethods)
34
+ end
35
+
36
+ # Convert an IPAddr arg to a string. Probably not necessary, but done
37
+ # for safety.
38
+ def bound_variable_arg(arg, conn)
39
+ case arg
40
+ when IPAddr
41
+ "#{arg.to_s}/#{arg.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ # Make the column type detection recognize the inet and cidr types.
48
+ def schema_column_type(db_type)
49
+ case db_type
50
+ when 'inet', 'cidr'
51
+ :ipaddr
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ # Typecast the given value to an IPAddr object.
60
+ def typecast_value_ipaddr(value)
61
+ case value
62
+ when IPAddr
63
+ value
64
+ when String
65
+ IPAddr.new(value)
66
+ else
67
+ raise Sequel::InvalidValue, "invalid value for inet/cidr: #{value.inspect}"
68
+ end
69
+ end
70
+ end
71
+
72
+ module InetDatasetMethods
73
+ private
74
+
75
+ # Convert IPAddr value to a string and append a literal version
76
+ # of the string to the sql.
77
+ def literal_other_append(sql, value)
78
+ if value.is_a?(IPAddr)
79
+ literal_string_append(sql, "#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}")
80
+ else
81
+ super
82
+ end
83
+ end
84
+ end
85
+
86
+ PG_TYPES = {} unless defined?(PG_TYPES)
87
+ PG_TYPES[869] = PG_TYPES[650] = IPAddr.method(:new)
88
+ end
89
+ end