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