nitro 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/ChangeLog +150 -0
  2. data/README +1 -1
  3. data/RELEASES +89 -0
  4. data/Rakefile +3 -3
  5. data/{AUTHORS → doc/AUTHORS} +0 -0
  6. data/{LICENSE → doc/LICENSE} +0 -0
  7. data/doc/bugs.txt +2 -1
  8. data/examples/README.windows +2 -2
  9. data/examples/blog/lib/blog/controller.rb +9 -8
  10. data/examples/blog/log/apache.error_log +71 -0
  11. data/{lib/xsl → examples/blog/root}/base.xsl +0 -0
  12. data/examples/blog/root/error.xhtml +56 -0
  13. data/examples/blog/root/index.xhtml +2 -2
  14. data/examples/blog/root/recent_posts.xhtml +1 -1
  15. data/examples/blog/root/style.xsl +4 -4
  16. data/examples/blog/run.rb +1 -2
  17. data/examples/no_xsl_blog/root/index.xhtml +2 -2
  18. data/examples/no_xsl_blog/root/recent_posts.xhtml +1 -1
  19. data/examples/why_wiki/run.rb +19 -19
  20. data/lib/nitro.rb +2 -21
  21. data/lib/nitro/adapters/webrick.rb +19 -3
  22. data/lib/nitro/context.rb +15 -1
  23. data/lib/nitro/controller.rb +84 -49
  24. data/lib/nitro/dispatcher.rb +30 -6
  25. data/lib/nitro/markup.rb +4 -2
  26. data/lib/nitro/render.rb +15 -11
  27. data/lib/nitro/routing.rb +33 -0
  28. data/lib/nitro/runner.rb +38 -3
  29. data/lib/nitro/scaffold.rb +7 -4
  30. data/lib/nitro/shaders.rb +11 -4
  31. data/lib/nitro/template.rb +140 -0
  32. data/lib/og.rb +25 -11
  33. data/lib/og/adapter.rb +141 -7
  34. data/lib/og/adapters/mysql.rb +41 -3
  35. data/lib/og/adapters/oracle.rb +4 -3
  36. data/lib/og/adapters/psql.rb +3 -3
  37. data/lib/og/adapters/sqlite.rb +3 -3
  38. data/lib/og/connection.rb +5 -1
  39. data/lib/og/database.rb +26 -12
  40. data/lib/og/enchant.rb +50 -16
  41. data/lib/og/meta.rb +15 -15
  42. data/lib/og/observer.rb +53 -0
  43. data/test/glue/tc_property_type_checking.rb +3 -0
  44. data/test/nitro/tc_controller.rb +1 -1
  45. data/test/nitro/tc_dispatcher.rb +1 -1
  46. data/test/nitro/tc_template.rb +32 -0
  47. data/test/og/tc_many_to_many.rb +62 -0
  48. data/test/og/tc_observer.rb +85 -0
  49. data/test/tc_og.rb +16 -2
  50. metadata +12 -14
  51. data/bin/cluster +0 -218
  52. data/examples/why_wiki/wiki.yml +0 -6
  53. data/examples/wiki.yml +0 -1
  54. data/lib/nitro/ui/select.rb +0 -40
  55. data/lib/nitro/ui/sitemap.rb +0 -183
  56. data/test/nitro/ui/tc_sitemap.rb +0 -37
data/lib/og.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: og.rb 266 2005-02-28 14:50:48Z gmosx $
3
+ # $Id: og.rb 270 2005-03-07 17:52:16Z gmosx $
4
4
 
5
5
  require 'glue'
6
6
  require 'glue/logger'
@@ -15,7 +15,7 @@ require 'glue/validation'
15
15
  # Og (ObjectGraph) is an efficient, yet simple Object-Relational
16
16
  # mapping library.
17
17
  #
18
- # == Features
18
+ # === Features
19
19
  #
20
20
  # The library provides the following features:
21
21
  #
@@ -30,12 +30,13 @@ require 'glue/validation'
30
30
  # + Thread safety.
31
31
  # + SQL transactions.
32
32
  # + Lifecycle callbacks.
33
+ # + Lifecycle observers.
33
34
  # + Transparent support for cascading deletes for all backends.
34
35
  # + Hierarchical structures (preorder traversal, materialized paths)
35
36
  # + Works safely as part of distributed application.
36
37
  # + Simple implementation.
37
38
  #
38
- # == Meta language
39
+ # === Meta language
39
40
  #
40
41
  # primary_key :pid (NOT IMPLEMENTED)
41
42
  # name_key :name (NOT IMPLEMENTED)
@@ -44,13 +45,24 @@ require 'glue/validation'
44
45
  # many_to_many Role, :roles
45
46
  # sql_index :pid
46
47
  #
47
- # == Design
48
+ # === Property Metadata
49
+ #
50
+ # Og defines, reserves and uses the following property
51
+ # metadata types:
52
+ #
53
+ # [+:sql_index+]
54
+ # Create an sql index for this property.
55
+ #
56
+ # [+:unique+]
57
+ # This value of the property must be unique.
58
+ #
59
+ # [+:name_key+]
60
+ # This property is used as name-key.
61
+ #
62
+ # === Design
48
63
  #
49
64
  # Keep the main classes backend agnostic.
50
- #--
51
- # Try to make the methods work with oids. Do NOT implement descendants
52
- # use a root id (rid).
53
- #++
65
+ #
54
66
  # For class ids we use the name instead of a hash. Class ids are
55
67
  # typically not used in querys, they are stored for completeness.
56
68
  # If we store a hash we cannot reclaim the class thus invalidating
@@ -68,8 +80,10 @@ require 'glue/validation'
68
80
  # The og.xxx methods are more flexible and allow you to use
69
81
  # multiple databases for example.
70
82
  #
71
- # == Managed Objects Lifecycle Callbacks
83
+ # === Managed Objects Lifecycle Callbacks
72
84
  #
85
+ # * og_pre_read
86
+ # * og_post_read
73
87
  # * og_pre_insert
74
88
  # * og_post_insert
75
89
  # * og_pre_update
@@ -81,7 +95,7 @@ require 'glue/validation'
81
95
  # A class level callback is used for delete because typically you call
82
96
  # delete with an oid and not an object to avoid a deserialization.
83
97
  #
84
- # == Future
98
+ # === Future
85
99
  #
86
100
  # * Support prepared statements (pgsql)
87
101
  # * Support stored procedures (pgsql)
@@ -97,7 +111,7 @@ module Og
97
111
 
98
112
  # The version.
99
113
 
100
- Version = '0.11.0'
114
+ Version = '0.12.0'
101
115
 
102
116
  # Library path.
103
117
 
data/lib/og/adapter.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: adapter.rb 266 2005-02-28 14:50:48Z gmosx $
3
+ # $Id: adapter.rb 270 2005-03-07 17:52:16Z gmosx $
4
4
 
5
5
  require 'yaml'
6
6
  require 'singleton'
@@ -20,6 +20,11 @@ class Adapter
20
20
  # A mapping between Ruby and backend Datastore types.
21
21
 
22
22
  attr_accessor :typemap
23
+
24
+ # A map for casting Ruby types to SQL safe textual
25
+ # representations.
26
+
27
+ attr_accessor :typecast
23
28
 
24
29
  # Lookup the adapter instance from the adapter name.
25
30
 
@@ -29,6 +34,9 @@ class Adapter
29
34
  end
30
35
 
31
36
  def initialize
37
+ # The default mappings, should be valid for most
38
+ # RDBMS.
39
+
32
40
  @typemap = {
33
41
  Integer => 'integer',
34
42
  Fixnum => 'integer',
@@ -41,6 +49,19 @@ class Adapter
41
49
  Array => 'text',
42
50
  Hash => 'text'
43
51
  }
52
+
53
+ # The :s: is a marker that will be replaced with the
54
+ # actual value to be casted. The default parameter of
55
+ # the Hash handles all other types (Object, Array, etc)
56
+
57
+ @typecast = Hash.new("'#\{#{self.class}.escape(:s:.to_yaml)\}'").update(
58
+ Integer => "\#\{:s:\}",
59
+ Float => "\#\{:s:\}",
60
+ String => "'#\{#{self.class}.escape(:s:)\}'",
61
+ Time => "'#\{#{self.class}.timestamp(:s:)\}'",
62
+ Date => "'#\{#{self.class}.date(:s:)\}'",
63
+ TrueClass => "#\{:s: ? \"'t'\" : 'NULL' \}"
64
+ )
44
65
  end
45
66
 
46
67
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -105,7 +126,7 @@ class Adapter
105
126
  # Create a new connection to the backend.
106
127
 
107
128
  def new_connection(db)
108
- return Og::Connection.new(db)
129
+ return Connection.new(db)
109
130
  end
110
131
 
111
132
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -145,8 +166,8 @@ class Adapter
145
166
  # You may want to override this method to map an existing
146
167
  # database schema using Og.
147
168
 
148
- def self.join_table(klass1, klass2)
149
- "og_#{Og.table_prefix}j_#{encode(klass1)}_#{encode(klass2)}"
169
+ def self.join_table(klass1, klass2, field)
170
+ "og_#{Og.table_prefix}j_#{encode(klass1)}_#{encode(klass2)}_#{field}"
150
171
  end
151
172
 
152
173
  # Return an sql string evaluator for the property.
@@ -222,7 +243,10 @@ class Adapter
222
243
  if default = p.meta[:default]
223
244
  field << " DEFAULT #{default.inspect} NOT NULL"
224
245
  end
225
-
246
+
247
+ # set unique
248
+ field << " UNIQUE" if p.meta[:unique]
249
+
226
250
  # attach extra sql
227
251
  if extra_sql = p.meta[:extra_sql]
228
252
  field << " #{extra_sql}"
@@ -266,9 +290,19 @@ class Adapter
266
290
  end
267
291
 
268
292
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
269
- # :section: Managed object enchant methods
293
+ # :section: Precompile lifecycle methods.
270
294
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
271
295
 
296
+ # Precompile some code that gets executed all the time.
297
+ # Deletion code is not precompiled, because it is not used
298
+ # as frequently.
299
+
300
+ def eval_lifecycle_methods(klass, db)
301
+ eval_og_insert(klass, db)
302
+ eval_og_update(klass, db)
303
+ eval_og_read(klass, db)
304
+ end
305
+
272
306
  # Generate the property for oid.
273
307
 
274
308
  def eval_og_oid(klass)
@@ -281,6 +315,9 @@ class Adapter
281
315
  # The generated code sets the oid when inserting!
282
316
 
283
317
  def eval_og_insert(klass, db)
318
+
319
+ # Attach object callbacks.
320
+
284
321
  if klass.instance_methods.include?('og_pre_insert')
285
322
  pre_cb = 'og_pre_insert(conn);'
286
323
  else
@@ -300,7 +337,36 @@ class Adapter
300
337
  if klass.instance_methods.include?('og_post_insert_update')
301
338
  post_cb << 'og_post_insert_update(conn);'
302
339
  end
303
-
340
+
341
+ # Attach observers.
342
+
343
+ if observers = klass.__meta[:og_observers]
344
+ observers.each_with_index do |o, idx|
345
+ if o.is_a?(Class)
346
+ obs = "#{o}.instance"
347
+ o = o.instance
348
+ else
349
+ obs = "self.class.__meta[:og_observers][#{idx}]"
350
+ end
351
+
352
+ if o.respond_to?(:og_pre_insert)
353
+ pre_cb << "#{obs}.og_pre_insert(conn, self);"
354
+ end
355
+
356
+ if o.respond_to?(:og_post_insert)
357
+ post_cb << "#{obs}.og_post_insert(conn, self);"
358
+ end
359
+
360
+ if o.respond_to?(:og_pre_insert_update)
361
+ pre_cb << "#{obs}.og_pre_insert_update(conn, self);"
362
+ end
363
+
364
+ if o.respond_to?(:og_post_insert_update)
365
+ post_cb << "#{obs}.og_post_insert_update(conn, self);"
366
+ end
367
+ end
368
+ end
369
+
304
370
  klass.class_eval %{
305
371
  def og_insert(conn)
306
372
  #{insert_code(klass, db, pre_cb, post_cb)}
@@ -320,6 +386,8 @@ class Adapter
320
386
 
321
387
  sql = "UPDATE #{klass::DBTABLE} SET #{updates.join(', ')} WHERE oid=#\{@oid\}"
322
388
 
389
+ # Attach object callbacks.
390
+
323
391
  if klass.instance_methods.include?('og_pre_update')
324
392
  pre_cb = 'og_pre_update(conn);'
325
393
  else
@@ -340,6 +408,35 @@ class Adapter
340
408
  post_cb << 'og_post_insert_update(conn);'
341
409
  end
342
410
 
411
+ # Attach observers.
412
+
413
+ if observers = klass.__meta[:og_observers]
414
+ observers.each_with_index do |o, idx|
415
+ if o.is_a?(Class)
416
+ obs = "#{o}.instance"
417
+ o = o.instance
418
+ else
419
+ obs = "self.class.__meta[:og_observers][#{idx}]"
420
+ end
421
+
422
+ if o.respond_to?(:og_pre_update)
423
+ pre_cb << "#{obs}.og_pre_update(conn, self);"
424
+ end
425
+
426
+ if o.respond_to?(:og_post_update)
427
+ post_cb << "#{obs}.og_post_update(conn, self);"
428
+ end
429
+
430
+ if o.respond_to?(:og_pre_insert_update)
431
+ pre_cb << "#{obs}.og_pre_insert_update(conn, self);"
432
+ end
433
+
434
+ if o.respond_to?(:og_post_insert_update)
435
+ post_cb << "#{obs}.og_post_insert_update(conn, self);"
436
+ end
437
+ end
438
+ end
439
+
343
440
  klass.class_eval %{
344
441
  def og_update(conn)
345
442
  #{pre_cb}
@@ -367,9 +464,46 @@ class Adapter
367
464
  end
368
465
  end
369
466
 
467
+ # Attach object callbacks.
468
+
469
+ if klass.instance_methods.include?('og_pre_read')
470
+ pre_cb = 'og_pre_read(conn);'
471
+ else
472
+ pre_cb = ''
473
+ end
474
+
475
+ if klass.instance_methods.include?('og_post_read')
476
+ post_cb = 'og_post_read(conn);'
477
+ else
478
+ post_cb = ''
479
+ end
480
+
481
+ # Attach observers.
482
+
483
+ if observers = klass.__meta[:og_observers]
484
+ observers.each_with_index do |o, idx|
485
+ if o.is_a?(Class)
486
+ obs = "#{o}.instance"
487
+ o = o.instance
488
+ else
489
+ obs = "self.class.__meta[:og_observers][#{idx}]"
490
+ end
491
+
492
+ if o.respond_to?(:og_pre_read)
493
+ pre_cb << "#{obs}.og_pre_read(conn, self);"
494
+ end
495
+
496
+ if o.respond_to?(:og_post_read)
497
+ post_cb << "#{obs}.og_post_read(conn, self);"
498
+ end
499
+ end
500
+ end
501
+
370
502
  klass.class_eval %{
371
503
  def og_read(res, tuple = nil)
504
+ #{pre_cb}
372
505
  #{code.join('; ')}
506
+ #{post_cb}
373
507
  end
374
508
  }
375
509
  end
@@ -1,6 +1,6 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: mysql.rb 266 2005-02-28 14:50:48Z gmosx $
3
+ # $Id: mysql.rb 270 2005-03-07 17:52:16Z gmosx $
4
4
 
5
5
  require 'mysql'
6
6
 
@@ -19,6 +19,7 @@ class MysqlAdapter < Adapter
19
19
  def initialize
20
20
  super
21
21
  @typemap.update(TrueClass => 'tinyint')
22
+ @typecast.update(TrueClass => "#\{:s: ? \"1\" : 'NULL' \}")
22
23
  end
23
24
 
24
25
  def self.escape(str)
@@ -118,6 +119,43 @@ class MysqlAdapter < Adapter
118
119
  res.free if res
119
120
  end
120
121
 
122
+ def create_fields(klass)
123
+ fields = []
124
+
125
+ klass.__props.each do |p|
126
+ klass.sql_index(p.symbol) if p.meta[:sql_index]
127
+
128
+ field = "#{p.symbol}"
129
+
130
+ if p.meta and p.meta[:sql]
131
+ field << " #{p.meta[:sql]}"
132
+ else
133
+ field << " #{@typemap[p.klass]}"
134
+
135
+ if p.meta
136
+ # set default value (gmosx: not that useful in the
137
+ # current implementation).
138
+ if default = p.meta[:default]
139
+ field << " DEFAULT #{default.inspect} NOT NULL"
140
+ end
141
+
142
+ # set unique
143
+ # FIXME: correctly handle UNIQUE constrain.
144
+ # field << " UNIQUE" if p.meta[:unique]
145
+
146
+ # attach extra sql
147
+ if extra_sql = p.meta[:extra_sql]
148
+ field << " #{extra_sql}"
149
+ end
150
+ end
151
+ end
152
+
153
+ fields << field
154
+ end
155
+
156
+ return fields
157
+ end
158
+
121
159
  def create_table(klass, db)
122
160
  conn = db.get_connection
123
161
 
@@ -165,11 +203,11 @@ class MysqlAdapter < Adapter
165
203
  if klass.__meta and joins = klass.__meta[:sql_join]
166
204
  for data in joins
167
205
  # the class to join to and some options.
168
- join_class, options = *data
206
+ join_name, join_class, options = *data
169
207
 
170
208
  # gmosx: dont use DBTABLE here, perhaps the join class
171
209
  # is not managed yet.
172
- join_table = "#{self.class.join_table(klass, join_class)}"
210
+ join_table = "#{self.class.join_table(klass, join_class, join_name)}"
173
211
  join_src = "#{self.class.encode(klass)}_oid"
174
212
  join_dst = "#{self.class.encode(join_class)}_oid"
175
213
  begin
@@ -1,7 +1,7 @@
1
1
  # * Matt Bowen <matt.bowen@farweststeel.com>
2
2
  # * George Moschovitis <gm@navel.gr>
3
3
  # (c) 2004-2005 Navel, all rights reserved.
4
- # $Id: oracle.rb 266 2005-02-28 14:50:48Z gmosx $
4
+ # $Id: oracle.rb 270 2005-03-07 17:52:16Z gmosx $
5
5
 
6
6
  require 'oracle'
7
7
 
@@ -28,6 +28,7 @@ class OracleAdapter < Adapter
28
28
  Array => 'varchar2(1024)',
29
29
  Hash => 'varchar2(1024)'
30
30
  )
31
+ @typecast.update(TrueClass => "#\{:s: ? \"1\" : 'NULL' \}")
31
32
  end
32
33
 
33
34
  def self.timestamp(time = Time.now)
@@ -190,11 +191,11 @@ class OracleAdapter < Adapter
190
191
  if klass.__meta and joins = klass.__meta[:sql_join]
191
192
  for data in joins
192
193
  # the class to join to and some options.
193
- join_class, options = *data
194
+ join_name, join_class, options = *data
194
195
 
195
196
  # gmosx: dont use DBTABLE here, perhaps the join class
196
197
  # is not managed yet.
197
- join_table = "#{self.class.join_table(klass, join_class)}"
198
+ join_table = "#{self.class.join_table(klass, join_class, join_name)}"
198
199
  join_src = "#{self.class.encode(klass)}_oid"
199
200
  join_dst = "#{self.class.encode(join_class)}_oid"
200
201
  begin
@@ -1,6 +1,6 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: psql.rb 266 2005-02-28 14:50:48Z gmosx $
3
+ # $Id: psql.rb 270 2005-03-07 17:52:16Z gmosx $
4
4
 
5
5
  require 'postgres'
6
6
 
@@ -137,11 +137,11 @@ class PsqlAdapter < Adapter
137
137
  if klass.__meta and joins = klass.__meta[:sql_join]
138
138
  for data in joins
139
139
  # the class to join to and some options.
140
- join_class, options = *data
140
+ join_name, join_class, options = *data
141
141
 
142
142
  # gmosx: dont use DBTABLE here, perhaps the join class
143
143
  # is not managed yet.
144
- join_table = "#{self.class.join_table(klass, join_class)}"
144
+ join_table = "#{self.class.join_table(klass, join_class, join_name)}"
145
145
  join_src = "#{self.class.encode(klass)}_oid"
146
146
  join_dst = "#{self.class.encode(join_class)}_oid"
147
147
  begin