dddbl 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/LICENSE +24 -0
- data/README.rdoc +188 -0
- data/Rakefile +2 -0
- data/dddbl.gemspec +21 -0
- data/example/dbdef.def +9 -0
- data/example/demo.rb +59 -0
- data/example/test.sql +12 -0
- data/lib/dddbl.rb +152 -0
- data/lib/dddbl/config.rb +119 -0
- data/lib/dddbl/pool.rb +108 -0
- data/lib/dddbl/results.rb +5 -0
- data/lib/dddbl/version.rb +3 -0
- metadata +88 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2011, André Gawron
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
- Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
- Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
- The names of its contributors may be used to endorse or promote products
|
13
|
+
derived from this software without specific prior written permission.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
18
|
+
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
19
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
20
|
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
22
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
23
|
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
24
|
+
OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
= DDDBL - Definition Driven Database Layer
|
2
|
+
|
3
|
+
== What?
|
4
|
+
|
5
|
+
DDDBL is a library to simplify the use of (different) databases and splits up
|
6
|
+
application's and database's (e.g: SQL queries) code through an easy
|
7
|
+
and unified API. The SQL is defined in files outside of the application's source
|
8
|
+
to easiliy port the SQL to diffrent applications - or even programming languages.
|
9
|
+
Refering to the written queries using an alias makes it even more clearer and
|
10
|
+
better to understand for newcomers, who then don't have to waste time figuring out
|
11
|
+
what the query does.
|
12
|
+
|
13
|
+
== Why?
|
14
|
+
|
15
|
+
Many applications interacting with databases have the sql code directly written
|
16
|
+
in the its source. Reading, reviewing and understanding that
|
17
|
+
unfimiliar code can be hard, even more for non sql-pros. Most of the time -
|
18
|
+
to optimize the queries and the database performance in general - DBAs are hired
|
19
|
+
who are probably not familiar with the programming language used. So they have
|
20
|
+
to dig into the code and try to optimize it without breaking anything.
|
21
|
+
|
22
|
+
== Supported databases
|
23
|
+
|
24
|
+
Common RDMS like MySQL, PostgreSQL and SQLite are supported. The DDDBL uses another
|
25
|
+
gem called RDBI which simplifies the handling of several databases. For a full
|
26
|
+
list please of supported drivers please check the RDBI's github page
|
27
|
+
(https://github.com/RDBI/).
|
28
|
+
|
29
|
+
== It's a port, isn't it?
|
30
|
+
|
31
|
+
Yes. The DDDBL was orginally written in PHP and is a creation of Torsten Zühlsdorff.
|
32
|
+
For more information on the PHP version, go to http://www.dddbl.de (German only).
|
33
|
+
The ruby version will also be featured there in the near future.
|
34
|
+
|
35
|
+
=== Any differences?
|
36
|
+
|
37
|
+
Yes. As of writing, the ruby port does not include every functionality the
|
38
|
+
PHP version offers. For example:
|
39
|
+
|
40
|
+
* not as much freedom regarding manipulating query-parameter pre-execution besides
|
41
|
+
casting the binded parameters to any value extendable through TypeLib
|
42
|
+
* caching of prepared statements
|
43
|
+
|
44
|
+
== Dependecies
|
45
|
+
|
46
|
+
The database handling and querying is handled by the RDBI library. For more
|
47
|
+
information please see their README (https://github.com/RDBI/rdbi/blob/master/README.txt)
|
48
|
+
or visit the project's page on https://github.com/RDBI/ .
|
49
|
+
|
50
|
+
== Getting started
|
51
|
+
|
52
|
+
=== Database definitions
|
53
|
+
|
54
|
+
Every database the DDDBL should connect to has to be defined in a configuration
|
55
|
+
file using the ini-format. Example:
|
56
|
+
|
57
|
+
[TEST-DB]
|
58
|
+
HOST = localhost
|
59
|
+
DBNAME = test
|
60
|
+
TYPE = PostgreSQL
|
61
|
+
USER = root
|
62
|
+
PASS =
|
63
|
+
DEFAULT = true
|
64
|
+
|
65
|
+
* [TEST-DB]: alias of the database connection. It's used if the DDDBL shall
|
66
|
+
execute the query to another database
|
67
|
+
* HOST: host of the database
|
68
|
+
* DBNAME: database to select
|
69
|
+
* TYPE: driver to use
|
70
|
+
* USER: user who will be used to connect to the database
|
71
|
+
* PASS: user's password
|
72
|
+
* DEFAULT: normally, the DDDBL needs to know which database shall be used
|
73
|
+
to execute the query. If the DEFAULT flag is set to true, this connection
|
74
|
+
will be used at first - no explit connection selection needed. DEFAULT is optional.
|
75
|
+
|
76
|
+
Now the file has to be added to the database pool:
|
77
|
+
|
78
|
+
DDDBL::Pool::DB << DDDBL::Config.parse_dbs('/path/to/db/definitions.def')
|
79
|
+
|
80
|
+
The database has to be added before any queries. Adding a file with database
|
81
|
+
definitions results in establishing a connection to every defined database.
|
82
|
+
|
83
|
+
==== Selecting (another) database
|
84
|
+
|
85
|
+
If only one connection is used, the following is just "good-to-know".
|
86
|
+
To select (another) database but the default is done by calling
|
87
|
+
|
88
|
+
DDDBL::select_db('TEST-DB')
|
89
|
+
|
90
|
+
Now every query executed after #select_db will be using the database connection
|
91
|
+
defined through the alias 'TEST-DB'.
|
92
|
+
|
93
|
+
=== Query definitions
|
94
|
+
|
95
|
+
The query definition file also uses the ini-format and has to be configurated
|
96
|
+
seperately from the application (that's what the DDDBL is all about, isn't it?)
|
97
|
+
|
98
|
+
As for now, the query definitions of the PHP and ruby versions are interchangable:
|
99
|
+
|
100
|
+
[QUERY-ALIAS]
|
101
|
+
QUERY = "SELECT * FROM table"
|
102
|
+
HANDLER = MULTI
|
103
|
+
|
104
|
+
[QUERY-MULTILINE]
|
105
|
+
QUERY = "SELECT *
|
106
|
+
FROM table
|
107
|
+
WHERE foo = ?"
|
108
|
+
HANDLER = MULTI
|
109
|
+
|
110
|
+
[CREATE-TABLE]
|
111
|
+
QUERY = "CREATE TABLE foobar ( id SERIAL, name VARCHAR(50) )"
|
112
|
+
|
113
|
+
* [QUERY-ALIAS]: as with the database definition, the alias is used to
|
114
|
+
refer to the query inside of the application.
|
115
|
+
* QUERY: the sql query. It can be enclosed by " but it's optional as long
|
116
|
+
as the query fits into one line. ? is used to define parameters which will
|
117
|
+
be binded later on. No ; needed.
|
118
|
+
* HANDLER: the handler is in charge of transforming the query's result set.
|
119
|
+
There's own section devoted to that topic. HANDLER is optional (since a
|
120
|
+
result set can be empty).
|
121
|
+
|
122
|
+
As the database definitions, the query definitions also have to be added to the pool:
|
123
|
+
|
124
|
+
DDDBL::Pool << DDDBL::Config.parse_queries('/path/to/query/definitions.sql')
|
125
|
+
|
126
|
+
==== Querying
|
127
|
+
|
128
|
+
To execute a query which was be added to the pool is straight forward:
|
129
|
+
|
130
|
+
formated_result = DDDBL::get('QUERY-ALIAS')
|
131
|
+
|
132
|
+
# binds 'bar'
|
133
|
+
formated_result = DDDBL::get('QUERY-MULTILINE', 'bar')
|
134
|
+
|
135
|
+
# without a result set
|
136
|
+
DDDBL::get('CREATE-TABLE')
|
137
|
+
|
138
|
+
=== Transactions
|
139
|
+
|
140
|
+
DDDBL also supports transaction. More information on the implementation details
|
141
|
+
has to be looked up in the RDBI::Database documentation
|
142
|
+
|
143
|
+
DDDBL::transaction do
|
144
|
+
DDDBL::get('CREATE-TABLE')
|
145
|
+
|
146
|
+
# will fail because QUERY-MULTILINE expects a parameter
|
147
|
+
DDDBL::get('QUERY-MULTILINE')
|
148
|
+
end
|
149
|
+
|
150
|
+
Since the QUERY-MULTLINE will fail, the whole transaction will be rolled back
|
151
|
+
and no table will be created.
|
152
|
+
|
153
|
+
=== Creating your own result handler
|
154
|
+
|
155
|
+
As you may know or guess by now, RDBI supports transforming the query's result
|
156
|
+
set to any datastructure you want. It's as straight forward as adding a query
|
157
|
+
file to the pool, just extend the RDBI's RDBI::Result::Driver class and
|
158
|
+
implement at least #fetch. The name of the new result driver is then the name
|
159
|
+
which has to be used in the query's HANDLER configuration field.
|
160
|
+
|
161
|
+
It's also possible to pass a configuration to the result driver defined in the
|
162
|
+
query definition:
|
163
|
+
|
164
|
+
[QUERY-ALIAS]
|
165
|
+
QUERY = "SELECT bar FROM foo;"
|
166
|
+
HANDLER = MULTI INT::bar
|
167
|
+
|
168
|
+
MULTI, as in the other examples, is the name of the result driver. If there's
|
169
|
+
a whitespace following with more text, this additional text will be passed to
|
170
|
+
the result driver's constructor. The example definition will explitictly cast
|
171
|
+
every row's bar-column value to an Integer.
|
172
|
+
|
173
|
+
For more information on creating a result driver, please read the documentation
|
174
|
+
of RDBI::Result::Driver. If you want to overwrite a default result driver,
|
175
|
+
just override the class.
|
176
|
+
|
177
|
+
== Bughunting was successful!
|
178
|
+
|
179
|
+
Just report the bug through github's tracker: https://github.com/melkon/dddbl/issues
|
180
|
+
|
181
|
+
== I'd like to patch and / or help maintain DDDBL. How can I?
|
182
|
+
|
183
|
+
* Fork the project: http://github.com/melkon/dddbl
|
184
|
+
* Make your feature addition or bug fix.
|
185
|
+
|
186
|
+
== Copyright
|
187
|
+
|
188
|
+
Copyright (c) 2011 André Gawron. See LICENSE for details.
|
data/Rakefile
ADDED
data/dddbl.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "dddbl/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "dddbl"
|
6
|
+
s.version = DDDBL::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["André Gawron"]
|
9
|
+
s.email = ["andre@ziemek.de"]
|
10
|
+
s.homepage = "https://github.com/melkon/dddbl"
|
11
|
+
s.summary = %q{A Definition Driven Database Layer}
|
12
|
+
s.description = %q{A Definition Driven Database Layer. First developed in php, see: http://www.dddbl.de}
|
13
|
+
|
14
|
+
s.rubyforge_project = "dddbl"
|
15
|
+
|
16
|
+
s.add_dependency "inifile", ">= 0.4.1"
|
17
|
+
s.add_dependency "rdbi"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
data/example/dbdef.def
ADDED
data/example/demo.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rdbi'
|
2
|
+
require 'rdbi-driver-postgresql'
|
3
|
+
|
4
|
+
require 'inifile'
|
5
|
+
require 'dddbl'
|
6
|
+
|
7
|
+
class RDBI::Result::Driver::YAL < RDBI::Result::Driver
|
8
|
+
def initialize(result, *args)
|
9
|
+
super
|
10
|
+
RDBI::Util.optional_require('yaml')
|
11
|
+
end
|
12
|
+
|
13
|
+
def format_single_row(raw)
|
14
|
+
::Hash[column_names.zip(raw)].to_yaml
|
15
|
+
end
|
16
|
+
|
17
|
+
def format_multiple_rows(raw_rows)
|
18
|
+
raw_rows.collect { |row| ::Hash[column_names.zip(row)] }.to_yaml
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def column_names
|
23
|
+
@column_names ||= @result.schema.columns.map(&:name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
DDDBL::Pool::DB << DDDBL::Config.parse_dbs('dbdef.def')
|
29
|
+
DDDBL::Pool << DDDBL::Config.parse_queries('test.sql')
|
30
|
+
|
31
|
+
DDDBL::get('TEST-QUERY')
|
32
|
+
|
33
|
+
DDDBL::get('TEST-INSERT', 'andre')
|
34
|
+
DDDBL::get('TEST-INSERT', 'melkon')
|
35
|
+
|
36
|
+
before = DDDBL::get('TEST-SELECT') ; p before
|
37
|
+
|
38
|
+
begin
|
39
|
+
DDDBL::transaction do
|
40
|
+
|
41
|
+
DDDBL::get('TEST-UPDATE', 'thorny', 2)
|
42
|
+
|
43
|
+
# raises an exception
|
44
|
+
# rollback initiated
|
45
|
+
# throws an exception
|
46
|
+
DDDBL::get('TEST-INSERT')
|
47
|
+
|
48
|
+
end ; rescue => e ; end
|
49
|
+
|
50
|
+
after = DDDBL::get('TEST-SELECT') ; p after
|
51
|
+
|
52
|
+
|
53
|
+
if before.to_s == after.to_s
|
54
|
+
p "transaction rollbacked"
|
55
|
+
else
|
56
|
+
p "transaction commited"
|
57
|
+
end
|
58
|
+
|
59
|
+
DDDBL::get('TEST-DROP')
|
data/example/test.sql
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
[TEST-QUERY]
|
2
|
+
QUERY = "CREATE TABLE muff (id SERIAL, name VARCHAR(255))"
|
3
|
+
|
4
|
+
[TEST-INSERT]
|
5
|
+
QUERY = "INSERT INTO muff (name) VALUES(?)"
|
6
|
+
|
7
|
+
[TEST-SELECT]
|
8
|
+
QUERY = "SELECT * FROM muff"
|
9
|
+
HANDLER = YAML
|
10
|
+
|
11
|
+
[TEST-DROP]
|
12
|
+
QUERY = "DROP TABLE IF EXISTS muff"
|
data/lib/dddbl.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
#
|
2
|
+
# DDDBL - Definition Driven Database Layer
|
3
|
+
# Copyright (c) 2011 André Gawron. See LICENSE and README for details.
|
4
|
+
#
|
5
|
+
# @example Executing Query
|
6
|
+
# result = DDDBL::get('QUERY-ALIAS', 'foo', 'bar')
|
7
|
+
#
|
8
|
+
# @example Simple Transaction
|
9
|
+
# DDDBL::transaction do
|
10
|
+
# DDDBL::get('QUERY-ALIAS', 'foo', 'bar')
|
11
|
+
# DDDBL::get('CREATE-TABLE')
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# @example Select another database
|
15
|
+
# DDDBL::select_db('DATABASE-ALIAS')
|
16
|
+
#
|
17
|
+
class DDDBL
|
18
|
+
|
19
|
+
#
|
20
|
+
# a query can be associated with a specific
|
21
|
+
# database driver but if none is given, it will
|
22
|
+
# be safed in GLOBAL_POOL so every database connection
|
23
|
+
# can execute the defined query.
|
24
|
+
#
|
25
|
+
GLOBAL_POOL = :default
|
26
|
+
|
27
|
+
class << self
|
28
|
+
|
29
|
+
#
|
30
|
+
# selects an database connection which will then
|
31
|
+
# be used to execute queries. if the connection
|
32
|
+
# is not yet established, it will be after calling
|
33
|
+
# this method.
|
34
|
+
#
|
35
|
+
# @param [String] database_name databse alias which was defined in the config file
|
36
|
+
#
|
37
|
+
def select_db(database_name)
|
38
|
+
if @dbh == nil || @database != database_name
|
39
|
+
@dbh = RDBI::pool(database_name).get_dbh
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# executes a defined alias and returns its result set which is
|
45
|
+
# transformed by an optionally defined result hanlder before.
|
46
|
+
#
|
47
|
+
# #get uses the current database connection set by #select_db
|
48
|
+
# if none was explicity selected but a default database was set
|
49
|
+
# using the configuration files, the default connection
|
50
|
+
# will be used.
|
51
|
+
#
|
52
|
+
# @param [String] query_alias querie's alias defined in the config file
|
53
|
+
# @param [Array] *binds the parameters which shall be binded to the query
|
54
|
+
#
|
55
|
+
# @return the result set transformed by the defined handler
|
56
|
+
#
|
57
|
+
def get(query_alias, *binds)
|
58
|
+
query = DDDBL::Pool[@dbh.driver, query_alias]
|
59
|
+
res = @dbh.execute(query[:query], *binds)
|
60
|
+
res.as(query[:handler]).fetch(:all) if !query[:handler].empty?
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# delegates method calls to RDBI::Database
|
65
|
+
# DDDBL::transaction is currently implemented this way.
|
66
|
+
#
|
67
|
+
# @param [String] method valid RDBI::Database method
|
68
|
+
# @param [Array] *args RDBI::Database method's arguments
|
69
|
+
# @param [Proc] &block and finally the optional block
|
70
|
+
#
|
71
|
+
# @return everything that the RDBI::Datbase method calls will return
|
72
|
+
# @return [Boolean] false if no database connection is set
|
73
|
+
#
|
74
|
+
# @raise [ArgumentError] if RDBI::Database does not implement given method
|
75
|
+
#
|
76
|
+
def method_missing(method, *args, &block)
|
77
|
+
return false if !@dbh.is_a? RDBI::Database
|
78
|
+
raise ArgumentError, "RDBI::Database doesnt have #{method}" if !@dbh.respond_to?(method)
|
79
|
+
|
80
|
+
@dbh.send(method, *args, &block)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# DDDBL::Utils provides methods for validation of queries
|
89
|
+
# and database definitions.
|
90
|
+
#
|
91
|
+
# @see DDDBL::Pool for implementation examples
|
92
|
+
# @see DDDBL::Pool::DB for implementation examples
|
93
|
+
#
|
94
|
+
module DDDBL::Utils
|
95
|
+
|
96
|
+
#
|
97
|
+
# sets the mandatory fiels of a configuration
|
98
|
+
#
|
99
|
+
# @param [#each] fields which are mandatory, has to implement `#each`
|
100
|
+
#
|
101
|
+
# @raise [ArgumentError] if fields does not respond to #each
|
102
|
+
#
|
103
|
+
def mandatory(fields)
|
104
|
+
raise ArgumentError, 'parameter has to implement #each' if !fields.respond_to?('each')
|
105
|
+
|
106
|
+
@mandatory = fields
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# sets the optional fiels of a configuration
|
111
|
+
#
|
112
|
+
# @param [#each] fields which are optional, has to implement #each
|
113
|
+
#
|
114
|
+
# @raise [ArgumentError] if fields does not respond to #each
|
115
|
+
#
|
116
|
+
def optional(fields)
|
117
|
+
raise ArgumentError, 'parameter has to implement #each' if !fields.respond_to?('each')
|
118
|
+
|
119
|
+
@optional = fields
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# checks check for the defined mandatory and optional values
|
124
|
+
#
|
125
|
+
# @param [#each] check data structure which implements #each
|
126
|
+
#
|
127
|
+
# @return [Boolean] true if validation was successful
|
128
|
+
# @return [Boolean] false if not
|
129
|
+
#
|
130
|
+
# @raise [ArgumentError] if check does not respond to #has_key?
|
131
|
+
# @raise [ArgumentError] if check does not respond to #empty?
|
132
|
+
#
|
133
|
+
def valid?(check)
|
134
|
+
raise ArgumentError, 'parameter has to implement has_key?' if !check.respond_to?('has_key?')
|
135
|
+
raise ArgumentError, 'parameter has to implement empty?' if !check.respond_to?('empty?')
|
136
|
+
|
137
|
+
@mandatory.each do |key|
|
138
|
+
return false if !check.has_key?(key) || check[key].empty?
|
139
|
+
end
|
140
|
+
|
141
|
+
@optional.each do |key|
|
142
|
+
return false if !check.has_key?(key)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
require 'dddbl/config'
|
149
|
+
require 'dddbl/pool'
|
150
|
+
|
151
|
+
# result handler
|
152
|
+
require 'dddbl/results'
|
data/lib/dddbl/config.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
#
|
2
|
+
# DDDBL - Definition Driven Database Layer
|
3
|
+
# Copyright (c) 2011 André Gawron. See LICENSE and README for details.
|
4
|
+
#
|
5
|
+
# DDDBL::Config parses the configuration files for queries and databases.
|
6
|
+
#
|
7
|
+
# Feel free to implement another parser (for example YAML-based files)
|
8
|
+
# and overwrite or extend DDDBL::Config. DDDBL::Config::Defaults
|
9
|
+
# will give you reasonable default values.
|
10
|
+
#
|
11
|
+
# @example Parse database definitions
|
12
|
+
# db_defintions = DDDBL::Config.parse_dbs('/path/to/file')
|
13
|
+
#
|
14
|
+
# @example Parse query definitions
|
15
|
+
# query_definitions = DDDBL::Config.parse_queries('/path/to/file')
|
16
|
+
#
|
17
|
+
module DDDBL::Config
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
#
|
22
|
+
# Offers methods to get default values for a database
|
23
|
+
# and query configuration.
|
24
|
+
#
|
25
|
+
module DDDBL::Config::Defaults
|
26
|
+
|
27
|
+
#
|
28
|
+
# returns the default values for a query configuration
|
29
|
+
#
|
30
|
+
# @param [String] query_alias the query's alias
|
31
|
+
#
|
32
|
+
# @return [Hash] the default values for a query configuration
|
33
|
+
# @option [String] :alias will contain the query_alias
|
34
|
+
# @option [String, Symbol] :type will contain DDDBL::GLOBAL_POOL
|
35
|
+
# @option [String] :handler will contain an empty string
|
36
|
+
#
|
37
|
+
def default_query(query_alias)
|
38
|
+
{
|
39
|
+
:alias => query_alias,
|
40
|
+
:type => DDDBL::GLOBAL_POOL,
|
41
|
+
:handler => ''
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# returns the default values for a database configuration
|
47
|
+
#
|
48
|
+
# @param [String] db_alias the database's alias
|
49
|
+
#
|
50
|
+
# @return [Hash] the default values for a database configuration
|
51
|
+
# @option [String] :pool_name will contain the db_alias
|
52
|
+
# @option [Boolean] :default will contain false
|
53
|
+
# @option [String] :pass will contain the user's password
|
54
|
+
#
|
55
|
+
def default_db(db_alias)
|
56
|
+
{
|
57
|
+
:pool_name => db_alias,
|
58
|
+
:default => false,
|
59
|
+
:pass => ''
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
include DDDBL::Config::Defaults
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# parses the queries from given file
|
71
|
+
#
|
72
|
+
# @param [File] file containing query definitions in IniFile-format
|
73
|
+
#
|
74
|
+
# @return [Hash<Hash>] parsed queries
|
75
|
+
#
|
76
|
+
# @raise [ArgumentError] if file does not exist
|
77
|
+
#
|
78
|
+
# @see [IniFile] for more information on the syntax
|
79
|
+
#
|
80
|
+
def self.parse_queries(file)
|
81
|
+
raise ArgumentError, "#{file} does not exist" if !File.exists?(file)
|
82
|
+
|
83
|
+
queries = {}
|
84
|
+
|
85
|
+
IniFile.load(file).each do |query_alias, query_param, query_value|
|
86
|
+
queries[query_alias] ||= default_query(query_alias)
|
87
|
+
queries[query_alias][query_param.downcase.to_sym] = query_value
|
88
|
+
end
|
89
|
+
|
90
|
+
queries
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# parses the databases from given file
|
96
|
+
#
|
97
|
+
# @param [File] file containing database definitions in IniFile-format
|
98
|
+
#
|
99
|
+
# @return [Hash<Hash>] parsed databases
|
100
|
+
#
|
101
|
+
# @raise [ArgumentError] if file does not exist
|
102
|
+
#
|
103
|
+
# @see [IniFile] for more information on the syntax
|
104
|
+
#
|
105
|
+
def self.parse_dbs(file)
|
106
|
+
raise ArgumentError, "#{file} does not exist" if !File.exists?(file)
|
107
|
+
|
108
|
+
dbs = {}
|
109
|
+
|
110
|
+
IniFile.load(file).each do |db_alias, db_param, db_value|
|
111
|
+
dbs[db_alias] ||= default_db(db_alias)
|
112
|
+
dbs[db_alias][db_param.downcase.to_sym] = db_value
|
113
|
+
end
|
114
|
+
|
115
|
+
dbs
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
data/lib/dddbl/pool.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
#
|
2
|
+
# DDDBL - Definition Driven Database Layer
|
3
|
+
# Copyright (c) 2011 André Gawron. See LICENSE and README for details.
|
4
|
+
#
|
5
|
+
# DDDBL::Pool contains the parsed queries
|
6
|
+
#
|
7
|
+
class DDDBL::Pool
|
8
|
+
|
9
|
+
class << self
|
10
|
+
include DDDBL::Utils
|
11
|
+
end
|
12
|
+
|
13
|
+
mandatory [:alias, :query, :type]
|
14
|
+
optional [:handler]
|
15
|
+
|
16
|
+
#
|
17
|
+
# returns a stored query for given database driver
|
18
|
+
# if none is found, the method looks in the global "namespace"
|
19
|
+
# if there is also no query stored, it raises an exception.
|
20
|
+
#
|
21
|
+
# @param [String] dbtype database's driver name (e.g. MySQL or PostgreSQL)
|
22
|
+
# @param [String] query_alias the query's alias
|
23
|
+
#
|
24
|
+
# @return [Hash] the query configuration
|
25
|
+
#
|
26
|
+
# @raise [StandardError] if no query is stored under given alias and driver
|
27
|
+
#
|
28
|
+
# @see [DDDBL::Pool::[]=] for hash keys
|
29
|
+
# @see [DDDBL::GLOBAL_POOL] for global "namespace"
|
30
|
+
#
|
31
|
+
def self.[](dbtype, query_alias)
|
32
|
+
if @pool.has_key?(dbtype) && @pool[dbtype].has_key?(query_alias)
|
33
|
+
@pool[dbtype][query_alias]
|
34
|
+
elsif @pool.has_key?(DDDBL::GLOBAL_POOL) && @pool[DDDBL::GLOBAL_POOL].has_key?(query_alias)
|
35
|
+
@pool[DDDBL::GLOBAL_POOL][query_alias]
|
36
|
+
else
|
37
|
+
raise StandardError, "#{query_alias} not a saved query alias"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# sets a query configuration for given database pool
|
43
|
+
# optional keys have to be set but can be empty.
|
44
|
+
#
|
45
|
+
# @param [String] dbtype database's driver name (e.g. MySQL or PostgreSQL)
|
46
|
+
# @param [Hash] query_config a valid query configuration
|
47
|
+
#
|
48
|
+
# @raise [StandardError] if the query configuration is not valid
|
49
|
+
#
|
50
|
+
# @see #mandatory for mandatory query configration keys
|
51
|
+
# @see #optional for optional query configuration keys
|
52
|
+
#
|
53
|
+
def self.[]=(dbtype, query_config)
|
54
|
+
raise StandardError, 'query is not properly configured' if !valid?(query_config)
|
55
|
+
|
56
|
+
@pool ||= Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)}))
|
57
|
+
@pool[dbtype][query_config[:alias]] = query_config
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# shortcut for an array (or hash) of query configurations
|
62
|
+
# adds the whole query_configs to their corresponding pools.
|
63
|
+
#
|
64
|
+
# @param [#each] query_configs a list of query_configs, has to respond to #each(key, query_config)
|
65
|
+
#
|
66
|
+
def self.<<(query_configs)
|
67
|
+
query_configs.each do |key, query_config|
|
68
|
+
DDDBL::Pool[query_config[:type]] = query_config
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# DDDBL - Definition Driven Database Layer
|
76
|
+
# Copyright (c) 2011 André Gawron. See LICENSE and README for details.
|
77
|
+
#
|
78
|
+
# DDDBL::Pool::DB contains the parsed database definitions
|
79
|
+
#
|
80
|
+
class DDDBL::Pool::DB
|
81
|
+
|
82
|
+
class << self
|
83
|
+
include DDDBL::Utils
|
84
|
+
end
|
85
|
+
|
86
|
+
mandatory [:type, :pool_name, :host, :dbname, :user]
|
87
|
+
optional [:default, :pass]
|
88
|
+
|
89
|
+
#
|
90
|
+
# adds the whole db_configs to the pool,
|
91
|
+
# establishs a connection to each of them,
|
92
|
+
# and selects the default database connection
|
93
|
+
# if no connection is already set.
|
94
|
+
#
|
95
|
+
# @param [#each] query_configs a list of query_configs, has to respond to #each(key, query_config)
|
96
|
+
#
|
97
|
+
# @raise [StandardError] if the configration file is not valid
|
98
|
+
#
|
99
|
+
def self.<<(db_configs)
|
100
|
+
db_configs.each do |key, db_config|
|
101
|
+
raise StandardError, 'db_config is not valid' if !valid?(db_config)
|
102
|
+
|
103
|
+
RDBI::connect_cached(db_config[:type], db_config);
|
104
|
+
DDDBL::select_db(db_config[:pool_name]) if db_config[:default] && !DDDBL::connected?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# the php-library makes use of MULTI as result handler
|
2
|
+
# RDBI provides with Struct the same functionality
|
3
|
+
# for portability reasons of the query config files
|
4
|
+
# a MULTI handler is introduced which is an alias for Struct
|
5
|
+
class RDBI::Result::Driver::MULTI < RDBI::Result::Driver::Struct ; end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dddbl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: 6
|
5
|
+
version: 0.0.1.alpha
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- "Andr\xC3\xA9 Gawron"
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-08-31 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: inifile
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.4.1
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rdbi
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
description: "A Definition Driven Database Layer. First developed in php, see: http://www.dddbl.de"
|
38
|
+
email:
|
39
|
+
- andre@ziemek.de
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- .gitignore
|
48
|
+
- LICENSE
|
49
|
+
- README.rdoc
|
50
|
+
- Rakefile
|
51
|
+
- dddbl.gemspec
|
52
|
+
- example/dbdef.def
|
53
|
+
- example/demo.rb
|
54
|
+
- example/test.sql
|
55
|
+
- lib/dddbl.rb
|
56
|
+
- lib/dddbl/config.rb
|
57
|
+
- lib/dddbl/pool.rb
|
58
|
+
- lib/dddbl/results.rb
|
59
|
+
- lib/dddbl/version.rb
|
60
|
+
homepage: https://github.com/melkon/dddbl
|
61
|
+
licenses: []
|
62
|
+
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 1.3.1
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project: dddbl
|
83
|
+
rubygems_version: 1.8.5
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: A Definition Driven Database Layer
|
87
|
+
test_files: []
|
88
|
+
|