momomoto 0.1.2 → 0.1.3
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/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
|