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
|
# File 'lib/tag_columns.rb', line 14
def tag_columns(*column_names)
@tag_columns ||= {}
tag_columns_sanitize_list(column_names).each do |column_name|
@tag_columns[column_name] ||= false
end
@tag_columns.each do |column_name, initialized|
next if initialized
column_name = column_name.to_s
method_name = column_name.downcase
define_singleton_method :"unique_#{method_name}" do |conditions = "true"|
unnest = Arel::Nodes::NamedFunction.new("unnest", [arel_table[column_name]])
query = distinct.select(unnest)
.where(conditions)
.where.not(arel_table[column_name].eq(nil))
.where.not(arel_table[column_name].eq("{}"))
connection.execute(query.to_sql).values.flatten.sort
end
define_singleton_method :"#{method_name}_cloud" do |conditions = "true"|
unnest = Arel::Nodes::NamedFunction.new("unnest", [arel_table[column_name]])
query = unscoped.select(unnest.as("tag"))
.where(conditions)
.where.not(arel_table[column_name].eq(nil))
.where.not(arel_table[column_name].eq("{}"))
from(query).group("tag").order("tag").pluck(Arel.sql("tag, count(*) as count"))
end
scope :"with_#{method_name}", -> {
where.not(arel_table[column_name].eq(nil)).where.not(arel_table[column_name].eq("{}"))
}
scope :"without_#{method_name}", -> {
where(arel_table[column_name].eq(nil)).or(where(arel_table[column_name].eq("{}")))
}
scope :"with_any_#{method_name}", ->(*tags) {
column_cast = Arel::Nodes::NamedFunction.new("CAST", [arel_table[column_name].as("text[]")])
value = Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["ARRAY[?]", tag_columns_sanitize_list(tags)]))
value_cast = Arel::Nodes::NamedFunction.new("CAST", [value.as("text[]")])
overlap = Arel::Nodes::InfixOperation.new("&&", column_cast, value_cast)
where overlap
}
scope :"with_all_#{method_name}", ->(*tags) {
column_cast = Arel::Nodes::NamedFunction.new("CAST", [arel_table[column_name].as("text[]")])
value = Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["ARRAY[?]", tag_columns_sanitize_list(tags)]))
value_cast = Arel::Nodes::NamedFunction.new("CAST", [value.as("text[]")])
contains = Arel::Nodes::InfixOperation.new("@>", column_cast, value_cast)
where contains
}
scope :"without_any_#{method_name}", ->(*tags) {
column_cast = Arel::Nodes::NamedFunction.new("CAST", [arel_table[column_name].as("text[]")])
value = Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["ARRAY[?]", tag_columns_sanitize_list(tags)]))
value_cast = Arel::Nodes::NamedFunction.new("CAST", [value.as("text[]")])
overlap = Arel::Nodes::InfixOperation.new("&&", column_cast, value_cast)
where.not overlap
}
scope :"without_all_#{method_name}", ->(*tags) {
column_cast = Arel::Nodes::NamedFunction.new("CAST", [arel_table[column_name].as("text[]")])
value = Arel::Nodes::SqlLiteral.new(sanitize_sql_array(["ARRAY[?]", tag_columns_sanitize_list(tags)]))
value_cast = Arel::Nodes::NamedFunction.new("CAST", [value.as("text[]")])
contains = Arel::Nodes::InfixOperation.new("@>", column_cast, value_cast)
where.not contains
}
before_validation -> { self[column_name] = self.class.tag_columns_sanitize_list(self[column_name]) }
define_method :"has_any_#{method_name}?" do |*values|
values = self.class.tag_columns_sanitize_list(values)
existing = self.class.tag_columns_sanitize_list(self[column_name])
(values & existing).present?
end
define_method :"has_all_#{method_name}?" do |*values|
values = self.class.tag_columns_sanitize_list(values)
existing = self.class.tag_columns_sanitize_list(self[column_name])
(values & existing).size == values.size
end
alias_method :"has_#{method_name.singularize}?", :"has_all_#{method_name}?"
@tag_columns[column_name] = true
end
end
|