ribs 0.0.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/README +139 -0
- data/Rakefile +64 -0
- data/lib/antlr-2.7.6.jar +0 -0
- data/lib/commons-collections-3.1.jar +0 -0
- data/lib/dom4j-1.6.1.jar +0 -0
- data/lib/hibernate3.jar +0 -0
- data/lib/javassist-3.4.GA.jar +0 -0
- data/lib/jruby-complete.jar +0 -0
- data/lib/jta-1.1.jar +0 -0
- data/lib/ribs.jar +0 -0
- data/lib/ribs.rb +78 -0
- data/lib/ribs/core_ext/time.rb +21 -0
- data/lib/ribs/db.rb +188 -0
- data/lib/ribs/definition.rb +329 -0
- data/lib/ribs/meat.rb +33 -0
- data/lib/ribs/session.rb +237 -0
- data/lib/slf4j-api-1.5.2.jar +0 -0
- data/lib/slf4j-jdk14-1.5.2.jar +0 -0
- data/test/artist_spec.rb +94 -0
- data/test/find_spec.rb +2 -0
- data/test/simple_select_spec.rb +79 -0
- data/test/test_helper.rb +83 -0
- data/test/track_spec.rb +415 -0
- data/test/type_spec.rb +2 -0
- metadata +80 -0
data/README
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
= Ribs - A Ruby ORM using Hibernate
|
2
|
+
|
3
|
+
The current ORM approaches for Ruby include ActiveRecord, DataMapper
|
4
|
+
and RBatis. None of these have the versatility and power that
|
5
|
+
Hibernate gives to any Java project. Ribs is a new interpretation of
|
6
|
+
the idea ActiveHibernate, which was proposed in a blog post
|
7
|
+
here[http://olabini.com/blog/2007/04/activehibernate-any-takers/]. The
|
8
|
+
original name didn't really suit, though, since it was based on the
|
9
|
+
ActiveRecord name, and Ribs will end up being something quite
|
10
|
+
different.
|
11
|
+
|
12
|
+
So what is Ribs? It's a Ruby framework written for JRuby, that allows
|
13
|
+
you to use Hibernate to persist your Ruby objects. There are many
|
14
|
+
things planned for Ribs, but currently it only supports quite basic
|
15
|
+
operations.
|
16
|
+
|
17
|
+
== Definitions
|
18
|
+
|
19
|
+
To get started, you first need to define a database connection for
|
20
|
+
Ribs to use. In most cases you only have one, but Ribs doesn't
|
21
|
+
preclude you defining several and using them in different places. The
|
22
|
+
code for doing that looks like this:
|
23
|
+
|
24
|
+
Ribs::DB.define do |db|
|
25
|
+
db.dialect = 'Derby'
|
26
|
+
db.uri = 'jdbc:derby:test_database;create=true'
|
27
|
+
db.driver = 'org.apache.derby.jdbc.EmbeddedDriver'
|
28
|
+
end
|
29
|
+
|
30
|
+
You can also provide username, password, and other properties that
|
31
|
+
will be passed through to Hibernate. Currently, Derby is the only
|
32
|
+
database tested against, but most of the features used are totally
|
33
|
+
database independent, and Hibernate shields Ribs from most of the
|
34
|
+
rest.
|
35
|
+
|
36
|
+
See the define method and the Ribs::DB class for more information on
|
37
|
+
what's available.
|
38
|
+
|
39
|
+
To actually make a Ruby class a database backed objects, you use the
|
40
|
+
method {Kernel#Ribs!}[link:classes/Kernel.html#M000038] In the simple case you don't need to actually
|
41
|
+
provide any configuration, but support is available for renaming
|
42
|
+
columns, deciding which one is the primary key, and also avoid making
|
43
|
+
columns part of the object. The simple case looks like this:
|
44
|
+
|
45
|
+
class Blog
|
46
|
+
Ribs!
|
47
|
+
end
|
48
|
+
|
49
|
+
It can also be written as:
|
50
|
+
|
51
|
+
class Blog; end
|
52
|
+
|
53
|
+
Ribs! :on => Blog
|
54
|
+
|
55
|
+
There is no need to actually have the Ribs definition inside of the
|
56
|
+
model class. You can define that a model should go against a specific
|
57
|
+
database definition if you want - by default it will use the default
|
58
|
+
database.
|
59
|
+
|
60
|
+
In the above example, the table name backing the model will be assumed
|
61
|
+
to be "blog". Case is not important here. Ribs tries to find the table
|
62
|
+
no matter what.
|
63
|
+
|
64
|
+
To redefine which table to use, the names of the columns, and where
|
65
|
+
the primary key is, you need to provide a block to the {Ribs!}[link:classes/Kernel.html#M000038] method:
|
66
|
+
|
67
|
+
class Blog
|
68
|
+
Ribs! do |r|
|
69
|
+
r.table :blogs
|
70
|
+
|
71
|
+
r.primary_key :blog_id
|
72
|
+
|
73
|
+
r.col :title, :blog_title
|
74
|
+
|
75
|
+
r.avoid :irrelevant_column
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
This code will back the model against the "blogs" table, have the
|
80
|
+
column name blog_id represent the primary key, and map the column
|
81
|
+
title to the property blog_title. Note that primary_key[link:classes/Ribs/Rib.html#M000028] can take a
|
82
|
+
second parameter that is the property name. Finally, avoid[link:classes/Ribs/Rib.html#M000029] tells Ribs
|
83
|
+
to avoid a specific column.
|
84
|
+
|
85
|
+
Currently Ribs only supports simple data types. It doesn't include
|
86
|
+
associations, and have no support for setting default values, or
|
87
|
+
constraints in the definitions. It's not possible to set the types in
|
88
|
+
the definitions either.
|
89
|
+
|
90
|
+
== Usage
|
91
|
+
|
92
|
+
Once you have a defined model, you can work with it in several
|
93
|
+
different ways.
|
94
|
+
|
95
|
+
If you want to create new instances you can do it much like with
|
96
|
+
ActiveRecord:
|
97
|
+
|
98
|
+
blog = Blog.new
|
99
|
+
blog.blog_id = 1
|
100
|
+
blog.blog_title = "Foobar"
|
101
|
+
blog.save
|
102
|
+
|
103
|
+
The new method can take the parameters as a hash of symbols too:
|
104
|
+
|
105
|
+
blog = Blog.new(
|
106
|
+
:blog_id => 1,
|
107
|
+
:blog_title => "Foobar")
|
108
|
+
blog.save
|
109
|
+
|
110
|
+
And as a short hand a create method is available:
|
111
|
+
|
112
|
+
blog = Blog.create(
|
113
|
+
:blog_id => 1,
|
114
|
+
:blog_title => "Foobar")
|
115
|
+
|
116
|
+
To find a specific entry:
|
117
|
+
|
118
|
+
blog = Blog.find(1)
|
119
|
+
|
120
|
+
Or to find all entries:
|
121
|
+
|
122
|
+
blogs = Blog.find(:all)
|
123
|
+
|
124
|
+
To update an entry:
|
125
|
+
|
126
|
+
blog.blog_title = "New title"
|
127
|
+
blog.save
|
128
|
+
|
129
|
+
To destroy an existing entry:
|
130
|
+
|
131
|
+
blog.destroy!
|
132
|
+
|
133
|
+
Or to destroy it based on id:
|
134
|
+
|
135
|
+
Blog.destroy(1)
|
136
|
+
|
137
|
+
== License
|
138
|
+
|
139
|
+
Ribs is released under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
|
6
|
+
task :ant do
|
7
|
+
ret = system('ant')
|
8
|
+
if !ret
|
9
|
+
raise "Compilation error"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => [:spec]
|
14
|
+
task :test => [:spec]
|
15
|
+
|
16
|
+
desc "Flog all Ruby files in lib"
|
17
|
+
task :flog do
|
18
|
+
system("find lib -name '*.rb' | xargs flog")
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Run all specs"
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
23
|
+
t.libs << "test"
|
24
|
+
t.libs << "lib"
|
25
|
+
t.spec_files = FileList['test/**/*_spec.rb']
|
26
|
+
t.verbose = true
|
27
|
+
t.spec_opts = ["-fs", "--color"]
|
28
|
+
end
|
29
|
+
|
30
|
+
task :spec => [:ant]
|
31
|
+
|
32
|
+
desc 'Generate RDoc'
|
33
|
+
Rake::RDocTask.new do |task|
|
34
|
+
task.main = 'README'
|
35
|
+
task.title = 'ribs'
|
36
|
+
task.rdoc_dir = 'doc'
|
37
|
+
task.options << "--line-numbers" << "--inline-source"
|
38
|
+
task.rdoc_files.include('README', 'lib/**/*.rb')
|
39
|
+
end
|
40
|
+
|
41
|
+
Gem::manage_gems
|
42
|
+
|
43
|
+
specification = Gem::Specification.new do |s|
|
44
|
+
s.name = "ribs"
|
45
|
+
s.summary = "Ribs wraps Hibernate, to provide a good ORM for JRuby"
|
46
|
+
s.version = "0.0.1"
|
47
|
+
s.author = 'Ola Bini'
|
48
|
+
s.description = s.summary
|
49
|
+
s.homepage = 'http://ribs.rubyforge.org'
|
50
|
+
s.rubyforge_project = 'ribs'
|
51
|
+
|
52
|
+
s.has_rdoc = true
|
53
|
+
s.extra_rdoc_files = ['README']
|
54
|
+
s.rdoc_options << '--title' << 'ribs' << '--main' << 'README' << '--line-numbers'
|
55
|
+
|
56
|
+
s.email = 'ola.bini@gmail.com'
|
57
|
+
s.files = FileList['{lib,test}/**/*.{rb,jar}', '[A-Z]*$', 'Rakefile'].to_a
|
58
|
+
# s.add_dependency('mocha', '>= 0.5.5')
|
59
|
+
end
|
60
|
+
|
61
|
+
Rake::GemPackageTask.new(specification) do |package|
|
62
|
+
package.need_zip = false
|
63
|
+
package.need_tar = false
|
64
|
+
end
|
data/lib/antlr-2.7.6.jar
ADDED
Binary file
|
Binary file
|
data/lib/dom4j-1.6.1.jar
ADDED
Binary file
|
data/lib/hibernate3.jar
ADDED
Binary file
|
Binary file
|
Binary file
|
data/lib/jta-1.1.jar
ADDED
Binary file
|
data/lib/ribs.jar
ADDED
Binary file
|
data/lib/ribs.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
# Force logging to a lower level
|
4
|
+
lm = java.util.logging.LogManager.log_manager
|
5
|
+
lm.logger_names.each do |ln|
|
6
|
+
lm.get_logger(ln).set_level(java.util.logging.Level::SEVERE)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Everything needed for Hibernate
|
10
|
+
require 'antlr-2.7.6.jar'
|
11
|
+
require 'commons-collections-3.1.jar'
|
12
|
+
require 'dom4j-1.6.1.jar'
|
13
|
+
require 'javassist-3.4.GA.jar'
|
14
|
+
require 'jta-1.1.jar'
|
15
|
+
require 'slf4j-api-1.5.2.jar'
|
16
|
+
require 'slf4j-jdk14-1.5.2.jar'
|
17
|
+
require 'hibernate3.jar'
|
18
|
+
|
19
|
+
# Java parts of Ribs
|
20
|
+
require 'ribs.jar'
|
21
|
+
|
22
|
+
require 'bigdecimal'
|
23
|
+
|
24
|
+
require 'ribs/db'
|
25
|
+
require 'ribs/definition'
|
26
|
+
require 'ribs/session'
|
27
|
+
require 'ribs/meat'
|
28
|
+
require 'ribs/core_ext/time'
|
29
|
+
|
30
|
+
# The Ribs module includes most functionality needed for Ribs. The
|
31
|
+
# module is strictly a namespace and isn't generally supposed to be
|
32
|
+
# mixed in.
|
33
|
+
#
|
34
|
+
module Ribs
|
35
|
+
class << self
|
36
|
+
|
37
|
+
# The with_session method provides an easy way of working with a
|
38
|
+
# low level Ribs session. It will get a session for the database
|
39
|
+
# in question, yield that session to the block and then release
|
40
|
+
# the session when finished. This should generally not be needed,
|
41
|
+
# but wrapping a block if code with this method is a good way of
|
42
|
+
# opening a session and make sure it doesn't get fully closed
|
43
|
+
# until after the block.
|
44
|
+
#
|
45
|
+
# +from+ decides which database definition to get a session for.
|
46
|
+
#
|
47
|
+
def with_session(from = :default)
|
48
|
+
s = Ribs::Session.get(from)
|
49
|
+
yield s
|
50
|
+
ensure
|
51
|
+
s.release
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module Kernel
|
57
|
+
|
58
|
+
# The main method for defining a Ribs model object can be used
|
59
|
+
# either from inside the class to define it on, or outside by
|
60
|
+
# providing a parameter for the class to act on. This gives some
|
61
|
+
# flexibility about where definitions should go. The implementation
|
62
|
+
# also keeps it open whether the model class is a real class or a
|
63
|
+
# singleton class. The end result of calling the Ribs! method will
|
64
|
+
# be to generate a definition for a database mapping, based on the
|
65
|
+
# information provided inside the optional block. The block will
|
66
|
+
# yield an object of type Ribs::Rib.
|
67
|
+
#
|
68
|
+
# +user_options+ currently takes these parameters:
|
69
|
+
# * <tt>:on</tt> - The class to define this mapping on. Default will be the receiver of the method call.
|
70
|
+
# * <tt>:db</tt> - The database to define this mapping for. <tt>:default</tt> is the default value.
|
71
|
+
# * <tt>:from</tt> - The hibernate XML file to fetch mapping information from. By default nil, and currently doesn't do anything.
|
72
|
+
#
|
73
|
+
def Ribs!(user_options = {}, &block)
|
74
|
+
default_options = {:on => self, :db => :default, :from => nil}
|
75
|
+
options = default_options.merge user_options
|
76
|
+
Ribs::define_ribs(options[:on], options, &block)
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
class Time
|
3
|
+
JavaSqlDate = java.sql.Date
|
4
|
+
JavaSqlTime = java.sql.Time
|
5
|
+
JavaSqlTimestamp = java.sql.Timestamp
|
6
|
+
|
7
|
+
# Returns this Time object as an instance of java.sql.Date
|
8
|
+
def to_java_sql_date
|
9
|
+
JavaSqlDate.new(self.to_i*1000)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns this Time object as an instance of java.sql.Time
|
13
|
+
def to_java_sql_time
|
14
|
+
JavaSqlTime.new(self.to_i*1000)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns this Time object as an instance of java.sql.Timestamp
|
18
|
+
def to_java_sql_time_stamp
|
19
|
+
JavaSqlTimestamp.new(self.to_i*1000)
|
20
|
+
end
|
21
|
+
end
|
data/lib/ribs/db.rb
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
module Ribs
|
2
|
+
# A DB instance represents a specific database configuration,
|
3
|
+
# including properties for the Hibernate connection.
|
4
|
+
class DB
|
5
|
+
Properties = java.util.Properties
|
6
|
+
Environment = org.hibernate.cfg.Environment
|
7
|
+
Configuration = org.hibernate.cfg.Configuration
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# All the defined databases
|
11
|
+
attr_accessor :databases
|
12
|
+
|
13
|
+
# Defines a new database. Takes the name of the database as
|
14
|
+
# parameter. There can only be one database with a specific name
|
15
|
+
# at any time. The default name is <tt>:main</tt>. After
|
16
|
+
# creating the DB instance, this will be yielded to the
|
17
|
+
# block. This block needs to be provided. The db is not
|
18
|
+
# registered until after the block has executed.
|
19
|
+
#
|
20
|
+
def define(name = :main)
|
21
|
+
db = DB.new(name)
|
22
|
+
|
23
|
+
yield db
|
24
|
+
register db
|
25
|
+
|
26
|
+
db.create
|
27
|
+
|
28
|
+
db
|
29
|
+
end
|
30
|
+
|
31
|
+
# Will register a new database, making sure that one and only
|
32
|
+
# one database is always the default. The rules are simple for
|
33
|
+
# this: If the argument is the only database in the system, it
|
34
|
+
# is the default. If there are more databases in the system, and
|
35
|
+
# the argument has the default flag set, then all other
|
36
|
+
# databases are reset to not be default.
|
37
|
+
#
|
38
|
+
def register(db)
|
39
|
+
if self.databases.empty?
|
40
|
+
db.default = true
|
41
|
+
elsif db.default?
|
42
|
+
self.databases.each do |name, db|
|
43
|
+
db.default = false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
self.databases[db.name] = db
|
47
|
+
end
|
48
|
+
|
49
|
+
# Gets the named database instance, or the default one if +name+
|
50
|
+
# is nil.
|
51
|
+
def get(name = nil)
|
52
|
+
if name && name != :default
|
53
|
+
self.databases[name]
|
54
|
+
else
|
55
|
+
self.databases.find do |name, db|
|
56
|
+
db.default?
|
57
|
+
end[1]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
self.databases = {}
|
63
|
+
|
64
|
+
# Name of the database. Default is <tt>:main</tt>
|
65
|
+
attr_accessor :name
|
66
|
+
# The JDBC uri of the database. This follows the same rules as
|
67
|
+
# Hibernate JDBC URLs. It's necessary to provide this.
|
68
|
+
attr_accessor :uri
|
69
|
+
# Driver for the database connection. Necessary to provide.
|
70
|
+
attr_accessor :driver
|
71
|
+
# The database Hibernate dialect to use. This is currently
|
72
|
+
# necessary. The available choices are:
|
73
|
+
#
|
74
|
+
# * Cache71
|
75
|
+
# * DataDirectOracle9
|
76
|
+
# * DB2390
|
77
|
+
# * DB2400
|
78
|
+
# * DB2
|
79
|
+
# * Derby
|
80
|
+
# * Firebird
|
81
|
+
# * FrontBase
|
82
|
+
# * H2
|
83
|
+
# * HSQL
|
84
|
+
# * Informix
|
85
|
+
# * Ingres
|
86
|
+
# * Interbase
|
87
|
+
# * JDataStore
|
88
|
+
# * Mckoi
|
89
|
+
# * MimerSQL
|
90
|
+
# * MySQL5
|
91
|
+
# * MySQL5InnoDB
|
92
|
+
# * MySQL
|
93
|
+
# * MySQLInnoDB
|
94
|
+
# * MySQLMyISAM
|
95
|
+
# * Oracle9
|
96
|
+
# * Oracle
|
97
|
+
# * Pointbase
|
98
|
+
# * PostgreSQL
|
99
|
+
# * Progress
|
100
|
+
# * RDBMSOS2200
|
101
|
+
# * SAPDB
|
102
|
+
# * SQLServer
|
103
|
+
# * Sybase11
|
104
|
+
# * SybaseAnywhere
|
105
|
+
# * Sybase
|
106
|
+
# * TimesTen
|
107
|
+
#
|
108
|
+
# See the package org.hibernate.dialect at
|
109
|
+
# http://www.hibernate.org/hib_docs/v3/api/ for an explanation of
|
110
|
+
# the different dialects.
|
111
|
+
attr_accessor :dialect
|
112
|
+
# The username to connect with. Can be nil
|
113
|
+
attr_accessor :username
|
114
|
+
# The password to connect with. Can be nil
|
115
|
+
attr_accessor :password
|
116
|
+
# A hash of properties to pass on to hibernate. Can be any string
|
117
|
+
# to string value.
|
118
|
+
attr_accessor :properties
|
119
|
+
# Is this database the default one? true or false.
|
120
|
+
attr_accessor :default
|
121
|
+
# All the mappings that have been defined for this database
|
122
|
+
attr_reader :mappings
|
123
|
+
|
124
|
+
# Creates the database with the specific name, or <tt>:main</tt>
|
125
|
+
# if none is provided.
|
126
|
+
def initialize(name = :main)
|
127
|
+
self.name = name
|
128
|
+
self.properties = {}
|
129
|
+
end
|
130
|
+
|
131
|
+
# Is this database the default?
|
132
|
+
def default?
|
133
|
+
self.default
|
134
|
+
end
|
135
|
+
|
136
|
+
# Creates the Hibernate Configuration object and the session
|
137
|
+
# factory that provides connections to this database.
|
138
|
+
def create
|
139
|
+
properties = Properties.new
|
140
|
+
properties.set_property(Environment::DIALECT, "org.hibernate.dialect.#{self.dialect}Dialect") if self.dialect
|
141
|
+
properties.set_property(Environment::USER, self.username) if self.username
|
142
|
+
properties.set_property(Environment::PASS, self.password) if self.password
|
143
|
+
properties.set_property(Environment::URL, self.uri) if self.uri
|
144
|
+
properties.set_property(Environment::DRIVER, self.driver) if self.driver
|
145
|
+
self.properties.each do |key, value|
|
146
|
+
properties.set_property(key, value)
|
147
|
+
end
|
148
|
+
@configuration = Configuration.new.add_properties(properties)
|
149
|
+
@configuration.set_interceptor org.jruby.ribs.EntityNameInterceptor.new
|
150
|
+
@mappings = @configuration.create_mappings
|
151
|
+
reset_session_factory!
|
152
|
+
end
|
153
|
+
|
154
|
+
# Resets the session factory. This is necessary after some
|
155
|
+
# configuration changes have happened.
|
156
|
+
def reset_session_factory!
|
157
|
+
@session_factory = @configuration.build_session_factory
|
158
|
+
end
|
159
|
+
|
160
|
+
# Fetch a new Ribs session connected to the this database. Returns
|
161
|
+
# a Ribs::Session object.
|
162
|
+
def session
|
163
|
+
sessions = (Thread.current[:ribs_db_sessions] ||= {})
|
164
|
+
if curr = sessions[self.object_id]
|
165
|
+
curr[1] += 1 #reference count
|
166
|
+
Session.new(self, curr[0])
|
167
|
+
else
|
168
|
+
sess = @session_factory.open_session
|
169
|
+
sessions[self.object_id] = [sess,1]
|
170
|
+
Session.new(self, sess)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Release a Ribs::Session object that is connected to this
|
175
|
+
# database. That Session object should not be used after this
|
176
|
+
# method has been invoked.
|
177
|
+
def release(session)
|
178
|
+
res = Thread.current[:ribs_db_sessions][self.object_id]
|
179
|
+
if res[0] == session.hibernate_session
|
180
|
+
res[1] -= 1
|
181
|
+
if res[1] == 0
|
182
|
+
res[0].close
|
183
|
+
Thread.current[:ribs_db_sessions].delete(self.object_id)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|