momomoto 0.1.13 → 0.1.14
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/Rakefile +15 -0
- data/lib/momomoto/base.rb +77 -12
- data/lib/momomoto/database.rb +16 -4
- data/lib/momomoto/datatype/base.rb +49 -8
- data/lib/momomoto/datatype/bigint.rb +2 -1
- data/lib/momomoto/datatype/boolean.rb +9 -0
- data/lib/momomoto/datatype/bytea.rb +4 -0
- data/lib/momomoto/datatype/character.rb +2 -0
- data/lib/momomoto/datatype/character_varying.rb +2 -0
- data/lib/momomoto/datatype/date.rb +5 -0
- data/lib/momomoto/datatype/inet.rb +4 -0
- data/lib/momomoto/datatype/integer.rb +5 -0
- data/lib/momomoto/datatype/interval.rb +14 -1
- data/lib/momomoto/datatype/numeric.rb +5 -0
- data/lib/momomoto/datatype/real.rb +2 -0
- data/lib/momomoto/datatype/smallint.rb +2 -0
- data/lib/momomoto/datatype/text.rb +14 -0
- data/lib/momomoto/datatype/time_with_time_zone.rb +3 -1
- data/lib/momomoto/datatype/time_without_time_zone.rb +10 -0
- data/lib/momomoto/datatype/timestamp_with_time_zone.rb +2 -0
- data/lib/momomoto/datatype/timestamp_without_time_zone.rb +5 -0
- data/lib/momomoto/information_schema/columns.rb +8 -0
- data/lib/momomoto/information_schema/fetch_procedure_columns.rb +2 -0
- data/lib/momomoto/information_schema/fetch_procedure_parameters.rb +2 -0
- data/lib/momomoto/information_schema/key_column_usage.rb +5 -0
- data/lib/momomoto/information_schema/routines.rb +4 -0
- data/lib/momomoto/information_schema/table_constraints.rb +5 -0
- data/lib/momomoto/order.rb +61 -0
- data/lib/momomoto/procedure.rb +14 -14
- data/lib/momomoto/row.rb +41 -4
- data/lib/momomoto/table.rb +106 -20
- data/lib/timeinterval.rb +90 -16
- data/test/test_datatype.rb +1 -1
- metadata +2 -2
data/Rakefile
CHANGED
@@ -28,11 +28,26 @@ begin
|
|
28
28
|
rescue LoadError
|
29
29
|
end
|
30
30
|
|
31
|
+
desc "check documentation coverage"
|
32
|
+
task :dcov do
|
33
|
+
sh "find lib -name '*.rb' | xargs dcov"
|
34
|
+
end
|
35
|
+
|
31
36
|
desc "create documentation for ri"
|
32
37
|
task :doc do
|
33
38
|
sh "rdoc -r lib"
|
34
39
|
end
|
35
40
|
|
41
|
+
desc "create html documentation"
|
42
|
+
task :html do
|
43
|
+
sh "rdoc --template jamis --main Momomoto::Table --inline-source --force-update --webcvs 'http://trac.c3d2.de/momomoto/browser/trunk/%s' lib"
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "update html documentation on momomoto.rubyforge.org"
|
47
|
+
task :update_html do
|
48
|
+
sh "scp -r doc rubyforge:/var/www/gforge-projects/momomoto"
|
49
|
+
end
|
50
|
+
|
36
51
|
desc "run benchmark"
|
37
52
|
task( :bench ) do | t |
|
38
53
|
sh "ruby benchmark.rb"
|
data/lib/momomoto/base.rb
CHANGED
@@ -4,30 +4,70 @@ module Momomoto
|
|
4
4
|
|
5
5
|
class << self
|
6
6
|
|
7
|
+
# Getter and setter for debugging.
|
8
|
+
# If +debug+ evaluates to +true+ then all SQL queries to the database
|
9
|
+
# are printed to STDOUT.
|
7
10
|
attr_accessor :debug
|
8
11
|
|
12
|
+
# Returns an instance of Order::Lower where +args+ is either a single
|
13
|
+
# or array of +Symbol+ representing columns.
|
14
|
+
#
|
15
|
+
# Eases the use of class Order::Lower. You can use it whenever selecting
|
16
|
+
# rows or in #default_order.
|
17
|
+
#
|
18
|
+
# order_lower = Momomoto.lower( :person )
|
19
|
+
# => #<Momomoto::Order::Lower:0x5184131c @fields=[:person]>
|
20
|
+
# Table.select( {}, {:order => order} )
|
21
|
+
# => returns Table's rows ordered case-insensitively by column person
|
9
22
|
def lower( *args )
|
10
23
|
Momomoto::Order::Lower.new( *args )
|
11
24
|
end
|
12
25
|
|
26
|
+
# Returns an instance of Order::Asc where +args+ is either a single
|
27
|
+
# or array of +Symbol+ representing columns.
|
28
|
+
#
|
29
|
+
# Eases the use of class Order::Asc. You can use it whenever selecting
|
30
|
+
# rows or in #default_order.
|
31
|
+
#
|
32
|
+
# order_lower = Momomoto.asc( :person )
|
33
|
+
# => #<Momomoto::Order::Asc:0x5184131c @fields=[:person]>
|
34
|
+
# Table.select( {}, {:order => order} )
|
35
|
+
# => returns Table's rows ordered asc by column person
|
13
36
|
def asc( *args )
|
14
37
|
Momomoto::Order::Asc.new( *args )
|
15
38
|
end
|
16
39
|
|
40
|
+
# Returns an instance of Order::Asc where +args+ is either a single
|
41
|
+
# or array of +Symbol+ representing columns.
|
42
|
+
#
|
43
|
+
# Eases the use of class Order::Desc. You can use it whenever selecting
|
44
|
+
# rows or in #default_order.
|
45
|
+
#
|
46
|
+
# order_lower = Momomoto.lower( :person )
|
47
|
+
# => #<Momomoto::Order::Desc:0x5184131c @fields=[:person]>
|
48
|
+
# Table.select( {}, {:order => order} )
|
49
|
+
# => returns Table's rows ordered desc by column person
|
17
50
|
def desc( *args )
|
18
51
|
Momomoto::Order::Desc.new( *args )
|
19
52
|
end
|
20
53
|
|
21
54
|
end
|
22
55
|
|
23
|
-
|
56
|
+
# Base exception for all exceptions thrown by Momomoto
|
24
57
|
class Error < StandardError; end
|
25
58
|
|
26
|
-
#
|
59
|
+
# Thrown when datatype conversion fails and if a +block+ given to
|
60
|
+
# Table#select_or_new does not act on all primary keys.
|
27
61
|
class ConversionError < Error; end
|
28
|
-
class CriticalError < Error; end
|
29
62
|
|
63
|
+
# Thrown when a critical error occurs.
|
64
|
+
class CriticalError < Error; end
|
65
|
+
|
66
|
+
# Thrown when multiple values are found in Table#select_or_new or
|
67
|
+
# Table#select_single.
|
30
68
|
class Too_many_records < Error; end
|
69
|
+
|
70
|
+
# Thrown when no row was found in Table#select_single.
|
31
71
|
class Nothing_found < Error; end
|
32
72
|
|
33
73
|
|
@@ -36,9 +76,13 @@ module Momomoto
|
|
36
76
|
|
37
77
|
class << self
|
38
78
|
|
79
|
+
# Getter for logical operator. This is used in #compile_where.
|
80
|
+
# See Table#select for usage of logical operators.
|
39
81
|
attr_reader :logical_operator
|
40
82
|
|
41
|
-
#
|
83
|
+
# Set the default logical operator for constraints. AND and OR are
|
84
|
+
# supported.
|
85
|
+
# See Table#select for usage of logical operators.
|
42
86
|
def logical_operator=( value )
|
43
87
|
@logical_operator = case value
|
44
88
|
when /and/i then "AND"
|
@@ -47,12 +91,14 @@ module Momomoto
|
|
47
91
|
end
|
48
92
|
end
|
49
93
|
|
50
|
-
#
|
94
|
+
# Set the schema name of the table this class operates on.
|
51
95
|
def schema_name=( schema_name )
|
52
96
|
@schema_name = schema_name
|
53
97
|
end
|
54
98
|
|
55
|
-
#
|
99
|
+
# Get the schema name of the table this class operates on. Invokes
|
100
|
+
# #schema_name= if +schema_name+ is given as parameter. Returns
|
101
|
+
# +@schema_name+
|
56
102
|
def schema_name( schema_name = nil )
|
57
103
|
return self.schema_name=( schema_name ) if schema_name
|
58
104
|
if not instance_variable_defined?( :@schema_name )
|
@@ -63,9 +109,10 @@ module Momomoto
|
|
63
109
|
|
64
110
|
protected
|
65
111
|
|
112
|
+
# Getter and setter used for marking tables as initialized.
|
66
113
|
attr_accessor :initialized
|
67
114
|
|
68
|
-
#
|
115
|
+
# Guesses the schema name of the table this class works on.
|
69
116
|
def construct_schema_name( classname )
|
70
117
|
# Uncomment these lines to derive the schema from the enclosing namespace of the class
|
71
118
|
#schema = classname.split('::')[-2]
|
@@ -73,7 +120,7 @@ module Momomoto
|
|
73
120
|
'public'
|
74
121
|
end
|
75
122
|
|
76
|
-
#
|
123
|
+
# Get the database connection.
|
77
124
|
def database # :nodoc:
|
78
125
|
Momomoto::Database.instance
|
79
126
|
end
|
@@ -85,6 +132,7 @@ module Momomoto
|
|
85
132
|
where.empty? ? '' : " WHERE #{where}"
|
86
133
|
end
|
87
134
|
|
135
|
+
# compiles subexpressions of the where-clause
|
88
136
|
def compile_expression( conditions, operator )
|
89
137
|
where = []
|
90
138
|
case conditions
|
@@ -110,7 +158,10 @@ module Momomoto
|
|
110
158
|
end
|
111
159
|
|
112
160
|
|
113
|
-
#
|
161
|
+
# Compiles the sql statement defining the limit
|
162
|
+
#
|
163
|
+
# #selects five feeds
|
164
|
+
# five_feeds = Feeds.select( {},{:limit => 5} )
|
114
165
|
def compile_limit( limit )
|
115
166
|
" LIMIT #{Integer(limit)}"
|
116
167
|
rescue => e
|
@@ -118,6 +169,9 @@ module Momomoto
|
|
118
169
|
end
|
119
170
|
|
120
171
|
# compiles the sql statement defining the offset
|
172
|
+
#
|
173
|
+
# #selects five feeds ommitting the first 23 rows
|
174
|
+
# five_feeds = Feeds.select( {}, {:offset => 23, :limit => 5} )
|
121
175
|
def compile_offset( offset )
|
122
176
|
" OFFSET #{Integer(offset)}"
|
123
177
|
rescue => e
|
@@ -140,7 +194,18 @@ module Momomoto
|
|
140
194
|
" ORDER BY #{order.join(',')}"
|
141
195
|
end
|
142
196
|
|
143
|
-
#
|
197
|
+
# Constructs the Row class for the given table or procedure +table+.
|
198
|
+
# If +columns+ is given as parameter to this method all setter and getter
|
199
|
+
# for the fields which are not included in columns will be removed.
|
200
|
+
#
|
201
|
+
# See Table#select for how this can be useful when only some columns are
|
202
|
+
# needed.
|
203
|
+
#
|
204
|
+
# module Methods can be used to modify setter and getter methods for columns.
|
205
|
+
# Methods is included to the row class after StandardMethods which holds all
|
206
|
+
# default accessors. That's why you can define your own accessors in Methods.
|
207
|
+
#
|
208
|
+
# See Row#set_column and Row#get_column for more information on this.
|
144
209
|
def initialize_row( row, table, columns = table.columns )
|
145
210
|
|
146
211
|
const_set( :Methods, Module.new ) if not const_defined?( :Methods )
|
@@ -173,8 +238,8 @@ module Momomoto
|
|
173
238
|
|
174
239
|
end
|
175
240
|
|
176
|
-
#
|
177
|
-
# is later included in the Row class
|
241
|
+
# Defines row setter and getter in the module StandardMethods which
|
242
|
+
# is later included in the Row class.
|
178
243
|
def define_row_accessors( method_module, table, columns )
|
179
244
|
columns.each_with_index do | ( field_name, data_type ), index |
|
180
245
|
method_module.instance_eval do
|
data/lib/momomoto/database.rb
CHANGED
@@ -36,6 +36,7 @@ module Momomoto
|
|
36
36
|
@config = config
|
37
37
|
end
|
38
38
|
|
39
|
+
# Eases the use of #config.
|
39
40
|
def self.config( conf )
|
40
41
|
instance.config( conf )
|
41
42
|
end
|
@@ -45,6 +46,10 @@ module Momomoto
|
|
45
46
|
@connection = nil
|
46
47
|
end
|
47
48
|
|
49
|
+
# Connects to database
|
50
|
+
# Momomoto::Database.config( :database=>:test, :username => 'test' )
|
51
|
+
# Momomoto::Database.connect
|
52
|
+
# # configure and connect
|
48
53
|
def connect
|
49
54
|
@connection.close if @connection
|
50
55
|
@transaction_active = false
|
@@ -56,6 +61,11 @@ module Momomoto
|
|
56
61
|
raise CriticalError, "Connection to database failed: #{e}"
|
57
62
|
end
|
58
63
|
|
64
|
+
# Eases the use of #connect.
|
65
|
+
#
|
66
|
+
# Momomoto::Database.config( :database=>:test, :username => 'test' )
|
67
|
+
# Momomoto::Database.connect
|
68
|
+
# # configure and connect
|
59
69
|
def self.connect
|
60
70
|
instance.connect
|
61
71
|
end
|
@@ -115,7 +125,7 @@ module Momomoto
|
|
115
125
|
columns
|
116
126
|
end
|
117
127
|
|
118
|
-
# fetches
|
128
|
+
# fetches parameters of a stored procedure
|
119
129
|
def fetch_procedure_parameters( procedure_name, schema_name = nil ) # :nodoc:
|
120
130
|
p = []
|
121
131
|
conditions = { :procedure_name => procedure_name }
|
@@ -125,14 +135,14 @@ module Momomoto
|
|
125
135
|
end
|
126
136
|
# mark parameters of strict procedures as not null
|
127
137
|
if Information_schema::Routines.select_single(:routine_name=>procedure_name).is_null_call == 'YES'
|
128
|
-
p.each do | param |
|
129
|
-
param[param.keys.first].instance_variable_set(:@not_null,true)
|
138
|
+
p.each do | param |
|
139
|
+
param[param.keys.first].instance_variable_set(:@not_null,true)
|
130
140
|
end
|
131
141
|
end
|
132
142
|
p
|
133
143
|
end
|
134
144
|
|
135
|
-
# fetches the
|
145
|
+
# fetches the result set columns of a stored procedure
|
136
146
|
def fetch_procedure_columns( procedure_name, schema_name = nil ) # :nodoc:
|
137
147
|
c = {}
|
138
148
|
conditions = { :procedure_name => procedure_name }
|
@@ -177,10 +187,12 @@ module Momomoto
|
|
177
187
|
@transaction_active = false
|
178
188
|
end
|
179
189
|
|
190
|
+
# escapes the given string +input+
|
180
191
|
def self.escape_string( input )
|
181
192
|
PGconn.escape( input )
|
182
193
|
end
|
183
194
|
|
195
|
+
# escapes the given binary data +input+
|
184
196
|
def self.escape_bytea( input )
|
185
197
|
PGconn.escape_bytea( input )
|
186
198
|
end
|
@@ -1,39 +1,61 @@
|
|
1
1
|
|
2
2
|
module Momomoto
|
3
3
|
|
4
|
+
# This module encapsulates all supported data types, i.e.:
|
5
|
+
# Numeric, Integer, Bigint, Smallint, Real,
|
6
|
+
# Timestamp_with_time_zone, Timestamp_without_time_zone,
|
7
|
+
# Time_with_time_zone, Time_without_time_zone, Date, Interval,
|
8
|
+
# Character, Character_varying, Bytea, Text, Inet and Boolean.
|
9
|
+
#
|
10
|
+
# Refer to http://www.postgresql.org/docs/8.2/static/datatype.html
|
11
|
+
# for more information on the specific data types.
|
4
12
|
module Datatype
|
5
|
-
|
13
|
+
|
14
|
+
# Every data type class (see #Datatype) is derived from this class.
|
6
15
|
class Base
|
7
|
-
|
8
|
-
# returns
|
16
|
+
|
17
|
+
# Gets the default value for this column or returns nil if none
|
18
|
+
# exists.
|
9
19
|
def default
|
10
20
|
@default
|
11
21
|
end
|
12
22
|
|
13
|
-
#
|
23
|
+
# Returns true if this column can be NULL otherwise false.
|
14
24
|
def not_null?
|
15
25
|
@not_null
|
16
26
|
end
|
17
27
|
|
28
|
+
# Creates a new instance of the special data type, setting +not_null+
|
29
|
+
# and +default+ according to the values from Information Schema.
|
18
30
|
def initialize( row = nil )
|
19
|
-
@not_null = row.respond_to?(:is_nullable) && row.is_nullable == "NO"
|
20
|
-
@default = row.respond_to?(
|
31
|
+
@not_null = row.respond_to?(:is_nullable) && row.is_nullable == "NO" ? true : false
|
32
|
+
@default = row.respond_to?(:column_default) ? row.column_default : nil
|
21
33
|
end
|
22
34
|
|
23
|
-
#
|
35
|
+
# Values are filtered by this function when being set. See the
|
36
|
+
# method in the appropriate derived data type class for allowed
|
37
|
+
# values.
|
24
38
|
def filter_set( value ) # :nodoc:
|
25
39
|
value
|
26
40
|
end
|
27
41
|
|
42
|
+
# Compares two values and return true if equal or false otherwise.
|
43
|
+
# It is used to check if a row field has been changed so that only
|
44
|
+
# changed fields are written to database.
|
28
45
|
def equal( a, b )
|
29
46
|
a == b
|
30
47
|
end
|
31
48
|
|
49
|
+
# Escapes +input+ to be saved in database.
|
50
|
+
# If +input+ equals nil, NULL is returned, otherwise Database#escape_string
|
51
|
+
# is called.
|
52
|
+
# This method is overwritten to get data type-specific escaping rules.
|
32
53
|
def escape( input )
|
33
54
|
input.nil? ? "NULL" : "'" + Database.escape_string( input.to_s ) + "'"
|
34
55
|
end
|
35
56
|
|
36
|
-
#
|
57
|
+
# This method is used when compiling the where clause. No need
|
58
|
+
# for direct use.
|
37
59
|
def compile_rule( field_name, value ) # :nodoc:
|
38
60
|
case value
|
39
61
|
when nil then
|
@@ -66,6 +88,25 @@ module Momomoto
|
|
66
88
|
end
|
67
89
|
end
|
68
90
|
|
91
|
+
# These are operators supported by all data types. In your select
|
92
|
+
# statement use something like:
|
93
|
+
#
|
94
|
+
# one_day_ago = Time.now - (3600*24)
|
95
|
+
# Feeds.select( :date => {:ge => one_day_ago.to_s} )
|
96
|
+
#
|
97
|
+
# This will select all rows from Feeds that are newer than 24 hours.
|
98
|
+
# Same with Momomoto's TimeInterval:
|
99
|
+
#
|
100
|
+
# one_day_ago = Time.now + TimeInterval.new({:hour => -24})
|
101
|
+
# Feeds.select( :date => {:ge => one_day_ago.to_s} )
|
102
|
+
#
|
103
|
+
# In case of data type Text also +:like+ and +:ilike+ are supported.
|
104
|
+
# For +:like+ and +:ilike+ operators you may use _ as placeholder for
|
105
|
+
# a single character or % as placeholder for multiple characters.
|
106
|
+
#
|
107
|
+
# # Selects all posts having "surveillance" in their content field
|
108
|
+
# # while ignoring case.
|
109
|
+
# Posts.select( :content => {:ilike => 'surveillance'} )
|
69
110
|
def self.operator_sign( op )
|
70
111
|
case op
|
71
112
|
when :le then '<='
|
@@ -1,7 +1,14 @@
|
|
1
1
|
module Momomoto
|
2
2
|
module Datatype
|
3
|
+
|
4
|
+
# This class represents values of boolean type.
|
3
5
|
class Boolean < Base
|
4
6
|
|
7
|
+
# Values are filtered by this method when being set.
|
8
|
+
# Returns true or false.
|
9
|
+
# If the given +value+ cannot be converted to true or false
|
10
|
+
# and NULL is not allowed, than again return false. Otherwise
|
11
|
+
# return +nil+.
|
5
12
|
def filter_set( value )
|
6
13
|
case value
|
7
14
|
when true, 1, 't', 'true', 'on' then true
|
@@ -10,6 +17,8 @@ module Momomoto
|
|
10
17
|
end
|
11
18
|
end
|
12
19
|
|
20
|
+
# Converts the given +input+ to true or false if possible.
|
21
|
+
# Otherwise returns NULL.
|
13
22
|
def escape( input )
|
14
23
|
case input
|
15
24
|
when true, 1, 't', 'true', 'on' then "'t'"
|
@@ -1,8 +1,12 @@
|
|
1
1
|
|
2
2
|
module Momomoto
|
3
3
|
module Datatype
|
4
|
+
|
5
|
+
# This class is used for Binary Data (Byte Array).
|
4
6
|
class Bytea < Base
|
5
7
|
|
8
|
+
# Escapes +input+ using Database#escape_bytea or returns NULL if
|
9
|
+
# +input+ is nil.
|
6
10
|
def escape( input )
|
7
11
|
input.nil? ? "NULL" : "E'" + Database.escape_bytea( input ) + "'"
|
8
12
|
end
|
@@ -3,8 +3,13 @@ require 'date'
|
|
3
3
|
|
4
4
|
module Momomoto
|
5
5
|
module Datatype
|
6
|
+
|
7
|
+
# Represents the data type Date.
|
6
8
|
class Date < Base
|
7
9
|
|
10
|
+
# Values are filtered by this function when being set.
|
11
|
+
# Returns ruby's Date or tries to build a Date from a String.
|
12
|
+
# Raises ConversionError if the given +value+ cannot be parsed.
|
8
13
|
def filter_set( value )
|
9
14
|
case value
|
10
15
|
when nil,'' then nil
|