og 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.og CHANGED
@@ -1,15 +1,16 @@
1
1
  = Og 0.6.0
2
2
 
3
- Og (ObjectGraph) is an efficient, yet simple object-relational mapping
3
+ Nitro integrates the Og (ObjectGraph) object-relational mapping
4
4
  library. Og provides transparent serialization of object graphs to a RDBMS
5
5
  backend. Unlike other similar libraries Og maps standard Ruby
6
6
  objects to SQL tables and not vice versa. Og provides a meta language
7
7
  to describe the relations between objects, a flexible and intuitive api
8
8
  for querieng the database, raw access to the SQL language if needed
9
9
  (for example to fine tune the automatically generated SQL tables, or
10
- for custom queries), suports deserialization to Ruby objects or tuples
10
+ for custom queries), suports deserialization to Ruby objects or tuples,
11
+ automatically generates join tables for many_to_many relations
11
12
  and provides a collection of usefull Mixins to synthesize common
12
- Objects.
13
+ Entities.
13
14
 
14
15
  Og is a combination of the best features of Active Record and the
15
16
  former O-R mapping library included in Nitro (NDB). Adapters for
data/RELEASES.og CHANGED
@@ -1,3 +1,18 @@
1
+ == Version 0.7 was released on 27/12/2004.
2
+
3
+ A snapshot of the latest code. Many fixes and new features result
4
+ in a more mature product. Many thanks to the ruby hackers that sent
5
+ suggestions and patches used in this release!
6
+
7
+ Most notable additions:
8
+
9
+ * Totaly recoded prop_accessor mechanism, avoids polution of the Module
10
+ class.
11
+ * prop_accessors for Modules, allows synthesizing of managed objects
12
+ from Mixins.
13
+ * new automatically generated methods in Og.
14
+ * MockDatabase leverages the FlexMock object for easier unit testing.
15
+
1
16
  == Version 0.6 was released on 13/12/2004.
2
17
 
3
18
  This is a preview release, the api for the new features is not
data/Rakefile ADDED
@@ -0,0 +1,135 @@
1
+ # code:
2
+ # * George Moschovitis <gm@navel.gr>
3
+ #
4
+ # (c) 2004 Navel, all rights reserved.
5
+ # $Id: connection.rb 71 2004-10-18 10:50:22Z gmosx $
6
+
7
+ require "rake"
8
+ require "rake/rdoctask"
9
+ require "rake/gempackagetask"
10
+ require "rake/testtask"
11
+ require "rubygems"
12
+
13
+ #project = 'nitro'
14
+ project = 'og'
15
+
16
+ # ----------------------------------------------------------------------
17
+
18
+ desc "Default Task"
19
+ task :default => :package
20
+
21
+ # ----------------------------------------------------------------------
22
+
23
+ # Run the tests
24
+
25
+ Rake::TestTask.new do |t|
26
+ t.libs << "test"
27
+ t.test_files = FileList["test/**/tc*.rb"].exclude("**/tc*og*.rb")
28
+ t.verbose = true
29
+ end
30
+
31
+ # Run all tests
32
+
33
+ Rake::TestTask.new(:test_all) do |t|
34
+ t.libs << "test"
35
+ t.test_files = FileList["test/**/tc*.rb"]
36
+ t.verbose = true
37
+ end
38
+
39
+ # ----------------------------------------------------------------------
40
+
41
+ Rake::RDocTask.new do |rd|
42
+ rd.main = "README"
43
+ rd.rdoc_dir = "rdoc"
44
+ rd.rdoc_files.include("README", "lib/**/*.rb")
45
+ end
46
+
47
+ # ----------------------------------------------------------------------
48
+
49
+ if 'nitro' == project
50
+
51
+ # Nitro GemSpec
52
+ #
53
+ require "lib/nitro/version"
54
+
55
+ PKG_VERSION = $srv_version
56
+ PKG_FILES = FileList[
57
+ "[A-Z]*", "{bin,benchmark,etc,ext,examples,doc,lib,test,vendor}/**/*"
58
+ # "examples/*.rb"
59
+ ].exclude(".svn/**/*").exclude("*.og").exclude("**/*.log")
60
+
61
+ spec = Gem::Specification.new do |s|
62
+ s.name = "nitro"
63
+ s.version = PKG_VERSION
64
+ s.summary = "Web Engine"
65
+ s.description = "An efficient, yet simple engine for Web Applications"
66
+ # s.add_dependency("postgres-pr", ">= 0.3.0")
67
+ # s.add_dependency("postgres", ">= 0.7.1")
68
+ # s.add_dependency("extensions", ">= 0.5")
69
+ s.required_ruby_version = ">= 1.8.1"
70
+ s.files = PKG_FILES.to_a
71
+ s.require_path = "lib"
72
+ s.autorequire = "nitro"
73
+ s.has_rdoc = true
74
+ s.extra_rdoc_files = FileList["[A-Z]*"].exclude("*.og").to_a
75
+ s.rdoc_options << "--main" << "README" << "--title" << "Nitro Documentation"
76
+ s.test_files = []
77
+ s.bindir = "bin"
78
+ s.author = "George Moschovitis"
79
+ s.email = "gm@navel.gr"
80
+ s.homepage = "http://www.navel.gr/nitro"
81
+ s.rubyforge_project = "nitro"
82
+ end
83
+
84
+ else
85
+
86
+ # Og stand-alone GemSpec
87
+ #
88
+ require "lib/og/version"
89
+
90
+ PKG_VERSION = $og_version
91
+ PKG_FILES = FileList[
92
+ "README.og", "RELEASES.og", "LICENSE", "AUTHORS", "Rakefile", "ChangeLog*",
93
+ "examples/og/*", "lib/glue.rb", "lib/glue/**/*", "lib/og/**/*", "lib/og.rb",
94
+ "test/*og*.rb", "test/og/*"
95
+ ].exclude(".svn/**/*").exclude("**/*.log")
96
+
97
+ spec = Gem::Specification.new do |s|
98
+ s.name = 'og'
99
+ s.version = PKG_VERSION
100
+ s.summary = 'Og (ObjectGraph)'
101
+ s.description = 'An efficient and transparent Object-Relational mapping library'
102
+ # s.add_dependency("postgres", ">= 0.7.1")
103
+ # s.add_dependency("extensions", ">= 0.5")
104
+ # s.add_dependency("builder")
105
+ s.required_ruby_version = ">= 1.8.1"
106
+ s.files = PKG_FILES.to_a
107
+ s.require_path = "lib"
108
+ s.autorequire = "og"
109
+ s.has_rdoc = true
110
+ s.extra_rdoc_files = FileList["README.og", "RELEASES.og", "LICENSE", "AUTHORS"].to_a
111
+ s.rdoc_options << "--main" << "README.og" << "--title" << "Og Documentation"
112
+ s.test_files = []
113
+ s.bindir = "bin"
114
+ s.author = "George Moschovitis"
115
+ s.email = "gm@navel.gr"
116
+ s.homepage = "http://www.navel.gr/og"
117
+ s.rubyforge_project = "og-rml"
118
+ end
119
+
120
+ end
121
+
122
+ Rake::GemPackageTask.new(spec) do |pkg|
123
+ pkg.package_dir = "dist"
124
+ pkg.need_zip = true
125
+ pkg.need_tar = true
126
+ end
127
+
128
+ # ----------------------------------------------------------------------
129
+
130
+ desc "Install the gem"
131
+ task :install => :repackage do
132
+ sh "gem install --local --no-rdoc dist/nitro-#{PKG_VERSION}.gem"
133
+ end
134
+
135
+ # vim: ft=ruby
data/examples/og/README CHANGED
@@ -1,4 +1,11 @@
1
1
  = Og Example
2
2
 
3
+ == run.rb
4
+
3
5
  A simple example that demonstrates some Og features. The example
4
6
  automatically creates a 'test' database.
7
+
8
+ == mock_example.rb
9
+
10
+ Demonstrates how easily the Og infrastructure can be mocked,
11
+ for easy test unit writing.
@@ -0,0 +1,58 @@
1
+ # = Og Mocking Example
2
+ #
3
+ # A simple example to demonstrate how to mock Og.
4
+ # Very useful in test units.
5
+ #
6
+ # code:
7
+ # * George Moschovitis <gm@navel.gr>
8
+ #
9
+ # (c) 2004 Navel, all rights reserved.
10
+ # $Id: run.rb 185 2004-12-10 13:29:09Z gmosx $
11
+
12
+ $LOAD_PATH.unshift '../../lib'
13
+
14
+ require 'rubygems'
15
+ require 'flexmock'
16
+ require 'og'
17
+ require 'og/mock'
18
+ require 'logger'
19
+
20
+ $log = Logger.new($stderr)
21
+
22
+ class Article
23
+ prop_accessor :body, String
24
+
25
+ def initialize(body = nil)
26
+ @body = body
27
+ end
28
+ end
29
+
30
+ class SimpleTest < Test::Unit::TestCase
31
+
32
+ def setup
33
+ $og = Og::MockDatabase.new
34
+ end
35
+
36
+ def teardown
37
+ $og = nil
38
+ end
39
+
40
+ def test_me
41
+ mocks = [
42
+ Article.new('body1'),
43
+ Article.new('body2'),
44
+ Article.new('body3')
45
+ ]
46
+ $og.mock_handle(:load_all) { |klass, extrasql| mocks }
47
+
48
+ # differnt ways to call the mocked method...
49
+ puts 'Here are the articles:', Article.all
50
+ puts 'Here are the articles:', Article.load_all
51
+ puts 'Here are the articles:', $og.load_all(Article)
52
+
53
+ # 3 times called
54
+ assert_equal(3, $og.mock_count(:load_all))
55
+ end
56
+
57
+ end
58
+
data/examples/og/run.rb CHANGED
@@ -6,7 +6,7 @@
6
6
  # * George Moschovitis <gm@navel.gr>
7
7
  #
8
8
  # (c) 2004 Navel, all rights reserved.
9
- # $Id: run.rb 185 2004-12-10 13:29:09Z gmosx $
9
+ # $Id: run.rb 198 2004-12-22 11:26:59Z gmosx $
10
10
 
11
11
  $:.unshift "../../lib"
12
12
 
@@ -103,7 +103,7 @@ class Category
103
103
  prop_accessor :body, String
104
104
 
105
105
  # define a 'many to many' relation.
106
- many_to_many Article
106
+ many_to_many :articles, Article
107
107
 
108
108
  def initialize(title = nil)
109
109
  @title = title
@@ -141,7 +141,6 @@ end
141
141
  # Initialize a logger.
142
142
 
143
143
  $log = Logger.new(STDERR);
144
-
145
144
  # Og configuration.
146
145
  config = {
147
146
  :address => "localhost",
@@ -199,9 +198,11 @@ c2.article_oid = a1.oid
199
198
  # managed objects).
200
199
  $og << c2
201
200
 
201
+ # an alternative (easier and cooler) way to add children in a
202
+ # has_many relation:
202
203
  c3 = ArticleComment.new("Comment 3")
203
- c3.article = a1
204
- c3.save!
204
+ # add_comment is automatically added by Og.
205
+ a1.add_comment(c3)
205
206
 
206
207
  puts "\n\n"
207
208
  puts "* Print all all comments for article 1:"
@@ -287,4 +288,7 @@ puts '---'
287
288
 
288
289
  article.categories.each { |c| puts c.title }
289
290
 
291
+ # create and save the article in one step.
292
+ article = Article.create("title", "body")
290
293
 
294
+ puts '--', article.oid
data/lib/glue/property.rb CHANGED
@@ -2,10 +2,9 @@
2
2
  # * George Moschovitis <gm@navel.gr>
3
3
  # design:
4
4
  # * Anastastios Koutoumanos <ak@navel.gr>
5
- # * Elias Karakoulakis <ekarak@ktismata.com>
6
5
  #
7
6
  # (c) 2004 Navel, all rights reserved.
8
- # $Id: property.rb 185 2004-12-10 13:29:09Z gmosx $
7
+ # $Id: property.rb 200 2004-12-27 11:24:41Z gmosx $
9
8
 
10
9
  require "glue/array"
11
10
  require "glue/hash"
@@ -25,10 +24,8 @@ module G
25
24
  #
26
25
  #--
27
26
  # TODO:
28
- # Inject only the really needd methods into Module.
29
27
  # Perhaps a sync is needed in evals (!!!!)
30
28
  #++
31
- #
32
29
  class Property
33
30
  # the symbol of the property
34
31
  attr_accessor :symbol
@@ -36,7 +33,7 @@ class Property
36
33
  attr_accessor :name
37
34
  # the class of the property
38
35
  attr_accessor :klass
39
- # additional metadata (like sql declaratio, sql index, etc)
36
+ # additional metadata (like sql declaration, sql index, etc)
40
37
  attr_accessor :meta
41
38
 
42
39
  def initialize(symbol, klass, meta = {})
@@ -52,6 +49,119 @@ class Property
52
49
  def to_s
53
50
  return name
54
51
  end
52
+
53
+ end
54
+
55
+ # = PropertyUtils
56
+ #
57
+ # A collection of Property related utility methods.
58
+ #
59
+ module PropertyUtils
60
+
61
+ # Add accessors to the properties to the given target
62
+ # (Module or Class). For simplicity also create the
63
+ # meta accessors.
64
+ #--
65
+ # gmosx: Perhaps we 'll optimize this in the future.
66
+ #++
67
+ def self.enchant(target)
68
+ unless target.singleton_methods.include?('__props')
69
+ target.module_eval <<-"end_eval", __FILE__, __LINE__
70
+ @@__meta = G::SafeHash.new
71
+ @@__props = G::SafeArray.new
72
+
73
+ def self.__props
74
+ @@__props
75
+ end
76
+
77
+ def self.__props=(props)
78
+ @@__props = props
79
+ end
80
+
81
+ def self.__meta
82
+ @@__meta
83
+ end
84
+
85
+ def self.__meta=(meta)
86
+ @@__meta = meta
87
+ end
88
+ end_eval
89
+ end
90
+ end
91
+
92
+ # Copy properties from src (Module or Class) to dest.
93
+ #
94
+ def self.copy_props(src, dest)
95
+ src.__props.each do |p|
96
+ add_prop(dest, p)
97
+ end
98
+
99
+ # copy the metadata.
100
+ src.__meta.each do |k, val|
101
+ val.each { |v| dest.meta(k, v) } if val
102
+ end
103
+ end
104
+
105
+ # Add the property to the target (Class or Module)
106
+ #
107
+ def self.add_prop(target, prop)
108
+ if idx = target.__props.index(prop)
109
+ # override in case of duplicates. Keep the order of the props.
110
+ target.__props[idx] = prop
111
+ else
112
+ target.__props << prop
113
+ end
114
+
115
+ # Precompile the property read/write methods
116
+
117
+ s, klass = prop.symbol, prop.klass
118
+
119
+ if prop.meta[:reader]
120
+ target.module_eval %{
121
+ def #{s}
122
+ return @#{s}
123
+ end
124
+ }
125
+ end
126
+
127
+ # gmosx: __force_xxx reuses xxx= to allow for easier
128
+ # overrides.
129
+ if prop.meta[:writer]
130
+ target.module_eval %{
131
+ #{prop_setter(prop)}
132
+
133
+ def __force_#{s}(val)
134
+ self.#{s}=(} + case klass.name
135
+ when Fixnum.name
136
+ "val.to_i()"
137
+ when String.name
138
+ "val.to_s()"
139
+ when Float.name
140
+ "val.to_f()"
141
+ when Time.name
142
+ "Time.parse(val.to_s())"
143
+ when TrueClass.name, FalseClass.name
144
+ "val.to_i() > 0"
145
+ else
146
+ "val"
147
+ end + %{)
148
+ end
149
+ }
150
+ end
151
+ end
152
+
153
+ # Generates the property setter code. Can be overriden
154
+ # to support extra functionality (example: markup)
155
+ #
156
+ def self.prop_setter(prop)
157
+ s = prop.symbol
158
+ %{
159
+ def #{s}=(val)
160
+ @#{s} = val
161
+ end
162
+ }
163
+ end
164
+
55
165
  end
56
166
 
57
167
  end # module
@@ -90,36 +200,55 @@ class Module
90
200
  end
91
201
  end
92
202
 
93
- unless self.methods.include?("__props")
94
- eval %{
95
- # Properties
96
- # An array is used to enforce order.
97
- def __props
98
- @__props
99
- end
203
+ G::PropertyUtils.enchant(self)
100
204
 
101
- def __props=(props)
102
- @__props = props
205
+ if self.is_a?(Class)
206
+
207
+ # Add some extra code to append features to
208
+ # subclasses.
209
+ self.module_eval <<-"end_eval", __FILE__, __LINE__
210
+
211
+ def self.inherited(sub)
212
+ G::PropertyUtils.enchant(sub)
213
+ G::PropertyUtils.copy_props(self, sub)
214
+ # gmosx: We have to define @@__props first to avoid reusing
215
+ # the hash from the module. super must stay at the end.
216
+ super
103
217
  end
104
-
105
- def __meta
106
- @__meta
218
+
219
+ end_eval
220
+
221
+ else
222
+
223
+ # Add some extra code for modules to append
224
+ # their features to classes that include it.
225
+ self.module_eval <<-"end_eval", __FILE__, __LINE__
226
+
227
+ def self.append_features(base)
228
+ G::PropertyUtils.enchant(base)
229
+ G::PropertyUtils.copy_props(self, base)
230
+ # gmosx: We have to define @@__props first to avoid reusing
231
+ # the hash from the module. super must stay at the end.
232
+ super
107
233
  end
108
234
 
109
- def __meta=(meta)
110
- @__meta = meta
111
- end
112
- }
235
+ end_eval
236
+
113
237
  end
114
-
115
- @__props = G::SafeArray.new() unless @__props
116
-
238
+
117
239
  property = G::Property.new(symbol, klass, meta)
118
240
 
119
241
  reader = meta[:reader] || true
120
242
  writer = writer || meta[:writer] || false
121
-
122
- __add_prop(property, reader, writer)
243
+
244
+ meta[:reader] = true if meta[:reader].nil?
245
+ if defined?(writer)
246
+ meta[:writer] = writer
247
+ else
248
+ meta[:writer] = true if meta[:writer].nil?
249
+ end
250
+
251
+ G::PropertyUtils.add_prop(self, property)
123
252
  end
124
253
 
125
254
  # Helper method. Accepts a collection of symbols and generates
@@ -218,90 +347,20 @@ class Module
218
347
  end
219
348
  end
220
349
 
221
- # Add the property
222
- #
223
- def __add_prop(prop, reader = true, writer = true)
224
- if idx = @__props.index(prop)
225
- # override in case of duplicates. Keep the order of the props.
226
- @__props[idx] = prop
227
- else
228
- @__props << prop
229
- end
230
-
231
- # Precompile the property read/write methods
232
-
233
- s, klass = prop.symbol, prop.klass
234
-
235
- if reader
236
- module_eval %{
237
- def #{s}
238
- return @#{s}
239
- end
240
- }
241
- end
242
-
243
- # gmosx: __force_xxx reuses xxx= to allow for easier
244
- # overrides.
245
- if writer
246
- module_eval %{
247
- def #{s}=(val)
248
- @#{s} = val
249
- end
250
-
251
- def __force_#{s}(val)
252
- self.#{s}=(} + case klass.name
253
- when Fixnum.name
254
- "val.to_i()"
255
- when String.name
256
- "val.to_s()"
257
- when Float.name
258
- "val.to_f()"
259
- when Time.name
260
- "Time.parse(val.to_s())"
261
- when TrueClass.name, FalseClass.name
262
- "val.to_i() > 0"
263
- else
264
- "val"
265
- end + %{)
266
- end
267
- }
268
- end
269
- end
270
350
 
271
- # Attach metadata
272
- #
351
+ # Attach metadata.
352
+ # Guard against duplicates, no need to keep order.
353
+ # This method uses closures :)
354
+ #--
355
+ # gmosx: crappy implementation, recode.
356
+ #++
273
357
  def meta(key, val)
274
- @__meta = G::SafeHash.new unless @__meta
275
-
276
- @__meta[key] = [] unless @__meta[key]
277
-
278
- # guard against duplicates, no need to keep order.
279
- @__meta[key].delete_if { |v| val == v }
280
- @__meta[key] << val
358
+ self.module_eval <<-"end_eval", __FILE__, __LINE__
359
+ @@__meta[key] ||= []
360
+ @@__meta[key].delete_if { |v| val == v }
361
+ @@__meta[key] << val
362
+ end_eval
281
363
  end
282
-
283
- # This method is typically called before including other
284
- # modules to preserve properties order.
285
- #
286
- def inherit_meta(mod = superclass)
287
- # concat props.
288
- if mod.__props
289
- @__props = G::SafeArray.new unless @__props
290
-
291
- mod.__props.each { |p|
292
- __add_prop(p)
293
- }
294
- end
295
364
 
296
- # concat metadata
297
- if mod.__meta
298
- mod.__meta.each { |k, val|
299
- val.each { |v|
300
- meta(k, v)
301
- } if val
302
- }
303
- end
304
- end
305
-
306
365
  end
307
366