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 CHANGED
@@ -31,17 +31,20 @@ module Momomoto
31
31
  class Nothing_found < Error; end
32
32
 
33
33
 
34
- ## Momomoto base class for Table, Procedure and Join
34
+ ## Momomoto base class for Table and Procedure
35
35
  class Base
36
36
 
37
37
  class << self
38
38
 
39
- # guesses the schema name of the table this class works on
40
- def construct_schema_name( classname ) # :nodoc:
41
- # Uncomment these lines to derive the schema from the enclosing namespace of the class
42
- #schema = classname.split('::')[-2]
43
- #schema ? schema.downcase.gsub(/[^a-z_0-9]/, '') : nil
44
- 'public'
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 ) # :nodoc:
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 ) # :nodoc:
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 ) # :nodoc:
110
- ( where.empty? ? ' WHERE ' : where + ' AND ' ) + append
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 ) # :nodoc:
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 class_variable_set( :@@table, table ) end
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 ) #:nodoc:
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
@@ -18,7 +18,7 @@ module Momomoto
18
18
  initialize_row( const_get( :Row ), self )
19
19
 
20
20
  # mark class as initialized
21
- class_variable_set( :@@initialized, true)
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 class_variables.member?('@@initialized')
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
- class_variable_get( :@@table )
10
+ @table
11
11
  end
12
12
 
13
13
  def []( fieldname )
14
- send( fieldname )
14
+ get_column( fieldname )
15
15
  end
16
16
 
17
17
  def []=( fieldname, value )
18
- send( fieldname.to_s + '=', value )
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
+
@@ -12,7 +12,7 @@ module Momomoto
12
12
  @default_order = order
13
13
  end
14
14
 
15
- # get the columns of the table this class operates on
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
- class_variable_set( :@@initialized, true)
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
- ## Searches for records and returns an array containing the records
100
- def select( conditions = {}, options = {} )
101
- initialize_table unless class_variables.member?('@@initialized')
102
- sql = "SELECT " + columns.keys.map{ | field | '"' + field.to_s + '"' }.join( "," ) + " FROM "
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 << const_get(:Row).new( row )
134
+ data << row_class.new( row )
111
135
  end
112
136
  data
113
137
  end
114
138
 
115
- ## Searches for records and returns an array containing the records
116
- def select_with_join( conditions = {}, options = {} )
117
- initialize_table unless class_variables.member?('@@initialized')
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 = const_get(:Row).new( row[0, columns.keys.length] )
132
- join_row = join_table.const_get(:Row).new( row[columns.keys.length,join_table.columns.keys.length] )
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
- data << new_row
165
+ result << new_row
136
166
  end
137
- data
167
+ result
138
168
  end
139
169
 
140
- def join_columns( join_table )
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
- ## constructor for a record in this table accepts a hash with presets for the fields of the record
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 class_variables.member?('@@initialized')
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
- ## Tries to find a specific record and creates a new one if it does not find it
171
- # raises an exception if multiple records are found
172
- # You can pass a block which has to deliver the respective values for the
173
- # primary key fields
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
- ## Select a single row from the database, raises an exception if more or zero
196
- # rows are found
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
- # return false unless row.dirty?
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.columns
8
- assert_equal( " WHERE person_id = '1'" , t.compile_where( :person_id => '1' ) )
9
- assert_equal( " WHERE person_id IN ('1')" , t.compile_where( :person_id => ['1'] ) )
10
- assert_equal( " WHERE person_id IN ('1','2')" , t.compile_where( :person_id => ['1',2] ) )
11
- assert_equal( " WHERE first_name = E'1'" , t.compile_where( :first_name => '1' ) )
12
- assert_equal( " WHERE first_name = E'chu''nky'" , t.compile_where( :first_name => "chu'nky" ) )
13
- assert_equal( " WHERE first_name IN (E'chu''nky',E'bac''n')" , t.compile_where( :first_name => ["chu'nky","bac'n"] ) )
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.2
7
- date: 2007-08-13 00:00:00 +02:00
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