Class: ROM::SQL::Attribute

Inherits:
ROM::Schema::Attribute
  • Object
show all
Extended by:
Dry::Core::Cache
Defined in:
lib/rom/sql/attribute.rb,
lib/rom/sql/extensions/postgres/types.rb

Overview

Extended schema attributes tailored for SQL databases

Defined Under Namespace

Modules: TypeExtensions

Constant Summary collapse

OPERATORS =
i[>= <= > <].freeze
NONSTANDARD_EQUALITY_VALUES =
[true, false, nil].freeze
QualifyError =

Error raised when an attribute cannot be qualified

Class.new(StandardError)

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object (private)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Delegate to sql expression if it responds to a given method



344
345
346
347
348
349
350
351
352
353
354
# File 'lib/rom/sql/attribute.rb', line 344

def method_missing(meth, *args, &block)
  if OPERATORS.include?(meth)
    __cmp__(meth, args[0])
  elsif extensions.key?(meth)
    extensions[meth].(type, sql_expr, *args, &block)
  elsif sql_expr.respond_to?(meth)
    meta(sql_expr: sql_expr.__send__(meth, *args, &block))
  else
    super
  end
end

Class Method Details

.[](*args) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



69
70
71
# File 'lib/rom/sql/attribute.rb', line 69

def self.[](*args)
  fetch_or_store(args) { new(*args) }
end

Instance Method Details

#!Attribute

Negate the attribute’s sql expression

Examples:

users.where(!users[:id].is(1))


242
243
244
# File 'lib/rom/sql/attribute.rb', line 242

def !
  ~self
end

#+(value) ⇒ Object

An alias for ROM::SQL::Attribute<JSONB>#merge



# File 'lib/rom/sql/extensions/postgres/types.rb', line 80


#=~(other) ⇒ Object



216
217
218
# File 'lib/rom/sql/attribute.rb', line 216

def =~(other)
  meta(sql_expr: sql_expr =~ binary_operation_arg(other))
end

#aliased(name) ⇒ SQL::Attribute Also known as: as

Return a new attribute with an alias

Examples:

users[:id].aliased(:user_id)


83
84
85
# File 'lib/rom/sql/attribute.rb', line 83

def aliased(name)
  super.meta(name: meta.fetch(:name, name), sql_expr: sql_expr.as(name))
end

#any(value) ⇒ SQL::Attribute<Types::Bool>

Check whether the array includes a value Translates to the ANY operator



# File 'lib/rom/sql/extensions/postgres/types.rb', line 21


#canonicalObject

Return a new attribute in its canonical form



91
92
93
94
95
96
97
# File 'lib/rom/sql/attribute.rb', line 91

def canonical
  if aliased?
    meta(alias: nil, sql_expr: nil)
  else
    self
  end
end

#concat(other, sep = ' ') ⇒ SQL::Function

Create a CONCAT function from the attribute

Examples:

with default separator (‘ ’)

users[:id].concat(users[:name])

with custom separator

users[:id].concat(users[:name], '-')


301
302
303
# File 'lib/rom/sql/attribute.rb', line 301

def concat(other, sep = ' ')
  Function.new(type).concat(self, sep, other)
end

#contain(value) ⇒ SQL::Attribute<Types::Bool>

Check whether the JSON value includes a json value Translates to the @> operator

Examples:

people.where { fields.contain(gender: 'Female') }
people.where(people[:fields].contain([name: 'age']))
people.select { fields.contain(gender: 'Female').as(:is_female) }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 2


#contained_by(value) ⇒ SQL::Attribute<Types::Bool>

Check whether the JSON value is contained by other value Translates to the <@ operator

Examples:

people.where { custom_values.contained_by(age: 25, foo: 'bar') }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 31


#delete(*path) ⇒ SQL::Attribute<Types::PG::JSONB>

Deletes the specified value by key, index, or path Translates to - or #- depending on the number of arguments

Examples:

people.select { data.delete('age').as(:data_without_age) }
people.select { fields.delete(0).as(:fields_without_first) }
people.select { fields.delete(-1).as(:fields_without_last) }
people.select { data.delete('deeply', 'nested', 'value').as(:data) }
people.select { fields.delete('0', 'name').as(:data) }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 118


#foreign_keySQL::Attribute

Return a new attribute marked as a FK



166
167
168
# File 'lib/rom/sql/attribute.rb', line 166

def foreign_key
  meta(foreign_key: true)
end

#func(&block) ⇒ SQL::Function

Create a function DSL from the attribute

Examples:

users[:id].func { int::count(id).as(:count) }


284
285
286
# File 'lib/rom/sql/attribute.rb', line 284

def func(&block)
  ProjectionDSL.new(name => self).call(&block).first
end

#get(*path) ⇒ SQL::Attribute<Types::PG::JSON>, SQL::Attribute<Types::PG::JSONB>

Extract the JSON value using at the specified path Translates to -> or #> depending on the number of arguments

Examples:

people.select { data.get('age').as(:person_age) }
people.select { fields.get(0).as(:first_field) }
people.select { fields.get('0', 'value').as(:first_field_value) }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 12


#get_text(*path) ⇒ SQL::Attribute<Types::String>

Extract the JSON value as text using at the specified path Translates to ->> or #>> depending on the number of arguments

Examples:

people.select { data.get('age').as(:person_age) }
people.select { fields.get(0).as(:first_field) }
people.select { fields.get('0', 'value').as(:first_field_value) }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 45


#has_all_keys(*keys) ⇒ SQL::Attribute<Types::Bool>

Does the JSON value have all the specified top-level keys Translates to ?&

Examples:

people.where { data.has_all_keys('age', 'height') }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 86


#has_any_key(*keys) ⇒ SQL::Attribute<Types::Bool>

Does the JSON value have any of the specified top-level keys Translates to ?|

Examples:

people.where { data.has_any_key('age', 'height') }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 73


#has_key(key) ⇒ SQL::Attribute<Types::Bool>

Does the JSON value have the specified top-level key Translates to ?

Examples:

people.where { data.has_key('age') }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 60


#in(*args) ⇒ Object

Return a boolean expression with an inclusion test

If the single argument passed to the method is a Range object then the resulting expression will restrict the attribute value with range’s bounds. Upper bound condition will be inclusive/non-inclusive depending on the range type.

If more than one argument is passed to the method or the first argument is not Range then the result will be a simple IN check.

Examples:

users.where { id.in(1..100) | created_at(((Time.now - 86400)..Time.now)) }
users.where { id.in(1, 2, 3) }
users.where(users[:id].in(1, 2, 3))


264
265
266
267
268
269
270
271
272
273
274
# File 'lib/rom/sql/attribute.rb', line 264

def in(*args)
  if args.first.is_a?(Range)
    range = args.first
    lower_cond = __cmp__(:>=, range.begin)
    upper_cond = __cmp__(range.exclude_end? ? :< : :<=, range.end)

    Sequel::SQL::BooleanExpression.new(:AND, lower_cond, upper_cond)
  else
    __cmp__(:IN, args)
  end
end

#is(other) ⇒ Object

Return a boolean expression with an equality operator

Examples:

users.where { id.is(1) }

users.where(users[:id].is(1))


211
212
213
# File 'lib/rom/sql/attribute.rb', line 211

def is(other)
  self =~ other
end

#join(delimiter, null_repr) ⇒ SQL::Attribute<Types::String>

Convert the array to a string by joining values with a delimiter (empty stirng by default) and optional filler for NULL values Translates to an array_to_string call



# File 'lib/rom/sql/extensions/postgres/types.rb', line 67


#joinedSQL::Attribute

Return a new attribute marked as joined

Whenever you join two schemas, the right schema’s attribute will be marked as joined using this method



127
128
129
# File 'lib/rom/sql/attribute.rb', line 127

def joined
  meta(joined: true)
end

#joined?Boolean

Return if an attribute was used in a join

Examples:

schema = users.schema.join(tasks.schema)

schema[:id, :tasks].joined?
# => true


142
143
144
# File 'lib/rom/sql/attribute.rb', line 142

def joined?
  meta[:joined].equal?(true)
end

#lengthSQL::Attribute<Types::Int>

Return array size



# File 'lib/rom/sql/extensions/postgres/types.rb', line 41


#merge(value) ⇒ SQL::Attribute<Types::PG::JSONB>

Concatenate two JSON values Translates to ||

Examples:

people.select { data.merge(fetched_at: Time.now).as(:data) }
people.select { (fields + [name: 'height', value: 165]).as(:fields) }


# File 'lib/rom/sql/extensions/postgres/types.rb', line 99


#not(other) ⇒ Object

Return a boolean expression with a negated equality operator

Examples:

users.where { id.not(1) }

users.where(users[:id].not(1))


230
231
232
# File 'lib/rom/sql/attribute.rb', line 230

def not(other)
  !is(other)
end

#overlaps(other) ⇒ SQL::Attribute<Types::Bool>

Check whether the arrays have common values Translates to &&



# File 'lib/rom/sql/extensions/postgres/types.rb', line 48


#qualifiedSQL::Attribute

Return a new attribute marked as qualified

Examples:

users[:id].aliased(:user_id)


107
108
109
110
111
112
113
114
115
116
117
# File 'lib/rom/sql/attribute.rb', line 107

def qualified
  return self if qualified?

  case sql_expr
  when Sequel::SQL::AliasedExpression, Sequel::SQL::Identifier
    type = meta(qualified: true)
    type.meta(sql_expr: type.to_sql_name)
  else
    raise QualifyError, "can't qualify #{name.inspect} (#{sql_expr.inspect})"
  end
end

#qualified?Boolean

Return if an attribute type is qualified

Examples:

id = users[:id].qualify

id.qualified?
# => true


157
158
159
# File 'lib/rom/sql/attribute.rb', line 157

def qualified?
  meta[:qualified].equal?(true)
end

#remove_value(value) ⇒ SQL::Attribute<Types::PG::Array>

Remove elements by value



# File 'lib/rom/sql/extensions/postgres/types.rb', line 58


#sql_literal(ds) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Sequel calls this method to coerce an attribute into SQL string



310
311
312
# File 'lib/rom/sql/attribute.rb', line 310

def sql_literal(ds)
  ds.literal(sql_expr)
end

#to_sql_nameSequel::SQL::AliasedExpression, Sequel::SQL::Identifier

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Sequel column representation



319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/rom/sql/attribute.rb', line 319

def to_sql_name
  @_to_sql_name ||=
    if qualified? && aliased?
      Sequel.qualify(source.dataset, name).as(meta[:alias])
    elsif qualified?
      Sequel.qualify(source.dataset, name)
    elsif aliased?
      Sequel.as(name, meta[:alias])
    else
      Sequel[name]
    end
end

#to_symSymbol

Return symbol representation of an attribute

This uses convention from sequel where double underscore in the name is used for qualifying, and triple underscore means aliasing

Examples:

users[:id].qualified.to_sym
# => :users__id

users[:id].as(:user_id).to_sym
# => :id___user_id

users[:id].qualified.as(:user_id).to_sym
# => :users__id___user_id


188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rom/sql/attribute.rb', line 188

def to_sym
  @_to_sym ||=
    if qualified? && aliased?
      :"#{source.dataset}__#{name}___#{meta[:alias]}"
    elsif qualified?
      :"#{source.dataset}__#{name}"
    elsif aliased?
      :"#{name}___#{meta[:alias]}"
    else
      name
    end
end