ribs 0.0.1 → 0.0.2

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