Class: ActiveRecord::Refined::BlockContext
- Inherits:
-
Object
- Object
- ActiveRecord::Refined::BlockContext
- Defined in:
- lib/active_record/refined.rb
Constant Summary collapse
- AGGREGATE_FUNCTIONS =
{ sum: :sum, avg: :average, min: :minimum, max: :maximum, }.freeze
- SCALAR_FUNCTIONS =
Scalar functions, defined as real methods so that a typo is a NoMethodError and a name Kernel also answers to (format, hash, test) cannot quietly mean something else.
The value lists the adapters that differ: a string is what the function is called there, nil says the adapter has no equivalent. An adapter that is not listed spells it like the method. The families are what the entries key on, so trilogy reads the mysql column.
Availability was checked by calling each one; the SQLite figures assume the math functions its build usually enables.
{ abs: {}, acos: {}, asin: {}, atan: {}, atan2: {}, ceil: {}, coalesce: {}, concat: {}, cos: {}, degrees: {}, exp: {}, floor: {}, length: {}, ln: {}, log: {}, log10: {}, lower: {}, ltrim: {}, mod: {}, nullif: {}, pi: {}, power: {}, radians: {}, replace: {}, round: {}, rtrim: {}, sign: {}, sin: {}, sqrt: {}, substr: {}, tan: {}, trim: {}, upper: {}, char_length: {sqlite: 'LENGTH'}, greatest: {sqlite: 'MAX'}, least: {sqlite: 'MIN'}, # PostgreSQL spells log2(x) as log(2, x), which no renaming carries. log2: {postgresql: nil}, # MySQL's TRUNCATE insists on the second argument, where the others # default it to zero; SQLite's trunc takes only the one. trunc: {mysql: 'TRUNCATE'}, now: {sqlite: nil}, date_trunc: {sqlite: nil, mysql: nil}, # Named for Kernel#rand, which it also takes back: a block calling # rand would otherwise get Ruby's and never reach the database. rand: {sqlite: 'RANDOM', postgresql: 'RANDOM'}, # Two different functions share this name: printf formatting here, and # on MySQL the one that puts separators in a number, which reads a # printf template as the number zero rather than complaining. The # name keeps the one meaning; fn(:format, ...) reaches MySQL's. format: {mysql: nil}, }.freeze
- ADAPTER_FAMILIES =
{ 'sqlite3' => :sqlite, 'postgresql' => :postgresql, 'postgis' => :postgresql, 'mysql2' => :mysql, 'trilogy' => :mysql, }.freeze
- DATETIME_VALUE_FUNCTIONS =
The datetime value functions, as the SQL grammar calls them. These the grammar has bare -- PostgreSQL and SQLite reject them written with parentheses -- and the one thing that does go into parentheses is an optional precision, current_timestamp(3), which current_date never takes and SQLite never accepts. The table reads like SCALAR_FUNCTIONS; current_timestamp is the portable spelling of what now means, reaching SQLite where now does not.
{ current_date: {}, current_time: {}, current_timestamp: {}, localtime: {sqlite: nil}, localtimestamp: {sqlite: nil}, }.freeze
Instance Method Summary collapse
-
#cast(expr, type) ⇒ Object
CAST(expr AS type).
- #count(column, distinct: false) ⇒ Object
- #current_date ⇒ Object
- #exists?(relation) ⇒ Boolean
-
#extract(field, expr) ⇒ Object
EXTRACT(field FROM expr).
-
#fn(name, *args) ⇒ Object
Escape hatch for functions without a method of their own.
-
#initialize(model) ⇒ BlockContext
constructor
The model is only consulted to learn which adapter the query is being built for, which is what decides how a scalar function is spelled.
-
#value(literal) ⇒ Object
A literal where an expression is expected, quoted like any other value:.
Constructor Details
#initialize(model) ⇒ BlockContext
The model is only consulted to learn which adapter the query is being built for, which is what decides how a scalar function is spelled.
42 43 44 |
# File 'lib/active_record/refined.rb', line 42 def initialize(model) @model = model end |
Instance Method Details
#cast(expr, type) ⇒ Object
CAST(expr AS type). The type is the adapter's own name for it, checked for shape by the node; whether it exists is the database's to say.
162 163 164 |
# File 'lib/active_record/refined.rb', line 162 def cast(expr, type) AST::Cast.new(expr, type) end |
#count(column, distinct: false) ⇒ Object
54 55 56 |
# File 'lib/active_record/refined.rb', line 54 def count(column, distinct: false) AST::Aggregate.new(column, :count, distinct: distinct) end |
#current_date ⇒ Object
125 126 127 128 |
# File 'lib/active_record/refined.rb', line 125 def current_date AST::DatetimeValueFunction.new( function_name(:current_date, DATETIME_VALUE_FUNCTIONS)) end |
#exists?(relation) ⇒ Boolean
175 176 177 |
# File 'lib/active_record/refined.rb', line 175 def exists?(relation) AST::Exists.new(relation) end |
#extract(field, expr) ⇒ Object
EXTRACT(field FROM expr). The field is a keyword, not a value, so it has to be a plain name; the node checks it. SQLite spells all of this as strftime formats, which no renaming carries, so it raises there -- after the node is built, so that a bad field is an ArgumentError on every adapter.
150 151 152 153 154 155 156 157 |
# File 'lib/active_record/refined.rb', line 150 def extract(field, expr) node = AST::Extract.new(field, expr) if adapter_family == :sqlite raise NotImplementedError, "extract has no equivalent on #{@model.connection_db_config.adapter}" end node end |
#fn(name, *args) ⇒ Object
Escape hatch for functions without a method of their own. The name is emitted as written, so a case-sensitive one can be spelled exactly, and for that reason it has to be a plain name, optionally qualified by a schema; anything else is refused rather than written into the SQL.
170 171 172 173 |
# File 'lib/active_record/refined.rb', line 170 def fn(name, *args) AST::Function.new( AST.check_name(name, AST::FUNCTION_NAME, "function name").to_s, args) end |
#value(literal) ⇒ Object
A literal where an expression is expected, quoted like any other value:
select { [:id, value(0).as(:depth)] }
Needed because the top of a select list is ActiveRecord's, and a bare
string there is SQL rather than a string. Numbers have a shorthand --
0.as(:depth) -- since nothing else could be meant by one.
186 187 188 |
# File 'lib/active_record/refined.rb', line 186 def value(literal) AST::Value.new(literal) end |