momomoto 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/momomoto/base.rb +30 -16
- data/lib/momomoto/procedure.rb +2 -2
- data/lib/momomoto/row.rb +8 -3
- data/lib/momomoto/table.rb +56 -25
- data/test/test_base.rb +25 -7
- data/test/test_table.rb +9 -0
- metadata +2 -2
data/lib/momomoto/base.rb
CHANGED
@@ -31,17 +31,20 @@ module Momomoto
|
|
31
31
|
class Nothing_found < Error; end
|
32
32
|
|
33
33
|
|
34
|
-
## Momomoto base class for Table
|
34
|
+
## Momomoto base class for Table and Procedure
|
35
35
|
class Base
|
36
36
|
|
37
37
|
class << self
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
attr_reader :logical_operator
|
40
|
+
|
41
|
+
# set the default logical operator for constraints
|
42
|
+
def logical_operator=( value )
|
43
|
+
@logical_operator = case value
|
44
|
+
when /and/i then "AND"
|
45
|
+
when /or/i then "OR"
|
46
|
+
else raise Momomoto::Error, "Unsupported logical operator"
|
47
|
+
end
|
45
48
|
end
|
46
49
|
|
47
50
|
# set the schema name of the table this class operates on
|
@@ -58,13 +61,25 @@ module Momomoto
|
|
58
61
|
@schema_name
|
59
62
|
end
|
60
63
|
|
64
|
+
protected
|
65
|
+
|
66
|
+
attr_accessor :initialized
|
67
|
+
|
68
|
+
# guesses the schema name of the table this class works on
|
69
|
+
def construct_schema_name( classname )
|
70
|
+
# Uncomment these lines to derive the schema from the enclosing namespace of the class
|
71
|
+
#schema = classname.split('::')[-2]
|
72
|
+
#schema ? schema.downcase.gsub(/[^a-z_0-9]/, '') : nil
|
73
|
+
'public'
|
74
|
+
end
|
75
|
+
|
61
76
|
# get the database connection
|
62
77
|
def database # :nodoc:
|
63
78
|
Momomoto::Database.instance
|
64
79
|
end
|
65
80
|
|
66
81
|
# compiles the where-clause of the query
|
67
|
-
def compile_where( conditions )
|
82
|
+
def compile_where( conditions )
|
68
83
|
conditions = {} if not conditions
|
69
84
|
where = ''
|
70
85
|
conditions.each do | key , value |
|
@@ -90,7 +105,7 @@ module Momomoto
|
|
90
105
|
end
|
91
106
|
|
92
107
|
# compiles the sql statement defining the table order
|
93
|
-
def compile_order( order )
|
108
|
+
def compile_order( order )
|
94
109
|
order = default_order if not order
|
95
110
|
order = [ order ] if not order.kind_of?( Array )
|
96
111
|
order = order.map do | field |
|
@@ -106,12 +121,12 @@ module Momomoto
|
|
106
121
|
end
|
107
122
|
|
108
123
|
# append where string
|
109
|
-
def where_append( where, append )
|
110
|
-
( where.empty? ? ' WHERE ' : where + '
|
124
|
+
def where_append( where, append )
|
125
|
+
( where.empty? ? ' WHERE ' : where + ' ' + logical_operator + ' ' ) + append
|
111
126
|
end
|
112
127
|
|
113
128
|
# construct the Row class for the table
|
114
|
-
def initialize_row( row, table )
|
129
|
+
def initialize_row( row, table )
|
115
130
|
|
116
131
|
const_set( :Methods, Module.new ) if not const_defined?( :Methods )
|
117
132
|
const_set( :StandardMethods, Module.new ) if not const_defined?( :StandardMethods )
|
@@ -120,9 +135,9 @@ module Momomoto
|
|
120
135
|
raise CriticalError, "Row is not inherited from Momomoto::Row"
|
121
136
|
end
|
122
137
|
|
123
|
-
row.instance_eval do
|
138
|
+
row.instance_eval do instance_variable_set( :@table, table ) end
|
124
139
|
|
125
|
-
define_row_accessors( table )
|
140
|
+
define_row_accessors( const_get( :StandardMethods ), table )
|
126
141
|
|
127
142
|
row.instance_eval do
|
128
143
|
include table.const_get( :StandardMethods )
|
@@ -133,8 +148,7 @@ module Momomoto
|
|
133
148
|
|
134
149
|
# defines row setter and getter in the module StandardMethods which
|
135
150
|
# is later included in the Row class
|
136
|
-
def define_row_accessors( table )
|
137
|
-
method_module = const_get( :StandardMethods )
|
151
|
+
def define_row_accessors( method_module, table, columns = self.columns )
|
138
152
|
columns.each_with_index do | ( field_name, data_type ), index |
|
139
153
|
method_module.instance_eval do
|
140
154
|
# define getter for row class
|
data/lib/momomoto/procedure.rb
CHANGED
@@ -18,7 +18,7 @@ module Momomoto
|
|
18
18
|
initialize_row( const_get( :Row ), self )
|
19
19
|
|
20
20
|
# mark class as initialized
|
21
|
-
|
21
|
+
self.initialized = true
|
22
22
|
|
23
23
|
end
|
24
24
|
|
@@ -84,7 +84,7 @@ module Momomoto
|
|
84
84
|
|
85
85
|
# execute the stored procedure
|
86
86
|
def call( params = {}, conditions = {}, options = {} )
|
87
|
-
initialize_procedure unless
|
87
|
+
initialize_procedure unless initialized
|
88
88
|
sql = "SELECT #{columns.keys.join(',')} FROM "
|
89
89
|
sql += "#{full_name}(#{compile_parameter(params)})"
|
90
90
|
sql += compile_where( conditions )
|
data/lib/momomoto/row.rb
CHANGED
@@ -7,15 +7,19 @@ module Momomoto
|
|
7
7
|
undef :id,:type
|
8
8
|
|
9
9
|
def self.table
|
10
|
-
|
10
|
+
@table
|
11
11
|
end
|
12
12
|
|
13
13
|
def []( fieldname )
|
14
|
-
|
14
|
+
get_column( fieldname )
|
15
15
|
end
|
16
16
|
|
17
17
|
def []=( fieldname, value )
|
18
|
-
|
18
|
+
set_column( fieldname, value )
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==( other )
|
22
|
+
@data == other.instance_variable_get( :@data )
|
19
23
|
end
|
20
24
|
|
21
25
|
def dirty?
|
@@ -87,3 +91,4 @@ module Momomoto
|
|
87
91
|
end
|
88
92
|
|
89
93
|
end
|
94
|
+
|
data/lib/momomoto/table.rb
CHANGED
@@ -12,7 +12,7 @@ module Momomoto
|
|
12
12
|
@default_order = order
|
13
13
|
end
|
14
14
|
|
15
|
-
# get the
|
15
|
+
# get the default order for selects
|
16
16
|
def default_order( order = nil )
|
17
17
|
return self.default_order=( order ) if order
|
18
18
|
@default_order
|
@@ -53,11 +53,13 @@ module Momomoto
|
|
53
53
|
@column_order = @columns.keys
|
54
54
|
@default_order ||= nil
|
55
55
|
|
56
|
+
@logical_operator ||= "AND"
|
57
|
+
|
56
58
|
const_set( :Row, Class.new( Momomoto::Row ) ) if not const_defined?( :Row )
|
57
59
|
initialize_row( const_get( :Row ), self )
|
58
60
|
|
59
61
|
# mark class as initialized
|
60
|
-
|
62
|
+
self.initialized = true
|
61
63
|
|
62
64
|
end
|
63
65
|
|
@@ -96,25 +98,47 @@ module Momomoto
|
|
96
98
|
@primary_keys
|
97
99
|
end
|
98
100
|
|
99
|
-
|
100
|
-
def
|
101
|
-
|
102
|
-
|
101
|
+
# compile the select clause
|
102
|
+
def compile_select( conditions, options ) # :nodoc:
|
103
|
+
if options[:columns]
|
104
|
+
cols = {}
|
105
|
+
options[:columns].each do | name | cols[name] = columns[name] end
|
106
|
+
else
|
107
|
+
cols = columns
|
108
|
+
end
|
109
|
+
sql = "SELECT " + cols.keys.map{ | field | '"' + field.to_s + '"' }.join( "," ) + " FROM "
|
103
110
|
sql += full_name
|
104
111
|
sql += compile_where( conditions )
|
105
112
|
sql += compile_order( options[:order] ) if options[:order] || default_order
|
106
113
|
sql += compile_limit( options[:limit] ) if options[:limit]
|
107
114
|
sql += compile_offset( options[:offset] ) if options[:offset]
|
115
|
+
sql
|
116
|
+
end
|
117
|
+
|
118
|
+
# Searches for records and returns an array containing the records
|
119
|
+
def select( conditions = {}, options = {} )
|
120
|
+
initialize_table unless initialized
|
121
|
+
if options[:columns]
|
122
|
+
row_class = Class.new( Momomoto::Row )
|
123
|
+
cols = {}
|
124
|
+
columns.each do | key, value |
|
125
|
+
cols[key] = value if options[:columns].member?( key )
|
126
|
+
end
|
127
|
+
define_row_accessors( row_class, self, cols )
|
128
|
+
else
|
129
|
+
row_class = const_get(:Row)
|
130
|
+
end
|
131
|
+
sql = compile_select( conditions, options )
|
108
132
|
data = []
|
109
133
|
database.execute( sql ).each do | row |
|
110
|
-
data <<
|
134
|
+
data << row_class.new( row )
|
111
135
|
end
|
112
136
|
data
|
113
137
|
end
|
114
138
|
|
115
|
-
|
116
|
-
def
|
117
|
-
initialize_table unless
|
139
|
+
# Searches for records and returns an array containing the records
|
140
|
+
def select_outer_join( conditions = {}, options = {} )
|
141
|
+
initialize_table unless initialized
|
118
142
|
join_table = options[:join]
|
119
143
|
fields = columns.keys.map{|field| full_name+'."'+field.to_s+'"'}
|
120
144
|
fields += join_table.columns.keys.map{|field| join_table.full_name+'."'+field.to_s+'"'}
|
@@ -126,24 +150,31 @@ module Momomoto
|
|
126
150
|
sql += compile_order( options[:order] ) if options[:order]
|
127
151
|
sql += compile_limit( options[:limit] ) if options[:limit]
|
128
152
|
sql += compile_offset( options[:offset] ) if options[:offset]
|
129
|
-
data =
|
153
|
+
data = {}
|
130
154
|
database.execute( sql ).each do | row |
|
131
|
-
new_row =
|
132
|
-
|
155
|
+
new_row = row[0, columns.keys.length]
|
156
|
+
data[new_row] ||= []
|
157
|
+
join_row = row[columns.keys.length,join_table.columns.keys.length]
|
158
|
+
data[new_row] << join_table.const_get(:Row).new( join_row ) if join_row.nitems > 0
|
159
|
+
end
|
160
|
+
result = []
|
161
|
+
data.each do | new_row, join_row |
|
162
|
+
new_row = const_get(:Row).new( new_row )
|
133
163
|
new_row.instance_variable_set(:@join, join_row)
|
134
164
|
new_row.send( :instance_eval ) { class << self; self; end }.send(:define_method, join_table.table_name ) do join_row end
|
135
|
-
|
165
|
+
result << new_row
|
136
166
|
end
|
137
|
-
|
167
|
+
result
|
138
168
|
end
|
139
169
|
|
140
|
-
|
170
|
+
# returns the columns to be used for joining
|
171
|
+
def join_columns( join_table ) # :nodoc:
|
141
172
|
join_table.primary_keys.select{|f| columns.key?(f)}
|
142
173
|
end
|
143
174
|
|
144
|
-
|
175
|
+
# constructor for a record in this table accepts a hash with presets for the fields of the record
|
145
176
|
def new( fields = {} )
|
146
|
-
initialize_table unless
|
177
|
+
initialize_table unless initialized
|
147
178
|
new_row = const_get(:Row).new( [] )
|
148
179
|
new_row.new_record = true
|
149
180
|
# set default values
|
@@ -167,10 +198,10 @@ module Momomoto
|
|
167
198
|
new_row
|
168
199
|
end
|
169
200
|
|
170
|
-
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
201
|
+
# Tries to find a specific record and creates a new one if it does not find it.
|
202
|
+
# Raises an exception if multiple records are found.
|
203
|
+
# You can pass a block which has to deliver the respective values for the
|
204
|
+
# primary key fields
|
174
205
|
def select_or_new( conditions = {}, options = {} )
|
175
206
|
begin
|
176
207
|
if block_given?
|
@@ -192,8 +223,8 @@ module Momomoto
|
|
192
223
|
end
|
193
224
|
end
|
194
225
|
|
195
|
-
|
196
|
-
#
|
226
|
+
# Select a single row from the database, raises an exception if more or zero
|
227
|
+
# rows are found
|
197
228
|
def select_single( conditions = {}, options = {} )
|
198
229
|
data = select( conditions, options )
|
199
230
|
case data.length
|
@@ -209,7 +240,7 @@ module Momomoto
|
|
209
240
|
if row.new_record?
|
210
241
|
insert( row )
|
211
242
|
else
|
212
|
-
|
243
|
+
return false unless row.dirty?
|
213
244
|
update( row )
|
214
245
|
end
|
215
246
|
row.dirty = false
|
data/test/test_base.rb
CHANGED
@@ -1,16 +1,34 @@
|
|
1
1
|
|
2
2
|
class TestBase < Test::Unit::TestCase
|
3
3
|
|
4
|
+
def test_logical_operator
|
5
|
+
t1 = Class.new( Momomoto::Table )
|
6
|
+
t1.table_name = 'person'
|
7
|
+
t1.initialize_table
|
8
|
+
t2 = Class.new( Momomoto::Table )
|
9
|
+
t2.table_name = 'person'
|
10
|
+
t2.initialize_table
|
11
|
+
assert_equal( "AND", t1.logical_operator )
|
12
|
+
assert_equal( "AND", t2.logical_operator )
|
13
|
+
t1.logical_operator = "OR"
|
14
|
+
assert_equal( "OR", t1.logical_operator )
|
15
|
+
assert_equal( "AND", t2.logical_operator )
|
16
|
+
t1.logical_operator = "or"
|
17
|
+
assert_equal( "OR", t1.logical_operator )
|
18
|
+
assert_equal( " WHERE first_name = E'a' OR person_id = '1'" , t1.instance_eval do compile_where(:person_id=>'1',:first_name=>'a') end )
|
19
|
+
assert_equal( " WHERE first_name = E'a' AND person_id = '1'" , t2.instance_eval do compile_where(:person_id=>'1',:first_name=>'a') end )
|
20
|
+
end
|
21
|
+
|
4
22
|
def test_compile_where
|
5
23
|
t = Class.new( Momomoto::Table )
|
6
24
|
t.table_name = 'person'
|
7
|
-
t.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
25
|
+
t.initialize_table
|
26
|
+
assert_equal( " WHERE person_id = '1'" , t.instance_eval do compile_where( :person_id => '1' ) end )
|
27
|
+
assert_equal( " WHERE person_id IN ('1')" , t.instance_eval do compile_where( :person_id => ['1'] ) end )
|
28
|
+
assert_equal( " WHERE person_id IN ('1','2')" , t.instance_eval do compile_where( :person_id => ['1',2] ) end )
|
29
|
+
assert_equal( " WHERE first_name = E'1'" , t.instance_eval do compile_where( :first_name => '1' ) end )
|
30
|
+
assert_equal( " WHERE first_name = E'chu''nky'" , t.instance_eval do compile_where( :first_name => "chu'nky" ) end )
|
31
|
+
assert_equal( " WHERE first_name IN (E'chu''nky',E'bac''n')" , t.instance_eval do compile_where( :first_name => ["chu'nky","bac'n"] ) end )
|
14
32
|
end
|
15
33
|
|
16
34
|
end
|
data/test/test_table.rb
CHANGED
@@ -131,6 +131,15 @@ class TestTable < Test::Unit::TestCase
|
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
+
def test_select_columns
|
135
|
+
p = Person.select({},{:columns=>[:person_id,:first_name],:limit=>1})[0]
|
136
|
+
assert( p.respond_to?( :person_id ))
|
137
|
+
assert( p.respond_to?( :first_name ))
|
138
|
+
assert_raise( NoMethodError ) do
|
139
|
+
p.nick_name
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
134
143
|
def test_select
|
135
144
|
r = Person.select( nil, {:limit => 3})
|
136
145
|
assert_equal( 3, r.length )
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: momomoto
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2007-08-
|
6
|
+
version: 0.1.3
|
7
|
+
date: 2007-08-17 00:00:00 +02:00
|
8
8
|
summary: Momomoto is an object relational mapper for PostgreSQL.
|
9
9
|
require_paths:
|
10
10
|
- lib
|