og 0.6.0 → 0.7.0

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