rails-pg-procs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|