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 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