Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure querying into plugin #216

Merged
merged 3 commits into from
May 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions lib/mobility/active_record.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# frozen_string_literal: true
require "mobility/arel"

module Mobility
=begin

Expand All @@ -10,24 +12,12 @@ module ActiveRecord

def self.included(model_class)
model_class.class_eval do
extend QueryMethod.new(Mobility.query_method)
unless const_defined?(:UniquenessValidator)
const_set(:UniquenessValidator,
Class.new(::Mobility::ActiveRecord::UniquenessValidator))
end
delegate :translated_attribute_names, to: :class
end
end

class QueryMethod < Module
def initialize(query_method)
module_eval <<-EOM, __FILE__, __LINE__ + 1
def #{query_method}
all
end
EOM
end
end
private_constant :QueryMethod
end
end
1 change: 1 addition & 0 deletions lib/mobility/arel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require "mobility/arel/nodes"
16 changes: 16 additions & 0 deletions lib/mobility/arel/nodes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen-string-literal: true
module Mobility
module Arel
module Nodes
class Unary < ::Arel::Nodes::Unary; end
class Binary < ::Arel::Nodes::Binary; end
class Grouping < Unary; end
class Equality < Binary; end

::Arel::Visitors::ToSql.class_eval do
alias :visit_Mobility_Arel_Nodes_Equality :visit_Arel_Nodes_Equality
alias :visit_Mobility_Arel_Nodes_Grouping :visit_Arel_Nodes_Grouping
end
end
end
end
99 changes: 99 additions & 0 deletions lib/mobility/arel/nodes/pg_ops.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen-string-literal: true
require "mobility/arel"

module Mobility
module Arel
module Nodes
%w[
JsonDashArrow
JsonDashDoubleArrow
JsonbDashArrow
JsonbQuestion
HstoreDashArrow
HstoreQuestion
].each do |name|
const_set name, (Class.new(Binary) do
include ::Arel::Expressions
include ::Arel::Predications
include ::Arel::OrderPredications
include ::Arel::AliasPredication

def eq(other)
Equality.new self, quoted_node(other)
end
end)
end

JsonbDashArrow.class_eval do
def quoted_node(other)
other && super(other.to_json)
end
end

# Needed for AR 4.2, can be removed when support is deprecated
HstoreDashArrow.class_eval do
def quoted_node(other)
other && super
end
end

class Jsonb < JsonbDashArrow; end
class Hstore < HstoreDashArrow; end
class Json < JsonDashDoubleArrow; end
end

module Visitors
def visit_Mobility_Arel_Nodes_Equality o, a
left, right = o.left, o.right

if right.nil?
case left
when Nodes::Jsonb
nodes = []
while Nodes::Jsonb === left
left, right = left.left, left.right
nodes << Nodes::JsonbQuestion.new(left, right).not
end
return visit(nodes.inject(&:or), a)
when Nodes::Hstore
return visit(Nodes::HstoreQuestion.new(left.left, left.right).not, a)
end
end

super o, a
end

def visit_Mobility_Arel_Nodes_JsonDashArrow o, a
json_infix o, a, '->'
end

def visit_Mobility_Arel_Nodes_JsonDashDoubleArrow o, a
json_infix o, a, '->>'
end

def visit_Mobility_Arel_Nodes_JsonbDashArrow o, a
json_infix o, a, '->'
end

def visit_Mobility_Arel_Nodes_JsonbQuestion o, a
json_infix o, a, '?'
end

def visit_Mobility_Arel_Nodes_HstoreDashArrow o, a
json_infix o, a, '->'
end

def visit_Mobility_Arel_Nodes_HstoreQuestion o, a
json_infix o, a, '?'
end

private

def json_infix(o, a, opr)
visit(Nodes::Grouping.new(::Arel::Nodes::InfixOperation.new(opr, o.left, o.right)), a)
end
end

::Arel::Visitors::PostgreSQL.include Visitors
end
end
34 changes: 23 additions & 11 deletions lib/mobility/backends/active_record.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
module Mobility
module Backends
module ActiveRecord
def setup_query_methods(query_methods)
setup do |attributes, options|
extend(Module.new do
define_method ::Mobility.query_method do
super().extending(query_methods.new(attributes, options))
end
end)
end
end

def self.included(backend_class)
backend_class.include(Backend)
backend_class.extend(self)
backend_class.extend(ClassMethods)
end

module ClassMethods
# @param [String] _attr Attribute name
# @param [Symbol] _locale Locale
def build_node(_attr, _locale)
raise NotImplementedError
end

# @param [ActiveRecord::Relation] relation Relation to scope
# @param [Symbol] locale Locale
# @option [Boolean] invert
# @return [ActiveRecord::Relation] Relation with scope added
def add_translations(relation, _opts, _locale, invert: false)
relation
end

private

def build_quoted(value)
::Arel::Nodes.build_quoted(value)
end
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/mobility/backends/active_record/column.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ class ActiveRecord::Column
include ActiveRecord
include Column

require 'mobility/backends/active_record/column/query_methods'

# @!group Backend Accessors
# @!macro backend_reader
def read(locale, _ = {})
Expand All @@ -53,7 +51,9 @@ def each_locale
available_locales.each { |l| yield(l) if present?(l) }
end

setup_query_methods(QueryMethods)
def self.build_node(attr, locale)
model_class.arel_table[Column.column_name_for(attr, locale)]
end

private

Expand Down
42 changes: 0 additions & 42 deletions lib/mobility/backends/active_record/column/query_methods.rb

This file was deleted.

33 changes: 20 additions & 13 deletions lib/mobility/backends/active_record/container.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require "mobility/backends/active_record"
require "mobility/arel/nodes/pg_ops"

module Mobility
module Backends
Expand All @@ -11,14 +12,15 @@ module Backends
class ActiveRecord::Container
include ActiveRecord

require 'mobility/backends/active_record/container/json_query_methods'
require 'mobility/backends/active_record/container/jsonb_query_methods'

# @!method column_name
# Returns name of json or jsonb column used to store translations
# @return [Symbol] (:translations) Name of translations column
option_reader :column_name

# @!method column_type
# @return [Symbol] Either :json or :jsonb
option_reader :column_type

# @!group Backend Accessors
#
# @note Translation may be a string, integer, boolean, hash or array
Expand Down Expand Up @@ -55,16 +57,28 @@ def self.configure(options)
end
# @!endgroup

# @param [String] attr Attribute name
# @param [Symbol] locale Locale
def self.build_node(attr, locale)
column = model_class.arel_table[column_name]
quoted_locale = build_quoted(locale)
quoted_attr = build_quoted(attr)
case column_type
when :json
Arel::Nodes::Json.new(Arel::Nodes::JsonDashArrow.new(column, quoted_locale), quoted_attr)
when :jsonb
Arel::Nodes::Jsonb.new(Arel::Nodes::Jsonb.new(column, quoted_locale), quoted_attr)
end
end

# @!macro backend_iterator
def each_locale
model[column_name].each do |l, v|
yield l.to_sym if v.present?
end
end

backend_class = self

setup do |attributes, options|
setup do |_attributes, options|
store options[:column_name], coder: Coder

# Fix for duping depth-2 jsonb column in AR < 5.0
Expand All @@ -83,13 +97,6 @@ def initialize_dup(source)
include const_set(module_name, dupable)
end
end

query_methods = backend_class.const_get("#{options[:column_type].capitalize}QueryMethods")
extend(Module.new do
define_method ::Mobility.query_method do
super().extending(query_methods.new(attributes, options))
end
end)
end

private
Expand Down

This file was deleted.

This file was deleted.

Loading