momomoto 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/momomoto/base.rb CHANGED
@@ -114,30 +114,43 @@ module Momomoto
114
114
  # construct the Row class for the table
115
115
  def initialize_row( row, table ) # :nodoc:
116
116
 
117
+ const_set( :Methods, Module.new ) if not const_defined?( :Methods )
118
+ const_set( :StandardMethods, Module.new ) if not const_defined?( :StandardMethods )
119
+
117
120
  if not row.ancestors.member?( Momomoto::Row )
118
121
  raise CriticalError, "Row is not inherited from Momomoto::Row"
119
122
  end
120
123
 
121
124
  row.instance_eval do class_variable_set( :@@table, table ) end
122
125
 
126
+ define_row_accessors( table )
127
+
128
+ row.instance_eval do
129
+ include table.const_get( :StandardMethods )
130
+ include table.const_get( :Methods )
131
+ end
132
+
133
+ end
134
+
135
+ # defines row setter and getter in the module StandardMethods which
136
+ # is later included in the Row class
137
+ def define_row_accessors( table ) #:nodoc:
138
+ method_module = const_get( :StandardMethods )
123
139
  columns.each_with_index do | ( field_name, data_type ), index |
124
- row.instance_eval do
140
+ method_module.instance_eval do
125
141
  # define getter for row class
126
- if not public_method_defined?( field_name )
127
- if data_type.respond_to?( :filter_get )
128
- define_method( field_name ) do
129
- data_type.filter_get( instance_variable_get(:@data)[index] )
130
- end
131
- else
132
- define_method( field_name ) do
133
- instance_variable_get(:@data)[index]
134
- end
142
+ if data_type.respond_to?( :filter_get )
143
+ define_method( field_name ) do
144
+ data_type.filter_get( instance_variable_get(:@data)[index] )
145
+ end
146
+ else
147
+ define_method( field_name ) do
148
+ instance_variable_get(:@data)[index]
135
149
  end
136
150
  end
137
151
 
138
152
  # define setter for row class
139
- setter_name = "#{field_name}="
140
- define_method( setter_name ) do | value |
153
+ define_method( "#{field_name}=" ) do | value |
141
154
  if not new_record? and table.primary_keys.member?( field_name )
142
155
  raise Error, "Setting primary keys(#{field_name}) is only allowed for new records"
143
156
  end
@@ -151,7 +164,6 @@ module Momomoto
151
164
 
152
165
  end
153
166
  end
154
-
155
167
  end
156
168
 
157
169
  end
@@ -1,7 +1,8 @@
1
1
 
2
2
  begin
3
3
  require 'rubygems'
4
- require_gem 'ruby-postgres', '>= 0.7.1.2006.04.06'
4
+ gem 'ruby-postgres', '>= 0.7.1.2006.04.06'
5
+ require 'postgres'
5
6
  rescue LoadError
6
7
  require 'postgres'
7
8
  end
@@ -63,7 +64,7 @@ module Momomoto
63
64
  def disconnect
64
65
  @connection.close
65
66
  @connection = nil
66
- @transaction_active = true
67
+ @transaction_active = false
67
68
  end
68
69
 
69
70
  # execute a query
@@ -71,6 +72,12 @@ module Momomoto
71
72
  puts sql if Momomoto.debug
72
73
  @connection.query( sql )
73
74
  rescue => e
75
+ if @connection.status == PGconn::CONNECTION_BAD
76
+ begin
77
+ connect
78
+ rescue
79
+ end
80
+ end
74
81
  raise CriticalError, "#{e}: #{sql}"
75
82
  end
76
83
 
@@ -4,7 +4,7 @@ module Momomoto
4
4
  class Bytea < Base
5
5
 
6
6
  def escape( input )
7
- input.nil? ? "NULL" : "'" + Database.escape_bytea( input ) + "'"
7
+ input.nil? ? "NULL" : "E'" + Database.escape_bytea( input ) + "'"
8
8
  end
9
9
 
10
10
  end
@@ -6,7 +6,7 @@ module Momomoto
6
6
  if input.nil? || input.empty?
7
7
  "NULL"
8
8
  else
9
- "'" + Database.escape_string( input.to_s ) + "'"
9
+ "E'" + Database.escape_string( input.to_s ) + "'"
10
10
  end
11
11
  end
12
12
 
data/lib/momomoto/row.rb CHANGED
@@ -50,6 +50,7 @@ module Momomoto
50
50
  self.class.table.delete( self )
51
51
  end
52
52
 
53
+ # convert row to hash
53
54
  def to_hash
54
55
  hash = {}
55
56
  self.class.table.columns.keys.each do | key |
@@ -58,6 +59,31 @@ module Momomoto
58
59
  hash
59
60
  end
60
61
 
62
+ # generic setter for column values
63
+ def set_column( column, value )
64
+ table = self.class.table
65
+ if not new_record? and table.primary_keys.member?( column.to_sym )
66
+ raise Error, "Setting primary keys(#{column}) is only allowed for new records"
67
+ end
68
+ value = table.columns[column.to_sym].filter_set( value )
69
+ index = table.column_order.index( column.to_sym )
70
+ if @data[index] != value
71
+ @dirty = true
72
+ @data[index] = value
73
+ end
74
+ end
75
+
76
+ # generic getter for column values
77
+ def get_column( column )
78
+ table = self.class.table
79
+ index = table.column_order.index( column.to_sym )
80
+ if table.columns[column.to_sym].respond_to?( :filter_get )
81
+ table.columns[column.to_sym].filter_get( @data[index] )
82
+ else
83
+ @data[index]
84
+ end
85
+ end
86
+
61
87
  end
62
88
 
63
89
  end
@@ -9,18 +9,31 @@ module Momomoto
9
9
 
10
10
  # set the default order for selects
11
11
  def default_order=( order )
12
- @default_order = order
12
+ class_variable_set( :@@default_order, order )
13
13
  end
14
14
 
15
- # get/set the default order for selects
15
+ # get the columns of the table this class operates on
16
16
  def default_order( order = nil )
17
17
  return self.default_order=( order ) if order
18
- @default_order
18
+ begin
19
+ class_variable_get( :@@default_order )
20
+ rescue NameError
21
+ class_variable_set( :@@default_order, nil )
22
+ end
19
23
  end
20
24
 
21
25
  # set the columns of the table this class operates on
22
26
  def columns=( columns )
23
27
  class_variable_set( :@@columns, columns)
28
+ # we store the order separate because it's quite important
29
+ # that it's constant otherwise get_colum and set_column
30
+ # on the row class might stop working
31
+ class_variable_set( :@@column_order, columns.keys )
32
+ end
33
+
34
+ # get the columns of this table
35
+ def column_order
36
+ class_variable_get( :@@column_order )
24
37
  end
25
38
 
26
39
  # get the columns of the table this class operates on
@@ -117,6 +130,35 @@ module Momomoto
117
130
  data
118
131
  end
119
132
 
133
+ ## Searches for records and returns an array containing the records
134
+ def select_with_join( conditions = {}, options = {} )
135
+ initialize_table unless class_variables.member?('@@initialized')
136
+ join_table = options[:join]
137
+ fields = columns.keys.map{|field| full_name+'."'+field.to_s+'"'}
138
+ fields += join_table.columns.keys.map{|field| join_table.full_name+'."'+field.to_s+'"'}
139
+
140
+ sql = "SELECT " + fields.join( "," ) + " FROM "
141
+ sql += full_name
142
+ sql += " LEFT OUTER JOIN " + join_table.full_name + " USING(#{join_columns(join_table).join(',')})"
143
+ sql += compile_where( conditions )
144
+ sql += compile_order( options[:order] ) if options[:order]
145
+ sql += compile_limit( options[:limit] ) if options[:limit]
146
+ sql += compile_offset( options[:offset] ) if options[:offset]
147
+ data = []
148
+ database.execute( sql ).each do | row |
149
+ new_row = const_get(:Row).new( row[0, columns.keys.length] )
150
+ join_row = join_table.const_get(:Row).new( row[columns.keys.length,join_table.columns.keys.length] )
151
+ new_row.instance_variable_set(:@join, join_row)
152
+ new_row.send( :instance_eval ) { class << self; self; end }.send(:define_method, join_table.table_name ) do join_row end
153
+ data << new_row
154
+ end
155
+ data
156
+ end
157
+
158
+ def join_columns( join_table )
159
+ join_table.primary_keys.select{|f| columns.key?(f)}
160
+ end
161
+
120
162
  ## constructor for a record in this table accepts a hash with presets for the fields of the record
121
163
  def new( fields = {} )
122
164
  initialize_table unless class_variables.member?('@@initialized')
data/test/test.sql ADDED
@@ -0,0 +1,68 @@
1
+
2
+ CREATE TABLE conference (
3
+ conference_id SERIAL,
4
+ acronym TEXT NOT NULL UNIQUE,
5
+ title TEXT NOT NULL,
6
+ subtitle TEXT,
7
+ description TEXT,
8
+ start_date DATE NOT NULL,
9
+ timeslot_duration INTERVAL NOT NULL DEFAULT '0:30:00',
10
+ default_timeslots INTEGER NOT NULL DEFAULT 1,
11
+ max_timeslots_per_event INTEGER NOT NULL DEFAULT 10,
12
+ day_change TIME WITHOUT TIME ZONE NOT NULL DEFAULT '0:00:00',
13
+ feedback_enabled BOOL NOT NULL DEFAULT FALSE,
14
+ submission_enabled BOOL NOT NULL DEFAULT FALSE,
15
+ visitor_enabled BOOL NOT NULL DEFAULT FALSE,
16
+ reconfirmation_enabled BOOL NOT NULL DEFAULT FALSE,
17
+ PRIMARY KEY(conference_id)
18
+ );
19
+
20
+ CREATE TABLE person (
21
+ person_id SERIAL,
22
+ first_name TEXT,
23
+ last_name TEXT,
24
+ nick_name TEXT,
25
+ PRIMARY KEY(person_id)
26
+ );
27
+
28
+ INSERT INTO person(nick_name) VALUES ('blossom');
29
+ INSERT INTO person(nick_name) VALUES ('buttercup');
30
+ INSERT INTO person(nick_name) VALUES ('bubbles');
31
+ INSERT INTO person(nick_name) VALUES ('mojojojo');
32
+
33
+ CREATE TABLE event (
34
+ event_id SERIAL,
35
+ title TEXT,
36
+ subtitle TEXT,
37
+ PRIMARY KEY(event_id)
38
+ );
39
+
40
+ CREATE TABLE event_person (
41
+ event_id INTEGER,
42
+ person_id INTEGER,
43
+ description TEXT,
44
+ FOREIGN KEY(event_id) REFERENCES event(event_id),
45
+ FOREIGN KEY(person_id) REFERENCES person(person_id),
46
+ PRIMARY KEY(event_id, person_id, description)
47
+ );
48
+
49
+ CREATE TABLE test_bigint( id SERIAL, data BIGINT, PRIMARY KEY(id));
50
+ CREATE TABLE test_boolean( id SERIAL, data BOOLEAN, PRIMARY KEY(id));
51
+ CREATE TABLE test_bytea( id SERIAL, data BYTEA, PRIMARY KEY(id));
52
+ CREATE TABLE test_character( id SERIAL, data CHARACTER(1000), PRIMARY KEY(id));
53
+ CREATE TABLE test_character_varying( id SERIAL, data VARCHAR(1000), PRIMARY KEY(id));
54
+ CREATE TABLE test_date( id SERIAL, data DATE, PRIMARY KEY(id));
55
+ CREATE TABLE test_inet( id SERIAL, data INET, PRIMARY KEY(id));
56
+ CREATE TABLE test_integer( id SERIAL, data INTEGER, PRIMARY KEY(id));
57
+ CREATE TABLE test_interval( id SERIAL, data INTERVAL, PRIMARY KEY(id));
58
+ CREATE TABLE test_nodefault ( id INTEGER, data TEXT, PRIMARY KEY(id));
59
+ CREATE TABLE test_numeric( id SERIAL, data NUMERIC, PRIMARY KEY(id));
60
+ CREATE TABLE test_real( id SERIAL, data REAL, PRIMARY KEY(id));
61
+ CREATE TABLE test_smallint( id SERIAL, data SMALLINT, PRIMARY KEY(id));
62
+ CREATE TABLE test_text( id SERIAL, data TEXT, PRIMARY KEY(id));
63
+ CREATE TABLE test_time_with_time_zone( id SERIAL, data TIME WITH TIME ZONE, PRIMARY KEY(id));
64
+ CREATE TABLE test_time_without_time_zone( id SERIAL, data TIME WITHOUT TIME ZONE, PRIMARY KEY(id));
65
+ CREATE TABLE test_timestamp_with_time_zone( id SERIAL, data TIMESTAMP WITH TIME ZONE, PRIMARY KEY(id));
66
+ CREATE TABLE test_timestamp_without_time_zone( id SERIAL, data TIMESTAMP WITHOUT TIME ZONE, PRIMARY KEY(id));
67
+
68
+
data/test/test_base.rb CHANGED
@@ -8,9 +8,9 @@ class TestBase < Test::Unit::TestCase
8
8
  assert_equal( " WHERE person_id = '1'" , t.compile_where( :person_id => '1' ) )
9
9
  assert_equal( " WHERE person_id IN ('1')" , t.compile_where( :person_id => ['1'] ) )
10
10
  assert_equal( " WHERE person_id IN ('1','2')" , t.compile_where( :person_id => ['1',2] ) )
11
- assert_equal( " WHERE first_name = '1'" , t.compile_where( :first_name => '1' ) )
12
- assert_equal( " WHERE first_name = 'chu''nky'" , t.compile_where( :first_name => "chu'nky" ) )
13
- assert_equal( " WHERE first_name IN ('chu''nky','bac''n')" , t.compile_where( :first_name => ["chu'nky","bac'n"] ) )
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"] ) )
14
14
  end
15
15
 
16
16
  end
@@ -0,0 +1,25 @@
1
+
2
+ CREATE OR REPLACE FUNCTION test_parameter_sql( param1 INTEGER ) RETURNS INTEGER AS $$
3
+ SELECT $1;
4
+ $$ LANGUAGE sql;
5
+
6
+ CREATE OR REPLACE FUNCTION test_parameter_plpgsql( param1 INTEGER, param2 TEXT ) RETURNS INTEGER AS $$
7
+ DECLARE
8
+ BEGIN
9
+ RETURN param1;
10
+ END;
11
+ $$ LANGUAGE plpgsql;
12
+
13
+ CREATE OR REPLACE FUNCTION test_set_returning( person_id INTEGER ) RETURNS SETOF person AS $$
14
+ DECLARE
15
+ result RECORD;
16
+ BEGIN
17
+ FOR result IN
18
+ SELECT person.* FROM person WHERE person.person_id <> person_id
19
+ LOOP
20
+ RETURN NEXT result;
21
+ END LOOP;
22
+ RETURN;
23
+ END;
24
+ $$ LANGUAGE plpgsql;
25
+
@@ -17,10 +17,8 @@ class TestInformationSchema < Test::Unit::TestCase
17
17
 
18
18
  # test for working information_schema.table_constraints class
19
19
  def test_information_schema_table_constraints
20
- a = Momomoto::Information_schema::Table_constraints.select(:table_schema => 'pg_catalog', :table_name => 'pg_class')
21
- assert_equal( 0, a.length )
22
20
  a = Momomoto::Information_schema::Table_constraints.select(:table_schema => 'public')
23
- assert_operator( 0, :<, a.length )
21
+ assert( a.length > 0 )
24
22
  end
25
23
 
26
24
  end
data/test/test_table.rb CHANGED
@@ -2,18 +2,32 @@
2
2
  class TestTable < Test::Unit::TestCase
3
3
 
4
4
  class Person < Momomoto::Table
5
- def self.person_id=( row , newvalue )
6
- newvalue
7
- end
8
-
9
- def self.nick_name=( row, newvalue )
10
- row.first_name + newvalue
5
+ module Methods
6
+ def nick_name=( newvalue )
7
+ set_column(:nick_name, get_column(:first_name) + newvalue )
8
+ end
11
9
  end
12
10
  end
13
11
 
14
12
  class Conference < Momomoto::Table
15
13
  end
16
14
 
15
+ def test_default_order_getter
16
+ c1 = Class.new( Momomoto::Table )
17
+ c1.default_order = [:a,:b]
18
+ assert_equal( [:a,:b], c1.default_order )
19
+ end
20
+
21
+ def test_default_order_setter
22
+ c1 = Class.new( Momomoto::Table )
23
+ c1.default_order = :a
24
+ assert_equal( :a, c1.default_order )
25
+ c1.default_order = :b
26
+ assert_equal( :b, c1.default_order )
27
+ c1.default_order( :c )
28
+ assert_equal( :c, c1.default_order )
29
+ end
30
+
17
31
  def test_columns_getter
18
32
  c1 = Class.new( Momomoto::Table )
19
33
  c1.columns = {:a=>Momomoto::Datatype::Text}
@@ -109,6 +123,14 @@ class TestTable < Test::Unit::TestCase
109
123
  assert_equal( true, sven.new_record? )
110
124
  end
111
125
 
126
+ def test_offset
127
+ p = Person.select( {}, {:limit => 1, :order => :person_id,:offset=>2})
128
+ assert_operator( 2, :<, p[0].person_id )
129
+ assert_raise( Momomoto::Error ) do
130
+ Person.select( {}, {:limit => 1, :order => :person_id,:offset=>'bacon'})
131
+ end
132
+ end
133
+
112
134
  def test_select
113
135
  r = Person.select( nil, {:limit => 3})
114
136
  assert_equal( 3, r.length )
metadata CHANGED
@@ -3,7 +3,7 @@ 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.0
6
+ version: 0.1.1
7
7
  date: 2007-08-13 00:00:00 +02:00
8
8
  summary: Momomoto is an object relational mapper for PostgreSQL.
9
9
  require_paths:
@@ -90,6 +90,8 @@ files:
90
90
  - test/test_datatype.rb
91
91
  - test/test_bigint.rb
92
92
  - test/test_date.rb
93
+ - test/test_functions.sql
94
+ - test/test.sql
93
95
  - LICENSE
94
96
  - Rakefile
95
97
  test_files: []
@@ -103,7 +105,7 @@ executables: []
103
105
  extensions: []
104
106
 
105
107
  requirements:
106
- - PostgreSQL 8.1.4 or greater
108
+ - PostgreSQL 8.1.x or greater
107
109
  dependencies:
108
110
  - !ruby/object:Gem::Dependency
109
111
  name: ruby-postgres