ruby-plsql 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +19 -0
- data/License.txt +1 -1
- data/README.rdoc +9 -4
- data/VERSION +1 -1
- data/lib/plsql/connection.rb +24 -9
- data/lib/plsql/helpers.rb +9 -0
- data/lib/plsql/jdbc_connection.rb +38 -9
- data/lib/plsql/oci8_patches.rb +25 -0
- data/lib/plsql/oci_connection.rb +46 -12
- data/lib/plsql/package.rb +25 -12
- data/lib/plsql/procedure.rb +23 -29
- data/lib/plsql/procedure_call.rb +53 -4
- data/lib/plsql/schema.rb +114 -45
- data/lib/plsql/sequence.rb +1 -1
- data/lib/plsql/sql_statements.rb +7 -2
- data/lib/plsql/table.rb +80 -25
- data/lib/plsql/type.rb +87 -0
- data/lib/plsql/variable.rb +146 -0
- data/lib/plsql/version.rb +1 -1
- data/lib/plsql/view.rb +41 -0
- data/lib/ruby_plsql.rb +1 -1
- data/spec/plsql/connection_spec.rb +112 -50
- data/spec/plsql/package_spec.rb +19 -10
- data/spec/plsql/procedure_spec.rb +159 -126
- data/spec/plsql/schema_spec.rb +109 -1
- data/spec/plsql/sql_statements_spec.rb +14 -32
- data/spec/plsql/table_spec.rb +75 -9
- data/spec/plsql/type_spec.rb +133 -0
- data/spec/plsql/variable_spec.rb +458 -0
- data/spec/plsql/version_spec.rb +8 -0
- data/spec/plsql/view_spec.rb +259 -0
- data/spec/spec_helper.rb +1 -1
- metadata +15 -2
data/lib/plsql/table.rb
CHANGED
@@ -12,12 +12,18 @@ module PLSQL
|
|
12
12
|
elsif (row = schema.select_first(
|
13
13
|
"SELECT t.owner, t.table_name
|
14
14
|
FROM all_synonyms s, all_tables t
|
15
|
-
WHERE s.owner
|
15
|
+
WHERE s.owner = :owner
|
16
16
|
AND s.synonym_name = :synonym_name
|
17
17
|
AND t.owner = s.table_owner
|
18
18
|
AND t.table_name = s.table_name
|
19
|
-
|
20
|
-
|
19
|
+
UNION ALL
|
20
|
+
SELECT t.owner, t.table_name
|
21
|
+
FROM all_synonyms s, all_tables t
|
22
|
+
WHERE s.owner = 'PUBLIC'
|
23
|
+
AND s.synonym_name = :synonym_name
|
24
|
+
AND t.owner = s.table_owner
|
25
|
+
AND t.table_name = s.table_name",
|
26
|
+
schema.schema_name, table.to_s.upcase, table.to_s.upcase))
|
21
27
|
new(schema, row[1], row[0])
|
22
28
|
else
|
23
29
|
nil
|
@@ -36,16 +42,17 @@ module PLSQL
|
|
36
42
|
@table_name = table.to_s.upcase
|
37
43
|
@columns = {}
|
38
44
|
|
39
|
-
@schema.
|
40
|
-
SELECT c.column_name, c.column_id position,
|
45
|
+
@schema.select_all(
|
46
|
+
"SELECT c.column_name, c.column_id position,
|
41
47
|
c.data_type, c.data_length, c.data_precision, c.data_scale, c.char_used,
|
42
|
-
c.data_type_owner, c.data_type_mod,
|
43
|
-
|
48
|
+
c.data_type_owner, c.data_type_mod,
|
49
|
+
CASE WHEN c.data_type_owner IS NULL THEN NULL
|
50
|
+
ELSE (SELECT t.typecode FROM all_types t
|
51
|
+
WHERE t.owner = c.data_type_owner
|
52
|
+
AND t.type_name = c.data_type) END typecode
|
53
|
+
FROM all_tab_columns c
|
44
54
|
WHERE c.owner = :owner
|
45
|
-
AND c.table_name = :table_name
|
46
|
-
AND t.owner(+) = c.data_type_owner
|
47
|
-
AND t.type_name(+) = c.data_type
|
48
|
-
ORDER BY c.column_id",
|
55
|
+
AND c.table_name = :table_name",
|
49
56
|
@schema_name, @table_name
|
50
57
|
) do |r|
|
51
58
|
column_name, position,
|
@@ -65,6 +72,11 @@ module PLSQL
|
|
65
72
|
end
|
66
73
|
end
|
67
74
|
|
75
|
+
# list of table column names
|
76
|
+
def column_names
|
77
|
+
@column_names ||= @columns.keys.sort_by{|k| columns[k][:position]}
|
78
|
+
end
|
79
|
+
|
68
80
|
# General select method with :first, :all or :count as first parameter.
|
69
81
|
# It is recommended to use #first, #all or #count method instead of this one.
|
70
82
|
def select(first_or_all, sql_params='', *bindvars)
|
@@ -87,6 +99,8 @@ module PLSQL
|
|
87
99
|
sql_params.each do |k,v|
|
88
100
|
if k == :order_by
|
89
101
|
order_by_sql = "ORDER BY #{v} "
|
102
|
+
elsif v.nil?
|
103
|
+
where_sqls << "#{k} IS NULL"
|
90
104
|
else
|
91
105
|
where_sqls << "#{k} = :#{k}"
|
92
106
|
bindvars << v
|
@@ -150,10 +164,44 @@ module PLSQL
|
|
150
164
|
return nil
|
151
165
|
end
|
152
166
|
|
153
|
-
|
167
|
+
table_proc = TableProcedure.new(@schema, self, :insert)
|
168
|
+
table_proc.add_insert_arguments(record)
|
169
|
+
|
170
|
+
call = ProcedureCall.new(table_proc, table_proc.argument_values)
|
154
171
|
call.exec
|
155
172
|
end
|
156
173
|
|
174
|
+
# Insert record or records in table using array of values. Examples:
|
175
|
+
#
|
176
|
+
# # with values for all columns
|
177
|
+
# plsql.employees.insert_values [1, 'First', 'Last', Time.local(2000,01,31)]
|
178
|
+
# # => INSERT INTO employees VALUES (1, 'First', 'Last', ...)
|
179
|
+
#
|
180
|
+
# # with values for specified columns
|
181
|
+
# plsql.employees.insert_values [:employee_id, :first_name, :last_name], [1, 'First', 'Last']
|
182
|
+
# # => INSERT INTO employees (employee_id, first_name, last_name) VALUES (1, 'First', 'Last')
|
183
|
+
#
|
184
|
+
# # with values for many records
|
185
|
+
# plsql.employees.insert_values [:employee_id, :first_name, :last_name], [1, 'First', 'Last'], [2, 'Second', 'Last']
|
186
|
+
# # => INSERT INTO employees (employee_id, first_name, last_name) VALUES (1, 'First', 'Last')
|
187
|
+
# # => INSERT INTO employees (employee_id, first_name, last_name) VALUES (2, 'Second', 'Last')
|
188
|
+
#
|
189
|
+
def insert_values(*args)
|
190
|
+
raise ArgumentError, "no arguments given" unless args.first
|
191
|
+
# if first argument is array of symbols then use it as list of fields
|
192
|
+
if args.first.all?{|a| a.instance_of?(Symbol)}
|
193
|
+
fields = args.shift
|
194
|
+
# otherwise use all columns as list of fields
|
195
|
+
else
|
196
|
+
fields = column_names
|
197
|
+
end
|
198
|
+
args.each do |record|
|
199
|
+
raise ArgumentError, "record should be Array of values" unless record.is_a?(Array)
|
200
|
+
raise ArgumentError, "wrong number of column values" unless record.size == fields.size
|
201
|
+
insert(ArrayHelpers::to_hash(fields, record))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
157
205
|
# Update table records using optional conditions. Example:
|
158
206
|
#
|
159
207
|
# plsql.employees.update(:first_name => 'Second', :where => {:employee_id => 1})
|
@@ -194,12 +242,6 @@ module PLSQL
|
|
194
242
|
@schema.execute(delete_sql, *bindvars)
|
195
243
|
end
|
196
244
|
|
197
|
-
private
|
198
|
-
|
199
|
-
def get_typecode(owner, type_name)
|
200
|
-
|
201
|
-
end
|
202
|
-
|
203
245
|
# wrapper class to simulate Procedure class for ProcedureClass#exec
|
204
246
|
class TableProcedure #:nodoc:
|
205
247
|
attr_reader :arguments, :argument_list, :return, :out_list, :schema
|
@@ -214,11 +256,10 @@ module PLSQL
|
|
214
256
|
|
215
257
|
case @operation
|
216
258
|
when :insert
|
217
|
-
@argument_list = [[
|
218
|
-
@arguments = [{
|
219
|
-
|
220
|
-
|
221
|
-
}}]
|
259
|
+
@argument_list = [[]]
|
260
|
+
@arguments = [{}]
|
261
|
+
@insert_columns = []
|
262
|
+
@insert_values = []
|
222
263
|
when :update
|
223
264
|
@argument_list = [[]]
|
224
265
|
@arguments = [{}]
|
@@ -237,6 +278,15 @@ module PLSQL
|
|
237
278
|
nil
|
238
279
|
end
|
239
280
|
|
281
|
+
def add_insert_arguments(params)
|
282
|
+
params.each do |k,v|
|
283
|
+
raise ArgumentError, "Invalid column name #{k.inspect} specified as argument" unless (column_metadata = @table.columns[k])
|
284
|
+
@argument_list[0] << k
|
285
|
+
@arguments[0][k] = column_metadata
|
286
|
+
@insert_values << v
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
240
290
|
def add_set_arguments(params)
|
241
291
|
params.each do |k,v|
|
242
292
|
raise ArgumentError, "Invalid column name #{k.inspect} specified as argument" unless (column_metadata = @table.columns[k])
|
@@ -263,13 +313,18 @@ module PLSQL
|
|
263
313
|
end
|
264
314
|
|
265
315
|
def argument_values
|
266
|
-
|
316
|
+
case @operation
|
317
|
+
when :insert
|
318
|
+
@insert_values
|
319
|
+
when :update
|
320
|
+
@set_values + @where_values
|
321
|
+
end
|
267
322
|
end
|
268
323
|
|
269
324
|
def call_sql(params_string)
|
270
325
|
case @operation
|
271
326
|
when :insert
|
272
|
-
"INSERT INTO \"#{@table.schema_name}\".\"#{@table.table_name}\" VALUES #{params_string};\n"
|
327
|
+
"INSERT INTO \"#{@table.schema_name}\".\"#{@table.table_name}\"(#{@argument_list[0].map{|a| a.to_s}.join(', ')}) VALUES (#{params_string});\n"
|
273
328
|
when :update
|
274
329
|
update_sql = "UPDATE \"#{@table.schema_name}\".\"#{@table.table_name}\" SET #{@set_sqls.join(', ')}"
|
275
330
|
update_sql << " WHERE #{@where_sqls.join(' AND ')}" unless @where_sqls.empty?
|
data/lib/plsql/type.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
module PLSQL
|
2
|
+
|
3
|
+
module TypeClassMethods #:nodoc:
|
4
|
+
def find(schema, type)
|
5
|
+
if schema.select_first(
|
6
|
+
"SELECT type_name FROM all_types
|
7
|
+
WHERE owner = :owner
|
8
|
+
AND type_name = :table_name",
|
9
|
+
schema.schema_name, type.to_s.upcase)
|
10
|
+
new(schema, type)
|
11
|
+
# search for synonym
|
12
|
+
elsif (row = schema.select_first(
|
13
|
+
"SELECT t.owner, t.type_name
|
14
|
+
FROM all_synonyms s, all_types t
|
15
|
+
WHERE s.owner = :owner
|
16
|
+
AND s.synonym_name = :synonym_name
|
17
|
+
AND t.owner = s.table_owner
|
18
|
+
AND t.type_name = s.table_name
|
19
|
+
UNION ALL
|
20
|
+
SELECT t.owner, t.type_name
|
21
|
+
FROM all_synonyms s, all_types t
|
22
|
+
WHERE s.owner = 'PUBLIC'
|
23
|
+
AND s.synonym_name = :synonym_name
|
24
|
+
AND t.owner = s.table_owner
|
25
|
+
AND t.type_name = s.table_name",
|
26
|
+
schema.schema_name, type.to_s.upcase, type.to_s.upcase))
|
27
|
+
new(schema, row[1], row[0])
|
28
|
+
else
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Type
|
35
|
+
extend TypeClassMethods
|
36
|
+
|
37
|
+
attr_reader :typecode, :attributes, :schema_name, :type_name #:nodoc:
|
38
|
+
|
39
|
+
def initialize(schema, type, override_schema_name = nil) #:nodoc:
|
40
|
+
@schema = schema
|
41
|
+
@schema_name = override_schema_name || schema.schema_name
|
42
|
+
@type_name = type.to_s.upcase
|
43
|
+
@attributes = {}
|
44
|
+
|
45
|
+
@typecode = @schema.select_first(
|
46
|
+
"SELECT typecode FROM all_types
|
47
|
+
WHERE owner = :owner
|
48
|
+
AND type_name = :type_name",
|
49
|
+
@schema_name, @type_name)[0]
|
50
|
+
|
51
|
+
@schema.select_all(
|
52
|
+
"SELECT attr_name, attr_no,
|
53
|
+
attr_type_name, length, precision, scale,
|
54
|
+
attr_type_owner, attr_type_mod,
|
55
|
+
(SELECT t.typecode FROM all_types t
|
56
|
+
WHERE t.owner = attr_type_owner
|
57
|
+
AND t.type_name = attr_type_name) typecode
|
58
|
+
FROM all_type_attrs
|
59
|
+
WHERE owner = :owner
|
60
|
+
AND type_name = :type_name
|
61
|
+
ORDER BY attr_no",
|
62
|
+
@schema_name, @type_name
|
63
|
+
) do |r|
|
64
|
+
attr_name, position,
|
65
|
+
data_type, data_length, data_precision, data_scale,
|
66
|
+
data_type_owner, data_type_mod, typecode = r
|
67
|
+
@attributes[attr_name.downcase.to_sym] = {
|
68
|
+
:position => position && position.to_i,
|
69
|
+
:data_type => data_type_owner && (typecode == 'COLLECTION' ? 'TABLE' : 'OBJECT' ) || data_type,
|
70
|
+
:data_length => data_type_owner ? nil : data_length && data_length.to_i,
|
71
|
+
:data_precision => data_precision && data_precision.to_i,
|
72
|
+
:data_scale => data_scale && data_scale.to_i,
|
73
|
+
:type_owner => data_type_owner,
|
74
|
+
:type_name => data_type_owner && data_type,
|
75
|
+
:sql_type_name => data_type_owner && "#{data_type_owner}.#{data_type}"
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# list of object type attribute names
|
81
|
+
def attribute_names
|
82
|
+
@attribute_names ||= @attributes.keys.sort_by{|k| @attributes[k][:position]}
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module PLSQL
|
2
|
+
|
3
|
+
module VariableClassMethods #:nodoc:
|
4
|
+
def find(schema, variable, package, override_schema_name = nil)
|
5
|
+
variable_upcase = variable.to_s.upcase
|
6
|
+
schema.select_all(
|
7
|
+
"SELECT text FROM all_source
|
8
|
+
WHERE owner = :owner
|
9
|
+
AND name = :object_name
|
10
|
+
AND type = 'PACKAGE'
|
11
|
+
AND UPPER(text) LIKE :variable_name",
|
12
|
+
override_schema_name || schema.schema_name, package, "%#{variable_upcase}%").each do |row|
|
13
|
+
if row[0] =~ /^\s*#{variable_upcase}\s+(CONSTANT\s+)?([A-Z0-9_. %]+(\([0-9,]+\))?)\s*(NOT\s+NULL)?\s*((:=|DEFAULT).*)?;\s*(--.*)?$/i
|
14
|
+
return new(schema, variable, package, $2.strip, override_schema_name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Variable #:nodoc:
|
22
|
+
extend VariableClassMethods
|
23
|
+
|
24
|
+
attr_reader :schema_name, :package_name, :variable_name #:nodoc:
|
25
|
+
|
26
|
+
def initialize(schema, variable, package, variable_type, override_schema_name = nil)
|
27
|
+
@schema = schema
|
28
|
+
@schema_name = override_schema_name || schema.schema_name
|
29
|
+
@variable_name = variable.to_s.upcase
|
30
|
+
@package_name = package
|
31
|
+
@variable_type = variable_type.upcase
|
32
|
+
@metadata = metadata(@variable_type)
|
33
|
+
end
|
34
|
+
|
35
|
+
def value
|
36
|
+
@variable_get_proc ||= VariableProcedure.new(@schema, self, :get, @metadata)
|
37
|
+
ProcedureCall.new(@variable_get_proc).exec
|
38
|
+
end
|
39
|
+
|
40
|
+
def value=(new_value)
|
41
|
+
@variable_set_proc ||= VariableProcedure.new(@schema, self, :set, @metadata)
|
42
|
+
ProcedureCall.new(@variable_set_proc, [new_value]).exec
|
43
|
+
new_value
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def metadata(type_string)
|
49
|
+
case type_string
|
50
|
+
when /^(VARCHAR2|CHAR|NVARCHAR2|NCHAR)(\((\d+)\))?$/
|
51
|
+
{:data_type => $1, :data_length => $3.to_i, :in_out => 'IN/OUT'}
|
52
|
+
when /^(CLOB|NCLOB|BLOB)$/,
|
53
|
+
/^(NUMBER)(\(.*\))?$/, /^(PLS_INTEGER|BINARY_INTEGER)$/,
|
54
|
+
/^(DATE|TIMESTAMP|TIMESTAMP WITH TIME ZONE|TIMESTAMP WITH LOCAL TIME ZONE)$/
|
55
|
+
{:data_type => $1, :in_out => 'IN/OUT'}
|
56
|
+
when /^INTEGER$/
|
57
|
+
{:data_type => 'NUMBER', :in_out => 'IN/OUT'}
|
58
|
+
when /^BOOLEAN$/
|
59
|
+
{:data_type => 'PL/SQL BOOLEAN', :in_out => 'IN/OUT'}
|
60
|
+
when /^(\w+\.)?(\w+)\.(\w+)%TYPE$/
|
61
|
+
schema = $1 ? plsql.send($1.chop) : plsql
|
62
|
+
table = schema.send($2.downcase.to_sym)
|
63
|
+
column = table.columns[$3.downcase.to_sym]
|
64
|
+
{:data_type => column[:data_type], :data_length => column[:data_length], :sql_type_name => column[:sql_type_name], :in_out => 'IN/OUT'}
|
65
|
+
when /^(\w+\.)?(\w+)$/
|
66
|
+
schema = $1 ? plsql.send($1.chop) : plsql
|
67
|
+
begin
|
68
|
+
type = schema.send($2.downcase.to_sym)
|
69
|
+
raise ArgumentError unless type.is_a?(PLSQL::Type)
|
70
|
+
typecode = case type.typecode
|
71
|
+
when 'COLLECTION' then 'TABLE'
|
72
|
+
else 'OBJECT'
|
73
|
+
end
|
74
|
+
{:data_type => typecode, :data_length => nil, :sql_type_name => "#{type.schema_name}.#{type.type_name}", :in_out => 'IN/OUT'}
|
75
|
+
rescue ArgumentError
|
76
|
+
raise ArgumentError, "Package variable data type #{type_string} is not object type defined in schema"
|
77
|
+
end
|
78
|
+
when /^(\w+\.)?(\w+)%ROWTYPE$/
|
79
|
+
schema = $1 ? plsql.send($1.chop) : plsql
|
80
|
+
table = schema.send($2.downcase.to_sym)
|
81
|
+
record_metadata = {
|
82
|
+
:data_type => 'PL/SQL RECORD',
|
83
|
+
:in_out => 'IN/OUT',
|
84
|
+
:fields => {}
|
85
|
+
}
|
86
|
+
table.columns.each do |name, column|
|
87
|
+
record_metadata[:fields][name] =
|
88
|
+
{:data_type => column[:data_type], :data_length => column[:data_length], :sql_type_name => column[:sql_type_name],
|
89
|
+
:position => column[:position], :in_out => 'IN/OUT'}
|
90
|
+
end
|
91
|
+
record_metadata
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Package variable data type #{type_string} is not supported"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# wrapper class to simulate Procedure class for ProcedureClass#exec
|
98
|
+
class VariableProcedure #:nodoc:
|
99
|
+
attr_reader :arguments, :argument_list, :return, :out_list, :schema
|
100
|
+
|
101
|
+
def initialize(schema, variable, operation, metadata)
|
102
|
+
@schema = schema
|
103
|
+
@variable = variable
|
104
|
+
@operation = operation
|
105
|
+
@metadata = metadata
|
106
|
+
|
107
|
+
@out_list = [[]]
|
108
|
+
|
109
|
+
case @operation
|
110
|
+
when :get
|
111
|
+
@argument_list = [[]]
|
112
|
+
@arguments = [{}]
|
113
|
+
@return = [@metadata]
|
114
|
+
when :set
|
115
|
+
@argument_list = [[:value]]
|
116
|
+
@arguments = [{:value => @metadata}]
|
117
|
+
@return = [nil]
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
def overloaded?
|
123
|
+
false
|
124
|
+
end
|
125
|
+
|
126
|
+
def procedure
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
def call_sql(params_string)
|
131
|
+
sql = (schema_name = @variable.schema_name) ? "#{schema_name}." : ""
|
132
|
+
sql << "#{@variable.package_name}.#{@variable.variable_name}"
|
133
|
+
case @operation
|
134
|
+
when :get
|
135
|
+
# params string contains assignment to return variable
|
136
|
+
"#{params_string} #{sql};\n"
|
137
|
+
when :set
|
138
|
+
"#{sql} := #{params_string};\n"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
data/lib/plsql/version.rb
CHANGED
data/lib/plsql/view.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module PLSQL
|
2
|
+
|
3
|
+
module ViewClassMethods #:nodoc:
|
4
|
+
def find(schema, view)
|
5
|
+
if schema.select_first(
|
6
|
+
"SELECT view_name FROM all_views
|
7
|
+
WHERE owner = :owner
|
8
|
+
AND view_name = :view_name",
|
9
|
+
schema.schema_name, view.to_s.upcase)
|
10
|
+
new(schema, view)
|
11
|
+
# search for synonym
|
12
|
+
elsif (row = schema.select_first(
|
13
|
+
"SELECT v.owner, v.view_name
|
14
|
+
FROM all_synonyms s, all_views v
|
15
|
+
WHERE s.owner = :owner
|
16
|
+
AND s.synonym_name = :synonym_name
|
17
|
+
AND v.owner = s.table_owner
|
18
|
+
AND v.view_name = s.table_name
|
19
|
+
UNION ALL
|
20
|
+
SELECT v.owner, v.view_name
|
21
|
+
FROM all_synonyms s, all_views v
|
22
|
+
WHERE s.owner = 'PUBLIC'
|
23
|
+
AND s.synonym_name = :synonym_name
|
24
|
+
AND v.owner = s.table_owner
|
25
|
+
AND v.view_name = s.table_name",
|
26
|
+
schema.schema_name, view.to_s.upcase, view.to_s.upcase))
|
27
|
+
new(schema, row[1], row[0])
|
28
|
+
else
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class View < Table
|
35
|
+
extend ViewClassMethods
|
36
|
+
|
37
|
+
alias :view_name :table_name #:nodoc:
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|