momomoto 0.1.0 → 0.1.1

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