ribs 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,26 +1,26 @@
1
1
  module Ribs
2
- # A Ribs session maps many-to-one with a Hibernate session. When
3
- # there are no more Ribs sessions left, the Hibernate session will
2
+ # A Ribs handle maps many-to-one with a Hibernate session. When
3
+ # there are no more Ribs handles left, the Hibernate session will
4
4
  # be released too.
5
5
  #
6
- # The methods in Session is not to be used outside the framework in
6
+ # The methods in Handle is not to be used outside the framework in
7
7
  # most cases, since they are internal, brittle, not based on
8
8
  # Hibernate in all cases, and very much subject to change. Currently
9
9
  # they provide capabilities that aren't part of the framework yet,
10
10
  # such as migrations and setting up test data.
11
11
  #
12
- class Session
13
- # This error is thrown when an operation on a session is
12
+ class Handle
13
+ # This error is thrown when an operation on a handle is
14
14
  # attempted but the internal Hibernate session has already
15
15
  # been closed.
16
16
  #
17
17
  class NotConnectedError < StandardError;end
18
18
 
19
19
  class << self
20
- # Returns a session object from the database +from+. The
20
+ # Returns a handle object from the database +from+. The
21
21
  # available values for from is either one of the existing
22
22
  # defined database names, or <tt>:default</tt>, which will give
23
- # a session to the default database.
23
+ # a handle to the default database.
24
24
  #
25
25
  def get(from = :default)
26
26
  db = case from
@@ -31,14 +31,14 @@ module Ribs
31
31
  else
32
32
  Ribs::DB::get(from)
33
33
  end
34
- db.session
34
+ db.handle
35
35
  end
36
36
  end
37
37
 
38
- # The current database for this session
38
+ # The current database for this handle
39
39
  attr_reader :db
40
40
 
41
- # Creates a new session that points to the provided +db+ and
41
+ # Creates a new handle that points to the provided +db+ and
42
42
  # +hibernate_session+
43
43
  #
44
44
  def initialize(db, hibernate_session)
@@ -47,7 +47,7 @@ module Ribs
47
47
  @hibernate_session = hibernate_session
48
48
  end
49
49
 
50
- # Releases this session from the database. This will possibly
50
+ # Releases this handle from the database. This will possibly
51
51
  # result in closing the internal Hibernate session, if this is the
52
52
  # last Ribs session pointing to that Hibernate session.
53
53
  def release
@@ -62,29 +62,35 @@ module Ribs
62
62
  end
63
63
 
64
64
  # LOW LEVEL - shouldn't be used except by Ribs
65
- def find(entity_name, id) # :nodoc:
65
+ def get(entity_name, id) # :nodoc:
66
66
  chk_conn
67
- if id == :all
68
- @hibernate_session.create_criteria(entity_name).list.to_a
69
- else
70
- @hibernate_session.get(entity_name, java.lang.Integer.new(id))
71
- end
67
+ @hibernate_session.get(entity_name, java.lang.Integer.new(id))
72
68
  end
73
69
 
74
70
  # LOW LEVEL - shouldn't be used except by Ribs
75
- def save(obj) # :nodoc:
71
+ def all(entity_name) # :nodoc:
72
+ chk_conn
73
+ @hibernate_session.create_criteria(entity_name).list.to_a
74
+ end
75
+
76
+ # LOW LEVEL - shouldn't be used except by Ribs
77
+ def update_obj(obj) # :nodoc:
76
78
  chk_conn
77
79
  tx = @hibernate_session.begin_transaction
78
- if obj.__ribs_meat.persistent
79
- @hibernate_session.update(obj)
80
- else
81
- @hibernate_session.save(obj)
82
- obj.__ribs_meat.persistent = true
83
- end
80
+ @hibernate_session.update(obj)
84
81
  tx.commit
85
82
  obj
86
83
  end
87
84
 
85
+ # LOW LEVEL - shouldn't be used except by Ribs
86
+ def insert_obj(obj) # :nodoc:
87
+ chk_conn
88
+ tx = @hibernate_session.begin_transaction
89
+ @hibernate_session.save(obj)
90
+ tx.commit
91
+ obj
92
+ end
93
+
88
94
  # LOW LEVEL - shouldn't be used except by Ribs
89
95
  def delete(obj) # :nodoc:
90
96
  chk_conn
@@ -0,0 +1,243 @@
1
+ module Ribs
2
+ # Gets a Repository object for the object in question. If the object
3
+ # is a class, the repository returned will be a Repository::Class,
4
+ # otherwise it will be a Repository::Instance. Specifically, what
5
+ # you will get for the class FooBar will be the class
6
+ # Ribs::Repository::DB_default::FooBar and you will get an instance
7
+ # of that class if the object is an instance of FooBar. This allows
8
+ # you to add specific functionality to these repositories. The
9
+ # class Ribs::Repository::DB_default::FooBar will also include
10
+ # Ribs::Repository::FooBar and extend
11
+ # Ribs::Repository::FooBar::ClassMethods so you can add behavior to
12
+ # these that map over all databases.
13
+ def self.Repository(obj, db = :default)
14
+ db_name = "DB_#{db}"
15
+ model_type = case obj
16
+ when Class
17
+ obj
18
+ else
19
+ obj.class
20
+ end
21
+
22
+ dbmod = Ribs::Repository::const_get(db_name)
23
+ name = model_type.name.split(/::/).join("_")
24
+
25
+ if !dbmod.constants.include?(name)
26
+ Repository::create_repository(name, dbmod)
27
+ end
28
+
29
+ cls = dbmod::const_get(name.to_sym)
30
+ Ribs::Repository.ensure_repository(name, cls, model_type, db)
31
+
32
+ return cls if obj.kind_of?(Class)
33
+
34
+ ret = cls.allocate
35
+ ret.send :initialize
36
+ ret.instance_variable_set :@database, db
37
+ ret.instance_variable_set :@model, obj
38
+ ret
39
+ end
40
+
41
+ # A Repository is the main gateway into all functionality in
42
+ # Ribs. This is where you send your objects to live in the DB, etc.
43
+ #
44
+ # A Repository is a combination implementation of both Data Mapper and Repository.
45
+ module Repository
46
+ # The ClassMethods are everything that's available when getting
47
+ # the repository for a specific model class. It includes most of
48
+ # the things you'd expect to do on the class itself in
49
+ # ActiveRecord - stuff like finders, creators and things like
50
+ # that.
51
+ module ClassMethods
52
+ include Repository
53
+
54
+ # Get the meta data for this model
55
+ def metadata
56
+ @metadata
57
+ end
58
+
59
+ # Define accessors for this model
60
+ def define_accessors
61
+ self.metadata.properties_and_identity.each do |name, _|
62
+ self.model.send :attr_accessor, name.downcase
63
+ end
64
+ end
65
+
66
+ # Makes a specific instance of this class be marked persistent
67
+ def persistent(obj)
68
+ (@persistent ||= {})[obj.object_id] = true
69
+ end
70
+
71
+ # Checks if a specific instance is marked as persistent
72
+ def persistent?(obj)
73
+ @persistent && @persistent[obj.object_id]
74
+ end
75
+
76
+ # Makes a specific instance of this class be marked destroyed
77
+ def destroyed(obj)
78
+ (@destroyed ||= {})[obj.object_id] = true
79
+ end
80
+
81
+ # Checks if a specific instance is marked as destroyed
82
+ def destroyed?(obj)
83
+ @destroyed && @destroyed[obj.object_id]
84
+ end
85
+
86
+ # Create a new instance of this model object, optionally setting
87
+ # properties based on +attrs+.
88
+ def new(attrs = {})
89
+ obj = self.model.new
90
+ attrs.each do |k,v|
91
+ obj.send("#{k}=", v)
92
+ end
93
+ obj
94
+ end
95
+
96
+ # First creates a model object based on the values in +attrs+ and
97
+ # then saves this to the database directly.
98
+ def create(attrs = {})
99
+ val = new(attrs)
100
+ R(val, self.database).save
101
+ val
102
+ end
103
+
104
+ # Returns all instances for the current model
105
+ def all
106
+ Ribs.with_handle(self.database) do |h|
107
+ h.all(self.metadata.persistent_class.entity_name)
108
+ end
109
+ end
110
+
111
+ # Will get the instance with +id+ or return nil if no such entity
112
+ # exists.
113
+ def get(id)
114
+ Ribs.with_handle(self.database) do |h|
115
+ h.get(self.metadata.persistent_class.entity_name, id)
116
+ end
117
+ end
118
+
119
+ # Destroys the model with the id +id+.
120
+ def destroy(id)
121
+ Ribs.with_handle(self.database) do |h|
122
+ h.delete(get(id))
123
+ end
124
+ end
125
+ end
126
+
127
+ # InstanceMethods are those that become available when you the
128
+ # repository for a specific instance. This allows things like
129
+ # saving and destroying a specific instance.
130
+ module InstanceMethods
131
+ include Repository
132
+
133
+ # Get the meta data for this model
134
+ def metadata
135
+ self.class.metadata
136
+ end
137
+
138
+ # Removes this instance from the database.
139
+ def destroy!
140
+ self.class.destroyed(self.model)
141
+ Ribs.with_handle(self.database) do |h|
142
+ h.delete(self.model)
143
+ end
144
+ end
145
+
146
+ # Saves this instance to the database. If the instance already
147
+ # exists, it will update the database, otherwise it will create
148
+ # it.
149
+ def save
150
+ Ribs.with_handle(self.database) do |h|
151
+ if self.class.persistent?(self.model)
152
+ h.update_obj(self.model)
153
+ else
154
+ h.insert_obj(self.model)
155
+ self.class.persistent(self.model)
156
+ end
157
+ end
158
+ self.model
159
+ end
160
+ end
161
+
162
+ # Every repository is tied to a specific database
163
+ attr_reader :database
164
+ # Every repository is tied to a specific model. This is either a
165
+ # class or an instance of a class
166
+ attr_reader :model
167
+
168
+ class << self
169
+ # Makes sure the class sent in is actually a repostiroy. It
170
+ # checks all invariants for a Repository and makes them true if
171
+ # they aren't already.
172
+ def ensure_repository(name, cls, real, db)
173
+ unless cls.kind_of?(Repository)
174
+ mod1 = if Repository.constants.include?(name)
175
+ Repository.const_get(name)
176
+ else
177
+ mod = Module.new
178
+ Repository.const_set(name, mod)
179
+ mod
180
+ end
181
+
182
+ unless mod1.kind_of?(Repository)
183
+ mod1.send :include, Repository::InstanceMethods
184
+ end
185
+
186
+ unless mod1.constants.include?("ClassMethods")
187
+ mod1.const_set(:ClassMethods, Module.new)
188
+ end
189
+
190
+ cls.send :include, mod1
191
+ cls.send :extend, mod1.const_get(:ClassMethods)
192
+ cls.send :extend, Repository::ClassMethods
193
+ cls.instance_variable_set :@database, db
194
+ cls.instance_variable_set :@model, real
195
+ cls.instance_variable_set :@metadata, Ribs::metadata_for(db, real, cls)
196
+ end
197
+ end
198
+
199
+ # Create a repository for a model inside a database
200
+ def create_repository(name, dbmod)
201
+ c = Class.new
202
+ mod1 = if Repository.constants.include?(name)
203
+ Repository.const_get(name)
204
+ else
205
+ mod = Module.new
206
+ Repository.const_set(name, mod)
207
+ mod
208
+ end
209
+ dbmod.const_set name, c
210
+ end
211
+
212
+ # Dynamically create new database modules
213
+ def const_missing(name)
214
+ if /^DB_(.*?)$/ =~ name.to_s
215
+ db_name = $1
216
+ mod = Module.new
217
+ mod.instance_variable_set :@database_name, db_name.to_sym
218
+ const_set name, mod
219
+ mod
220
+ else
221
+ super
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ module Kernel
229
+ # Gets a Repository object for the object in question. If the object
230
+ # is a class, the repository returned will be a Repository::Class,
231
+ # otherwise it will be a Repository::Instance. Specifically, what
232
+ # you will get for the class FooBar will be the class
233
+ # Ribs::Repository::DB_default::FooBar and you will get an instance
234
+ # of that class if the object is an instance of FooBar. This allows
235
+ # you to add specific functionality to these repositories. The
236
+ # class Ribs::Repository::DB_default::FooBar will also include
237
+ # Ribs::Repository::FooBar and extend
238
+ # Ribs::Repository::FooBar::ClassMethods so you can add behavior to
239
+ # these that map over all databases.
240
+ def R(obj, db=:default)
241
+ Ribs::Repository(obj, db)
242
+ end
243
+ end
@@ -0,0 +1,100 @@
1
+ module Ribs
2
+ # Contains the mapping data that gets created when calling the
3
+ # {Ribs!} method.
4
+ class Rib
5
+ # The proxy object returned when a property name is used in a call
6
+ # to Rib without any parameters.
7
+ class RibColumn
8
+ # Sets the name and all the reference values
9
+ def initialize(name, columns, primary_keys, to_avoid, default_values)
10
+ @name, @columns, @primary_keys, @to_avoid, @default_values =
11
+ name.to_s, columns, primary_keys, to_avoid, default_values
12
+ end
13
+
14
+ # This name is a primary key
15
+ def primary_key!
16
+ @primary_keys << @name
17
+ end
18
+
19
+ # This name should be avoided in the database
20
+ def avoid!
21
+ @to_avoid << @name.downcase
22
+ end
23
+
24
+ # This name is mapped to a specific column
25
+ def column=(col)
26
+ @columns[@name] = [col.to_s, {}]
27
+ end
28
+ end
29
+
30
+ # Value object that contains references to the Rib data
31
+ class ColumnData
32
+ # List of all the columns defined
33
+ attr_reader :columns
34
+ # List of all primary keys defined
35
+ attr_reader :primary_keys
36
+ # List of all columns to avoid
37
+ attr_reader :to_avoid
38
+ # List of default values for columns
39
+ attr_reader :default_values
40
+
41
+ # Sets all values
42
+ def initialize(columns, primary_keys, to_avoid, default_values)
43
+ @columns, @primary_keys, @to_avoid, @default_values = columns, primary_keys, to_avoid, default_values
44
+ end
45
+ end
46
+
47
+ # These are the only methods left alone - this becomes a blank
48
+ # slate, mostly.
49
+ METHODS_TO_LEAVE_ALONE = ['__id__', '__send__']
50
+ undef_method *(instance_methods - METHODS_TO_LEAVE_ALONE)
51
+
52
+ # Create all value parts
53
+ def initialize
54
+ @columns = { }
55
+ @primary_keys = []
56
+ @to_avoid = []
57
+ @default_values = { }
58
+ end
59
+
60
+ # Returns a reference object that allow access to the resulting
61
+ # data objects
62
+ def __column_data__
63
+ ColumnData.new(@columns, @primary_keys, @to_avoid, @default_values)
64
+ end
65
+
66
+ # Handles property names. The only ones that aren't possibly to
67
+ # use is "initialize", "__column_data__", "__id__",
68
+ # "method_missing" and "__send__". Everything else is
69
+ # kosher. ... Maybe there should be a way of getting those last
70
+ # ones too...
71
+ #
72
+ # If no arguments are supplied, will return a RibColumn
73
+ def method_missing(name, *args, &block)
74
+ if args.empty?
75
+ RibColumn.new(name, @columns, @primary_keys, @to_avoid, @default_values)
76
+ else
77
+ hsh = args.grep(Hash).first || {}
78
+ args.grep(Symbol).each do |ss|
79
+ hsh[ss] = true
80
+ end
81
+
82
+ hsh = {:column => name}.merge(hsh)
83
+
84
+ if hsh[:primary_key]
85
+ @primary_keys << name.to_s
86
+ end
87
+
88
+ if hsh[:avoid]
89
+ @to_avoid << name.to_s.downcase
90
+ if hsh[:default]
91
+ @default_values[name.to_s.downcase] = hsh[:default]
92
+ end
93
+ end
94
+
95
+ @columns[name.to_s] = [hsh[:column].to_s, hsh]
96
+ nil
97
+ end
98
+ end
99
+ end
100
+ end