rails-pg-procs 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README +15 -0
- data/README.rdoc +18 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/docs/classes/ActiveRecord.html +117 -0
- data/docs/classes/ActiveRecord/ConnectionAdapters.html +113 -0
- data/docs/classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html +589 -0
- data/docs/classes/ActiveRecord/ConnectionAdapters/ProcedureDefinition.html +110 -0
- data/docs/classes/ActiveRecord/ConnectionAdapters/TriggerDefinition.html +519 -0
- data/docs/classes/ActiveRecord/ConnectionAdapters/TypeDefinition.html +110 -0
- data/docs/classes/ActiveRecord/SchemaDumper.html +371 -0
- data/docs/classes/Inflector.html +164 -0
- data/docs/classes/SchemaProcs.html +211 -0
- data/docs/classes/SqlFormat.html +139 -0
- data/docs/classes/String.html +117 -0
- data/docs/classes/Symbol.html +117 -0
- data/docs/created.rid +1 -0
- data/docs/fr_class_index.html +36 -0
- data/docs/fr_method_index.html +63 -0
- data/docs/index.html +22 -0
- data/docs/rdoc-style.css +208 -0
- data/init.rb +3 -0
- data/install.rb +1 -0
- data/lib/connection_adapters/aggregagtes_definition.rb +1 -0
- data/lib/connection_adapters/connection_adapters.rb +8 -0
- data/lib/connection_adapters/index_definition.rb +24 -0
- data/lib/connection_adapters/postgresql_adapter.rb +256 -0
- data/lib/connection_adapters/procedure_definition.rb +6 -0
- data/lib/connection_adapters/schema_definition.rb +24 -0
- data/lib/connection_adapters/schema_statements.rb +11 -0
- data/lib/connection_adapters/trigger_definition.rb +114 -0
- data/lib/connection_adapters/type_definition.rb +17 -0
- data/lib/connection_adapters/view_definition.rb +34 -0
- data/lib/inflector.rb +10 -0
- data/lib/rails_pg_procs.rb +8 -0
- data/lib/schema_dumper.rb +96 -0
- data/lib/schema_procs.rb +19 -0
- data/lib/sql_format.rb +17 -0
- data/tasks/rails_pg_procs.rake +4 -0
- data/test/connection.rb +16 -0
- data/test/procedure_test.rb +78 -0
- data/test/rails_pg_procs_test.rb +246 -0
- data/test/test_helper.rb +41 -0
- data/test/trigger_test.rb +87 -0
- data/test/type_test.rb +46 -0
- data/test/view_test.rb +36 -0
- data/uninstall.rb +1 -0
- metadata +112 -0
data/init.rb
ADDED
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1 @@
|
|
1
|
+
# TODO -- Add Aggregates ability
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'connection_adapters/schema_statements'
|
2
|
+
require 'connection_adapters/schema_definition'
|
3
|
+
require 'connection_adapters/aggregagtes_definition'
|
4
|
+
require 'connection_adapters/postgresql_adapter'
|
5
|
+
require 'connection_adapters/procedure_definition'
|
6
|
+
require 'connection_adapters/trigger_definition'
|
7
|
+
require 'connection_adapters/type_definition'
|
8
|
+
require 'connection_adapters/view_definition'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class IndexDefinition < Struct.new(:table, :name, :unique, :columns)
|
4
|
+
include SchemaProcs
|
5
|
+
|
6
|
+
def to_rdl add=true, options={}
|
7
|
+
return " add_index #{table.inspect}, #{columns.inspect}, :name => #{name.inspect}#{', :unique => true' if unique || unique == 't'}" if add
|
8
|
+
# " remove_index #{table.inspect}, #{columns.inspect}, :name => #{name.inspect}#{', :unique => true' if unique || unique == 't'}" if add
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_sql(action="create", options={})
|
12
|
+
case action
|
13
|
+
when "create", :create
|
14
|
+
"CREATE INDEX #{name.to_sql_name}"
|
15
|
+
# TODO - [ schema_element ]
|
16
|
+
when "drop", :drop
|
17
|
+
"DROP INDEX #{quote_column_name(index_name(table, options))} ON #{table}"
|
18
|
+
# "DROP SCHEMA #{name.to_sql_name} #{cascade_or_restrict(options[:cascade])}"
|
19
|
+
# TODO - [ IF EXISTS ]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
# TODO -- Add Aggregates ability
|
4
|
+
class PostgreSQLAdapter < AbstractAdapter
|
5
|
+
include SchemaProcs
|
6
|
+
|
7
|
+
@@ignore_namespaces = %w(pg_toast pg_temp_1 pg_catalog public information_schema)
|
8
|
+
|
9
|
+
def drop_database name
|
10
|
+
execute "DROP DATABASE#{' IF EXISTS' if postgresql_version >= 80200} #{name}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def schemas
|
14
|
+
query(<<-end_sql).collect {|row| SchemaDefinition.new(*row) }
|
15
|
+
SELECT N.nspname, S.usename
|
16
|
+
FROM pg_namespace N
|
17
|
+
JOIN pg_shadow S ON (N.nspowner = S.usesysid)
|
18
|
+
WHERE N.nspname NOT IN (#{@@ignore_namespaces.collect {|nsp| nsp.to_sql_value }.join(',')})
|
19
|
+
end_sql
|
20
|
+
end
|
21
|
+
|
22
|
+
def procedures(lang=nil)
|
23
|
+
query <<-end_sql
|
24
|
+
SELECT P.oid, proname, pronamespace, proowner, lanname, proisagg, prosecdef, proisstrict, proretset, provolatile, pronargs, prorettype, proargtypes, proargnames, prosrc, probin, proacl
|
25
|
+
FROM pg_proc P
|
26
|
+
JOIN pg_language L ON (P.prolang = L.oid)
|
27
|
+
JOIN pg_namespace N ON (P.pronamespace = N.oid)
|
28
|
+
WHERE N.nspname = 'public'
|
29
|
+
AND (proisagg = 'f')
|
30
|
+
#{'AND (lanname ' + lang + ')'unless lang.nil?}
|
31
|
+
end_sql
|
32
|
+
end
|
33
|
+
|
34
|
+
def triggers(table_name)
|
35
|
+
query(<<-end_sql).collect {|row| TriggerDefinition.new(*row) }
|
36
|
+
SELECT T.oid, C.relname, T.tgname, T.tgtype, P.proname
|
37
|
+
FROM pg_trigger T
|
38
|
+
JOIN pg_class C ON (T.tgrelid = C.OID AND C.relname = '#{table_name}' AND T.tgisconstraint = 'f')
|
39
|
+
JOIN pg_proc P ON (T.tgfoid = P.OID)
|
40
|
+
end_sql
|
41
|
+
end
|
42
|
+
|
43
|
+
def types
|
44
|
+
result = query(<<-end_sql)
|
45
|
+
SELECT T.oid, T.typname, A.attname, format_type(A.atttypid, A.atttypmod) AS type
|
46
|
+
FROM pg_type T
|
47
|
+
JOIN pg_class C ON (T.typrelid = C.oid)
|
48
|
+
JOIN pg_attribute A ON (A.attrelid = C.oid AND C.relkind = 'c')
|
49
|
+
end_sql
|
50
|
+
|
51
|
+
type_id = nil
|
52
|
+
types = []
|
53
|
+
result.each { |row|
|
54
|
+
if type_id != row[0]
|
55
|
+
types << TypeDefinition.new(row[0], row[1], [])
|
56
|
+
type_id = row[0]
|
57
|
+
end
|
58
|
+
|
59
|
+
types.last.columns << [row[2], row[3]]
|
60
|
+
}
|
61
|
+
|
62
|
+
types
|
63
|
+
end
|
64
|
+
|
65
|
+
# def tables(name = nil)
|
66
|
+
# schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
|
67
|
+
# query(<<-SQL, name).map { |row| row[0] << '.' << row[1] }
|
68
|
+
# SELECT N.nspname, C.relname
|
69
|
+
# FROM pg_class C
|
70
|
+
# JOIN pg_namespace N ON (C.relnamespace = N.oid)
|
71
|
+
# WHERE N.nspname IN (#{schemas})
|
72
|
+
# AND C.relkind = 'r'
|
73
|
+
# SQL
|
74
|
+
# end
|
75
|
+
|
76
|
+
# TODO Implement this
|
77
|
+
def views #:nodoc:
|
78
|
+
end
|
79
|
+
|
80
|
+
def columns(table_name, name = nil)
|
81
|
+
# Limit, precision, and scale are all handled by the superclass.
|
82
|
+
column_definitions(table_name).collect do |name, type, default, notnull|
|
83
|
+
PostgreSQLColumn.new(name, default, type, notnull == 'f')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_type(name, *columns)
|
88
|
+
if type = types.find {|typ| typ.name == name.to_s }
|
89
|
+
drop_type(type.name, true)
|
90
|
+
end
|
91
|
+
execute get_type_query(name, *columns)
|
92
|
+
end
|
93
|
+
|
94
|
+
def drop_type(name, cascade=false)
|
95
|
+
# puts "drop_type(#{name.to_sql_name})"
|
96
|
+
execute "DROP TYPE #{name.to_sql_name} #{cascade_or_restrict(cascade)}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_view(name, columns=[], options={}, &block)
|
100
|
+
view = ViewDefinition.new(0, name, columns) { yield }
|
101
|
+
execute view.to_sql(:create, options)
|
102
|
+
end
|
103
|
+
|
104
|
+
def drop_view(name, options={})
|
105
|
+
view = ViewDefinition.new(0, name)
|
106
|
+
execute view.to_sql(:drop, options)
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_schema(name, owner='postgres', options={})
|
110
|
+
if schema = schemas.find {|schema| schema.name.to_s == name.to_s }
|
111
|
+
drop_schema(schema.name, :cascade => true)
|
112
|
+
end
|
113
|
+
execute (schema = SchemaDefinition.new(name, owner)).to_sql(:create, options)
|
114
|
+
self.schema_search_path = (self.schema_search_path.split(",") | [schema.name]).join(',')
|
115
|
+
# self.schema_search_path = ( [schema.name] | self.schema_search_path.split(",") ).join(',')
|
116
|
+
end
|
117
|
+
|
118
|
+
def drop_schema(name, options={})
|
119
|
+
search_path = self.schema_search_path.split(",")
|
120
|
+
self.schema_search_path = search_path.join(',') if search_path.delete(name.to_s)
|
121
|
+
if schema = schemas.find {|schema| schema.name.to_s == name.to_s }
|
122
|
+
execute SchemaDefinition.new(name).to_sql(:drop, options)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Add a trigger to a table
|
127
|
+
def add_trigger(table, events, options={})
|
128
|
+
events += [:row] if options.delete(:row)
|
129
|
+
events += [:before] if options.delete(:before)
|
130
|
+
trigger = TriggerDefinition.new(0, table, options[:name], events, options[:function])
|
131
|
+
execute trigger.to_sql_create
|
132
|
+
end
|
133
|
+
|
134
|
+
# DROP TRIGGER name ON table [ CASCADE | RESTRICT ]
|
135
|
+
def remove_trigger(table, name, options={})
|
136
|
+
options[:name] = name
|
137
|
+
execute "DROP TRIGGER #{trigger_name(table, [], options).to_sql_name} ON #{table} #{cascade_or_restrict(options[:deep])};"
|
138
|
+
end
|
139
|
+
|
140
|
+
# Create a stored procedure
|
141
|
+
def create_proc(name, columns=[], options={}, &block)
|
142
|
+
if select_value("SELECT count(oid) FROM pg_language WHERE lanname = 'plpgsql' ","count").to_i == 0
|
143
|
+
execute("CREATE FUNCTION plpgsql_call_handler() RETURNS language_handler AS '$libdir/plpgsql', 'plpgsql_call_handler' LANGUAGE c")
|
144
|
+
execute("CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql HANDLER plpgsql_call_handler")
|
145
|
+
end
|
146
|
+
|
147
|
+
if options[:force]
|
148
|
+
drop_proc(name, columns) rescue nil
|
149
|
+
end
|
150
|
+
|
151
|
+
if block_given?
|
152
|
+
execute get_proc_query(name, columns, options) { yield }
|
153
|
+
elsif options[:resource]
|
154
|
+
execute get_proc_query(name, columns, options)
|
155
|
+
else
|
156
|
+
raise StatementInvalid.new("Missing function source")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# DROP FUNCTION name ( [ type [, ...] ] ) [ CASCADE | RESTRICT ]
|
161
|
+
# default RESTRICT
|
162
|
+
def drop_proc(name, columns=[], cascade=false)
|
163
|
+
execute "DROP FUNCTION #{name.to_sql_name}(#{columns.collect {|column| column}.join(", ")}) #{cascade_or_restrict(cascade)};"
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
# def column_definitions(table_name) #:nodoc:
|
168
|
+
# schema, table_name = table_name.split '.'
|
169
|
+
# unless table_name
|
170
|
+
# table_name = schema
|
171
|
+
# schema = 'public'
|
172
|
+
# end
|
173
|
+
# query <<-end_sql
|
174
|
+
# SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
|
175
|
+
# FROM pg_attribute a LEFT JOIN pg_attrdef d
|
176
|
+
# ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
177
|
+
# JOIN pg_class c ON (a.attrelid = c.oid)
|
178
|
+
# JOIN pg_namespace n ON (c.relnamespace = n.oid)
|
179
|
+
# WHERE c.relname = '#{table_name}'
|
180
|
+
# AND n.nspname = '#{schema}'
|
181
|
+
# AND a.attnum > 0 AND NOT a.attisdropped
|
182
|
+
# ORDER BY a.attnum
|
183
|
+
# end_sql
|
184
|
+
# end
|
185
|
+
|
186
|
+
def trigger_name(table, events=[], options={})
|
187
|
+
options[:name] || Inflector.triggerize(table, events, options[:before])
|
188
|
+
end
|
189
|
+
|
190
|
+
# Helper function that builds the sql query used to create a stored procedure.
|
191
|
+
# Mostly this is here so we can unit test the generated sql.
|
192
|
+
# Either an option[:resource] or block must be defined for this method.
|
193
|
+
# Otherwise an ActiveRecord::StatementInvalid exception is raised.
|
194
|
+
# Defaults are:
|
195
|
+
# RETURNS (no default -- which is cheap since that means you have to call this method w/ the options Hash) TODO: fix this
|
196
|
+
# LANGUAGE = plpgsql (The plugin will add this if you don't have it added already)
|
197
|
+
# behavior = VOLATILE (Don't specify IMMUTABLE or STABLE and this will be added for you)
|
198
|
+
# strict = CALLED ON NULL INPUT (Otherwise STRICT, According to the 8.0 manual STRICT and RETURNS NULL ON NULL INPUT (RNONI)
|
199
|
+
# behave the same so I didn't make a case for RNONI)
|
200
|
+
# user = INVOKER
|
201
|
+
def delim(name, options)
|
202
|
+
name = name.split('.').last if name.is_a?(String) && name.include?('.')
|
203
|
+
options[:delim] || "$#{ActiveSupport::Inflector.underscore(name)}_body$"
|
204
|
+
end
|
205
|
+
|
206
|
+
# From PostgreSQL
|
207
|
+
## CREATE [ OR REPLACE ] FUNCTION
|
208
|
+
## name ( [ [ argmode ] [ argname ] argtype [, ...] ] )
|
209
|
+
## [ RETURNS rettype ]
|
210
|
+
## { LANGUAGE langname
|
211
|
+
## | IMMUTABLE | STABLE | VOLATILE
|
212
|
+
## | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
213
|
+
# | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
214
|
+
## | AS 'definition'
|
215
|
+
# | AS 'obj_file', 'link_symbol'
|
216
|
+
# } ...
|
217
|
+
# [ WITH ( isStrict &| isCacheable ) ]
|
218
|
+
# TODO Implement [ [ argmode ] [ argname ] argtype ]
|
219
|
+
def get_proc_query(name, columns=[], options={}, &block)
|
220
|
+
returns = "RETURNS#{' SETOF' if options[:set]} #{options[:return] || 'VOID'}"
|
221
|
+
lang = options[:lang] || "plpgsql"
|
222
|
+
|
223
|
+
if block_given?
|
224
|
+
body = "#{delim(name, options)}\n#{yield}\n#{delim(name, options)}"
|
225
|
+
elsif options[:resource]
|
226
|
+
options[:resource] += [name] if options[:resource].size == 1
|
227
|
+
body = options[:resource].collect {|res| "'#{res}'" }.join(", ")
|
228
|
+
else
|
229
|
+
raise StatementInvalid.new("Missing function source")
|
230
|
+
end
|
231
|
+
|
232
|
+
result = "
|
233
|
+
CREATE OR REPLACE FUNCTION #{name.to_sql_name}(#{columns.collect{|column| column}.join(", ")}) #{returns} AS
|
234
|
+
#{body}
|
235
|
+
LANGUAGE #{lang}
|
236
|
+
#{ behavior(options[:behavior] || 'v').upcase }
|
237
|
+
#{ strict_or_null(options[:strict]) }
|
238
|
+
EXTERNAL SECURITY #{ definer_or_invoker(options[:definer]) }
|
239
|
+
"
|
240
|
+
end
|
241
|
+
|
242
|
+
def get_type_query(name, *columns)
|
243
|
+
raise StatementInvalid.new if columns.empty?
|
244
|
+
"CREATE TYPE #{quote_column_name(name)} AS (
|
245
|
+
#{columns.collect{|column,type|
|
246
|
+
if column.is_a?(Hash)
|
247
|
+
column.collect { |column, type| "#{quote_column_name(column)} #{type}" }
|
248
|
+
else
|
249
|
+
"#{quote_column_name(column)} #{type}"
|
250
|
+
end
|
251
|
+
}.join(",\n")}
|
252
|
+
)"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class SchemaDefinition < Struct.new(:name, :owner)
|
4
|
+
include SchemaProcs
|
5
|
+
|
6
|
+
def to_rdl
|
7
|
+
" create_schema #{name.to_sql_name}, #{owner.to_sql_name}"
|
8
|
+
end
|
9
|
+
|
10
|
+
# CREATE SCHEMA schemaname [ AUTHORIZATION username ] [ schema_element [ ... ] ]
|
11
|
+
# DROP SCHEMA [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
|
12
|
+
def to_sql(action="create", options={})
|
13
|
+
case action
|
14
|
+
when "create", :create
|
15
|
+
"CREATE SCHEMA #{name.to_sql_name} AUTHORIZATION #{owner.to_sql_name}"
|
16
|
+
# TODO - [ schema_element ]
|
17
|
+
when "drop", :drop
|
18
|
+
"DROP SCHEMA #{name.to_sql_name} #{cascade_or_restrict(options[:cascade])}"
|
19
|
+
# TODO - [ IF EXISTS ]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class TriggerDefinition
|
4
|
+
CLEAN = 0b0
|
5
|
+
ROW = 0b00001
|
6
|
+
BEFORE = 0b00010
|
7
|
+
INSERT = 0b00100
|
8
|
+
DELETE = 0b01000
|
9
|
+
UPDATE = 0b10000
|
10
|
+
|
11
|
+
attr_accessor :id, :table, :name, :procedure_name
|
12
|
+
attr_reader :binary_type
|
13
|
+
def initialize(id, table, name=nil, binary_type=[], procedure_name=nil)
|
14
|
+
@id = id
|
15
|
+
@table = table
|
16
|
+
self.binary_type = binary_type
|
17
|
+
self.name = (name || triggerized_name)
|
18
|
+
self.procedure_name = (procedure_name || name || triggerized_name)
|
19
|
+
end
|
20
|
+
|
21
|
+
# that's to_r(uby)d(efinition)l(anguage)
|
22
|
+
def to_rdl()
|
23
|
+
" add_trigger #{table.to_sql_name}" <<
|
24
|
+
", [" + events.join(", ") + "]" <<
|
25
|
+
( before? ? ", :before => true" : "") <<
|
26
|
+
( row? ? ", :row => true" : "") <<
|
27
|
+
(!triggerized? ? ", :name => #{ActiveSupport::Inflector.symbolize(name)}" : "") <<
|
28
|
+
(!triggerized?(procedure_name) ? ", :function => #{ActiveSupport::Inflector.symbolize(procedure_name)}" : "")
|
29
|
+
end
|
30
|
+
|
31
|
+
def binary_type=(*types)
|
32
|
+
case types[0]
|
33
|
+
when Fixnum, Array
|
34
|
+
@binary_type = bin_typ(types[0])
|
35
|
+
else
|
36
|
+
@binary_type = bin_typ(types)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# CREATE TRIGGER name { BEFORE | AFTER } { event [ OR ... ] }
|
41
|
+
# ON table [ FOR [ EACH ] { ROW | STATEMENT } ]
|
42
|
+
# EXECUTE PROCEDURE funcname ( arguments )
|
43
|
+
def to_sql_create()
|
44
|
+
result = "CREATE TRIGGER " <<
|
45
|
+
name.to_sql_name <<
|
46
|
+
(before? ? " BEFORE" : " AFTER") <<
|
47
|
+
" " <<
|
48
|
+
(
|
49
|
+
events.collect {|event|
|
50
|
+
event.to_s.upcase.gsub(/^:/, '') }.join(" OR ")
|
51
|
+
) <<
|
52
|
+
" ON " <<
|
53
|
+
table.to_sql_name <<
|
54
|
+
" FOR EACH " <<
|
55
|
+
(row? ? "ROW" : "STATEMENT") <<
|
56
|
+
" EXECUTE PROCEDURE " <<
|
57
|
+
procedure_name.to_sql_name <<
|
58
|
+
"();"
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
def triggerized?(nam=nil)
|
63
|
+
nam ||= self.name
|
64
|
+
triggerized_name == nam
|
65
|
+
end
|
66
|
+
|
67
|
+
def before?
|
68
|
+
calc(BEFORE)
|
69
|
+
end
|
70
|
+
|
71
|
+
def row?
|
72
|
+
calc(ROW)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def triggerized_name
|
78
|
+
ActiveSupport::Inflector.triggerize(table, events, calc(BEFORE))
|
79
|
+
end
|
80
|
+
|
81
|
+
def events
|
82
|
+
events = []
|
83
|
+
events.push(":insert") if calc(INSERT)
|
84
|
+
events.push(":update") if calc(UPDATE)
|
85
|
+
events.push(":delete") if calc(DELETE)
|
86
|
+
events
|
87
|
+
end
|
88
|
+
|
89
|
+
def calc(bin)
|
90
|
+
eval(sprintf("0b%0.8b", self.binary_type())) & bin > 0
|
91
|
+
end
|
92
|
+
|
93
|
+
def bin_typ(typs)
|
94
|
+
case typs
|
95
|
+
when Fixnum
|
96
|
+
return typs
|
97
|
+
when Symbol
|
98
|
+
return bin_typ(typs.to_s)
|
99
|
+
when String
|
100
|
+
return typs.to_i if typs =~ /^\d+$/
|
101
|
+
return self.class.const_get(typs.upcase.to_sym)
|
102
|
+
when Array
|
103
|
+
ctype = 0
|
104
|
+
typs.each {|typ|
|
105
|
+
ctype += bin_typ(typ)
|
106
|
+
}
|
107
|
+
end
|
108
|
+
ctype
|
109
|
+
end
|
110
|
+
|
111
|
+
# end private
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|