ribs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
Binary file
Binary file
Binary file
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