nitro 0.12.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{ChangeLog → CHANGELOG} +137 -0
- data/INSTALL +1 -2
- data/README +1 -1
- data/Rakefile +10 -61
- data/benchmark/{nitro/bench.rb → bench.rb} +1 -1
- data/benchmark/{nitro/simple-webrick-n-200.txt → simple-webrick-n-200.txt} +0 -0
- data/benchmark/{nitro/static-webrick-n-200.txt → static-webrick-n-200.txt} +0 -0
- data/benchmark/{nitro/tiny-lhttpd-n-200-c-5.txt → tiny-lhttpd-n-200-c-5.txt} +0 -0
- data/benchmark/{nitro/tiny-webrick-n-200-c-5.txt → tiny-webrick-n-200-c-5.txt} +0 -0
- data/benchmark/{nitro/tiny-webrick-n-200.txt → tiny-webrick-n-200.txt} +0 -0
- data/benchmark/{nitro/tiny2-webrick-n-200.txt → tiny2-webrick-n-200.txt} +0 -0
- data/doc/{ChangeLog.1 → CHANGELOG.1} +0 -0
- data/{RELEASES → doc/RELEASES} +46 -0
- data/doc/faq.txt +7 -0
- data/examples/README.windows +1 -1
- data/examples/ajax/controller.rb +21 -0
- data/examples/ajax/public/index.xhtml +70 -0
- data/examples/ajax/public/js/ajax.js +64 -0
- data/examples/ajax/run.rb +16 -0
- data/examples/blog/README +6 -3
- data/examples/blog/conf/apache.conf +2 -2
- data/examples/blog/conf/lhttpd.conf +2 -2
- data/examples/blog/log/apache.error_log +777 -0
- data/examples/blog/{root → public}/base.xsl +0 -0
- data/examples/blog/{root → public}/fcgi.rb +0 -0
- data/examples/blog/{root → public}/m/bubbles.gif +0 -0
- data/examples/blog/{root → public}/m/comments_curve.gif +0 -0
- data/examples/blog/{root → public}/m/down.gif +0 -0
- data/examples/blog/{root → public}/m/footer_bg.gif +0 -0
- data/examples/blog/{root → public}/m/garrow.gif +0 -0
- data/examples/blog/{root → public}/m/gbull.gif +0 -0
- data/examples/blog/{root → public}/m/grbull.gif +0 -0
- data/examples/blog/{root → public}/m/h1_bg.gif +0 -0
- data/examples/blog/{root → public}/m/header_bg.gif +0 -0
- data/examples/blog/{root → public}/m/nitro.gif +0 -0
- data/examples/blog/{root → public}/m/obull.gif +0 -0
- data/examples/blog/{root → public}/m/page_bg.gif +0 -0
- data/examples/blog/{root → public}/m/rss.gif +0 -0
- data/examples/blog/{root → public}/m/side_title_bg.gif +0 -0
- data/examples/blog/{root → public}/m/sidebar_bg.gif +0 -0
- data/examples/{no_xsl_blog/root → blog/public}/style.css +6 -0
- data/examples/blog/run.rb +10 -12
- data/examples/blog/{lib → src}/blog.rb +3 -3
- data/examples/blog/{lib/blog → src}/controller.rb +13 -2
- data/examples/blog/src/mailer.rb +23 -0
- data/examples/blog/{lib/blog/model.rb → src/models/blog.rb} +4 -7
- data/examples/blog/src/models/content.rb +52 -0
- data/examples/blog/src/views/blog_entry_email.xhtml +16 -0
- data/examples/blog/{root → src/views}/comments.xhtml +0 -0
- data/examples/blog/{root → src/views}/entry_form.xhtml +0 -0
- data/examples/blog/{root → src/views}/error.xhtml +0 -0
- data/examples/blog/{root → src/views}/index.xhtml +0 -0
- data/examples/blog/{root → src/views}/login.xhtml +0 -0
- data/examples/blog/{root → src/views}/recent_posts.xhtml +0 -0
- data/examples/blog/{root → src/views}/view_entry.xhtml +8 -0
- data/examples/blog/{root → src/views}/view_entry.xml +0 -0
- data/examples/blog/src/xsl/base.xsl +153 -0
- data/examples/blog/{root → src/xsl}/style.xsl +2 -2
- data/examples/no_xsl_blog/README +5 -1
- data/examples/no_xsl_blog/conf/apache.conf +2 -2
- data/examples/no_xsl_blog/conf/lhttpd.conf +2 -2
- data/examples/no_xsl_blog/lib/blog/model.rb +1 -1
- data/{lib/parts → examples/no_xsl_blog/lib}/content.rb +1 -11
- data/examples/no_xsl_blog/log/apache.error_log +405 -0
- data/examples/no_xsl_blog/{root → public}/comments.xhtml +0 -0
- data/examples/no_xsl_blog/{root → public}/entry_form.xhtml +0 -0
- data/examples/no_xsl_blog/{root → public}/fcgi.rb +0 -0
- data/examples/no_xsl_blog/{root → public}/index.xhtml +0 -0
- data/examples/no_xsl_blog/{root → public}/login.xhtml +0 -0
- data/examples/no_xsl_blog/{root → public}/m/bubbles.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/comments_curve.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/down.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/footer_bg.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/garrow.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/gbull.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/grbull.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/h1_bg.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/header_bg.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/nitro.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/obull.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/page_bg.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/rss.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/side_title_bg.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/m/sidebar_bg.gif +0 -0
- data/examples/no_xsl_blog/{root → public}/recent_posts.xhtml +0 -0
- data/examples/{blog/root → no_xsl_blog/public}/style.css +0 -0
- data/examples/no_xsl_blog/{root → public}/view_entry.xhtml +0 -0
- data/examples/no_xsl_blog/{root → public}/view_entry.xml +0 -0
- data/examples/tiny/conf/apache.conf +2 -2
- data/examples/tiny/log/apache.error_log +100 -0
- data/examples/tiny/{root → public}/fcgi.rb +0 -0
- data/examples/tiny/{root → public}/include.xhtml +0 -0
- data/examples/tiny/{root → public}/index.xhtml +0 -0
- data/{bin/proto/root/m → examples/tiny/public}/nitro.png +0 -0
- data/examples/tiny/{root → public}/upload.xhtml +0 -0
- data/examples/tiny/run.rb +1 -2
- data/examples/why_wiki/wiki.yml +1 -0
- data/install.rb +5 -2
- data/lib/nitro.rb +2 -6
- data/lib/nitro/adapters/fastcgi.rb +2 -2
- data/lib/nitro/adapters/webrick.rb +4 -4
- data/lib/nitro/conf.rb +5 -2
- data/lib/nitro/controller.rb +2 -2
- data/lib/nitro/dispatcher.rb +19 -8
- data/lib/nitro/mail.rb +252 -8
- data/lib/nitro/render.rb +24 -21
- data/lib/nitro/runner.rb +1 -1
- data/lib/nitro/scaffold.rb +2 -5
- data/lib/nitro/simple.rb +2 -1
- data/lib/nitro/template.rb +42 -2
- data/test/nitro/tc_controller.rb +9 -4
- data/test/nitro/tc_dispatcher.rb +4 -6
- data/test/nitro/tc_mail.rb +95 -0
- data/test/{root → public}/blog/list.xhtml +0 -0
- data/test/public/dummy_mailer/registration.xhtml +5 -0
- data/vendor/README +0 -1
- metadata +136 -181
- data/benchmark/og/bench.rb +0 -75
- data/benchmark/og/sqlite-no-prepare.1.txt +0 -13
- data/benchmark/og/sqlite-no-prepare.2.txt +0 -13
- data/benchmark/og/sqlite-prepare.1.txt +0 -13
- data/benchmark/og/sqlite-prepare.2.txt +0 -13
- data/bin/proto/README +0 -34
- data/bin/proto/conf/apache.conf +0 -1
- data/bin/proto/conf/app.conf.rb +0 -14
- data/bin/proto/conf/lhttpd.conf +0 -236
- data/bin/proto/ctl +0 -4
- data/bin/proto/lib/README +0 -5
- data/bin/proto/log/README +0 -3
- data/bin/proto/root/fcgi.rb +0 -6
- data/bin/proto/root/index.xhtml +0 -69
- data/bin/proto/root/style.css +0 -152
- data/bin/proto/root/style.xsl +0 -99
- data/doc/og_config.txt +0 -35
- data/doc/og_tutorial.txt +0 -595
- data/examples/og/README +0 -11
- data/examples/og/mock_example.rb +0 -50
- data/examples/og/mysql_to_psql.rb +0 -96
- data/examples/og/run.rb +0 -286
- data/examples/tiny/root/nitro.png +0 -0
- data/lib/glue.rb +0 -55
- data/lib/glue/array.rb +0 -61
- data/lib/glue/attribute.rb +0 -83
- data/lib/glue/cache.rb +0 -138
- data/lib/glue/flexob.rb +0 -12
- data/lib/glue/hash.rb +0 -122
- data/lib/glue/inflector.rb +0 -91
- data/lib/glue/logger.rb +0 -147
- data/lib/glue/misc.rb +0 -14
- data/lib/glue/mixins.rb +0 -36
- data/lib/glue/number.rb +0 -24
- data/lib/glue/object.rb +0 -32
- data/lib/glue/pool.rb +0 -60
- data/lib/glue/property.rb +0 -408
- data/lib/glue/string.rb +0 -162
- data/lib/glue/time.rb +0 -85
- data/lib/glue/validation.rb +0 -394
- data/lib/og.rb +0 -185
- data/lib/og/adapter.rb +0 -513
- data/lib/og/adapters/filesys.rb +0 -121
- data/lib/og/adapters/mysql.rb +0 -347
- data/lib/og/adapters/oracle.rb +0 -375
- data/lib/og/adapters/psql.rb +0 -273
- data/lib/og/adapters/sqlite.rb +0 -262
- data/lib/og/backend.rb +0 -297
- data/lib/og/connection.rb +0 -304
- data/lib/og/database.rb +0 -282
- data/lib/og/enchant.rb +0 -125
- data/lib/og/meta.rb +0 -373
- data/lib/og/mock.rb +0 -165
- data/lib/og/observer.rb +0 -53
- data/lib/og/typemacros.rb +0 -23
- data/lib/parts/README +0 -9
- data/test/glue/tc_attribute.rb +0 -22
- data/test/glue/tc_cache.rb +0 -45
- data/test/glue/tc_hash.rb +0 -38
- data/test/glue/tc_logger.rb +0 -39
- data/test/glue/tc_numbers.rb +0 -20
- data/test/glue/tc_property.rb +0 -89
- data/test/glue/tc_property_mixins.rb +0 -93
- data/test/glue/tc_property_type_checking.rb +0 -35
- data/test/glue/tc_strings.rb +0 -103
- data/test/glue/tc_validation.rb +0 -188
- data/test/og/tc_filesys.rb +0 -83
- data/test/og/tc_lifecycle.rb +0 -104
- data/test/og/tc_many_to_many.rb +0 -62
- data/test/og/tc_meta.rb +0 -55
- data/test/og/tc_observer.rb +0 -85
- data/test/og/tc_sqlite.rb +0 -87
- data/test/tc_og.rb +0 -355
- data/vendor/composite_sexp_processor.rb +0 -43
- data/vendor/parse_tree.rb +0 -745
- data/vendor/sexp_processor.rb +0 -453
data/lib/og.rb
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
# * George Moschovitis <gm@navel.gr>
|
2
|
-
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: og.rb 270 2005-03-07 17:52:16Z gmosx $
|
4
|
-
|
5
|
-
require 'glue'
|
6
|
-
require 'glue/logger'
|
7
|
-
require 'glue/attribute'
|
8
|
-
require 'glue/property'
|
9
|
-
require 'glue/array'
|
10
|
-
require 'glue/hash'
|
11
|
-
require 'glue/time'
|
12
|
-
require 'glue/pool'
|
13
|
-
require 'glue/validation'
|
14
|
-
|
15
|
-
# Og (ObjectGraph) is an efficient, yet simple Object-Relational
|
16
|
-
# mapping library.
|
17
|
-
#
|
18
|
-
# === Features
|
19
|
-
#
|
20
|
-
# The library provides the following features:
|
21
|
-
#
|
22
|
-
# + Object-Relational mapping.
|
23
|
-
# + Absolutely no configuration files.
|
24
|
-
# + Multiple backends (PostgreSQL, MySQL, SQLite).
|
25
|
-
# + ActiveRecord-style meta language and db aware methods.
|
26
|
-
# + Deserialize to Ruby Objects.
|
27
|
-
# + Deserialize sql join queries to Ruby Objects (temporarily dissabled).
|
28
|
-
# + Serialize arbitrary ruby object graphs through YAML.
|
29
|
-
# + Connection pooling.
|
30
|
-
# + Thread safety.
|
31
|
-
# + SQL transactions.
|
32
|
-
# + Lifecycle callbacks.
|
33
|
-
# + Lifecycle observers.
|
34
|
-
# + Transparent support for cascading deletes for all backends.
|
35
|
-
# + Hierarchical structures (preorder traversal, materialized paths)
|
36
|
-
# + Works safely as part of distributed application.
|
37
|
-
# + Simple implementation.
|
38
|
-
#
|
39
|
-
# === Meta language
|
40
|
-
#
|
41
|
-
# primary_key :pid (NOT IMPLEMENTED)
|
42
|
-
# name_key :name (NOT IMPLEMENTED)
|
43
|
-
# prop_accessor Fixnum, :pid, :sql => "smallint DEFAULT 1"
|
44
|
-
# has_many Child, :children
|
45
|
-
# many_to_many Role, :roles
|
46
|
-
# sql_index :pid
|
47
|
-
#
|
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
|
63
|
-
#
|
64
|
-
# Keep the main classes backend agnostic.
|
65
|
-
#
|
66
|
-
# For class ids we use the name instead of a hash. Class ids are
|
67
|
-
# typically not used in querys, they are stored for completeness.
|
68
|
-
# If we store a hash we cannot reclaim the class thus invalidating
|
69
|
-
# the point. Instead of .name(), to_s() is used so the methods
|
70
|
-
# are more flexible (they accept class names too!!)
|
71
|
-
#
|
72
|
-
# Og allows the serialization of arbitrary Ruby objects. Just
|
73
|
-
# mark them as Object (or Array or Hash) in the prop_accessor
|
74
|
-
# and the engine will serialize a YAML dump of the object.
|
75
|
-
# Arbitrary object graphs are supported too.
|
76
|
-
#
|
77
|
-
# This is NOT a singleton, an application may access multiple
|
78
|
-
# databases.
|
79
|
-
#
|
80
|
-
# The og.xxx methods are more flexible and allow you to use
|
81
|
-
# multiple databases for example.
|
82
|
-
#
|
83
|
-
# === Managed Objects Lifecycle Callbacks
|
84
|
-
#
|
85
|
-
# * og_pre_read
|
86
|
-
# * og_post_read
|
87
|
-
# * og_pre_insert
|
88
|
-
# * og_post_insert
|
89
|
-
# * og_pre_update
|
90
|
-
# * og_post_update
|
91
|
-
# * og_pre_insert_update
|
92
|
-
# * og_post_insert_update
|
93
|
-
# * self.og_pre_delete
|
94
|
-
#
|
95
|
-
# A class level callback is used for delete because typically you call
|
96
|
-
# delete with an oid and not an object to avoid a deserialization.
|
97
|
-
#
|
98
|
-
# === Future
|
99
|
-
#
|
100
|
-
# * Support prepared statements (pgsql)
|
101
|
-
# * Support stored procedures (pgsql)
|
102
|
-
# * Support caching.
|
103
|
-
# * Deserialize to OpenStruct.
|
104
|
-
# * Better documentation.
|
105
|
-
|
106
|
-
module Og
|
107
|
-
|
108
|
-
# The name.
|
109
|
-
|
110
|
-
Name = 'ObjectGraph'
|
111
|
-
|
112
|
-
# The version.
|
113
|
-
|
114
|
-
Version = '0.12.0'
|
115
|
-
|
116
|
-
# Library path.
|
117
|
-
|
118
|
-
LibPath = File.dirname(__FILE__)
|
119
|
-
|
120
|
-
# If true, only allow reading from the database. Usefull
|
121
|
-
# for maintainance.
|
122
|
-
|
123
|
-
mattr_accessor :read_only_mode, false
|
124
|
-
|
125
|
-
# If true, the library automatically 'enchants' managed classes.
|
126
|
-
# In enchant mode, special db aware methods are added to
|
127
|
-
# managed classes and instances.
|
128
|
-
# If false, Og enchants only classes that define properties.
|
129
|
-
|
130
|
-
mattr_accessor :enchant_managed_classes, true
|
131
|
-
|
132
|
-
# If true, use Ruby's advanced introspection capabilities to
|
133
|
-
# automatically manage classes tha define properties.
|
134
|
-
|
135
|
-
mattr_accessor :auto_manage_classes, true
|
136
|
-
|
137
|
-
# If true, automatically include the Og meta-language into Module.
|
138
|
-
# If false, the polution of the Module object is avoided. However
|
139
|
-
# if you include a prop_accessor or a managed Mixin in your
|
140
|
-
# object MetaLanguage gets automatically extended in the class.
|
141
|
-
|
142
|
-
mattr_accessor :include_meta_language, true
|
143
|
-
|
144
|
-
# Attach the following prefix to all generated SQL table names.
|
145
|
-
# Usefull on hosting scenarios where you have to run multiple
|
146
|
-
# web applications/sites on a single database.
|
147
|
-
|
148
|
-
mattr_accessor :table_prefix, nil
|
149
|
-
|
150
|
-
# If true, Og tries to create/update the schema in the
|
151
|
-
# data store. For production/live environments set this to false
|
152
|
-
# and only set to true when the object model is upadated.
|
153
|
-
# For debug/development environments this should stay true
|
154
|
-
# for convienience.
|
155
|
-
|
156
|
-
mattr_accessor :create_schema, true
|
157
|
-
|
158
|
-
# The active database. Og allows you to access multiple
|
159
|
-
# databases from a single application.
|
160
|
-
|
161
|
-
mattr_accessor :db
|
162
|
-
|
163
|
-
# Set the active database.
|
164
|
-
|
165
|
-
def self.use(db)
|
166
|
-
@@db = db
|
167
|
-
@@db.get_connection
|
168
|
-
end
|
169
|
-
|
170
|
-
# The adapter of the active database.
|
171
|
-
|
172
|
-
def self.adapter
|
173
|
-
@@db.adapter
|
174
|
-
end
|
175
|
-
|
176
|
-
# Marker module. If included this in a class, the Og automanager
|
177
|
-
# ignores this class.
|
178
|
-
|
179
|
-
module Unmanageable; end
|
180
|
-
|
181
|
-
end
|
182
|
-
|
183
|
-
# gmosx: leave this here.
|
184
|
-
|
185
|
-
require 'og/database'
|
data/lib/og/adapter.rb
DELETED
@@ -1,513 +0,0 @@
|
|
1
|
-
# * George Moschovitis <gm@navel.gr>
|
2
|
-
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: adapter.rb 270 2005-03-07 17:52:16Z gmosx $
|
4
|
-
|
5
|
-
require 'yaml'
|
6
|
-
require 'singleton'
|
7
|
-
|
8
|
-
require 'og/connection'
|
9
|
-
|
10
|
-
module Og
|
11
|
-
|
12
|
-
# An adapter communicates with the backend datastore.
|
13
|
-
# The adapters for all supported datastores extend this
|
14
|
-
# class. Typically, an RDBMS is used to implement a
|
15
|
-
# datastore.
|
16
|
-
|
17
|
-
class Adapter
|
18
|
-
include Singleton
|
19
|
-
|
20
|
-
# A mapping between Ruby and backend Datastore types.
|
21
|
-
|
22
|
-
attr_accessor :typemap
|
23
|
-
|
24
|
-
# A map for casting Ruby types to SQL safe textual
|
25
|
-
# representations.
|
26
|
-
|
27
|
-
attr_accessor :typecast
|
28
|
-
|
29
|
-
# Lookup the adapter instance from the adapter name.
|
30
|
-
|
31
|
-
def self.for_name(name)
|
32
|
-
require "og/adapters/#{name}"
|
33
|
-
eval %{ return #{name.capitalize}Adapter.instance }
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize
|
37
|
-
# The default mappings, should be valid for most
|
38
|
-
# RDBMS.
|
39
|
-
|
40
|
-
@typemap = {
|
41
|
-
Integer => 'integer',
|
42
|
-
Fixnum => 'integer',
|
43
|
-
Float => 'float',
|
44
|
-
String => 'text',
|
45
|
-
Time => 'timestamp',
|
46
|
-
Date => 'date',
|
47
|
-
TrueClass => 'boolean',
|
48
|
-
Object => 'text',
|
49
|
-
Array => 'text',
|
50
|
-
Hash => 'text'
|
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
|
-
)
|
65
|
-
end
|
66
|
-
|
67
|
-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
68
|
-
# :section: Utilities
|
69
|
-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
70
|
-
|
71
|
-
# Escape an SQL string
|
72
|
-
|
73
|
-
def self.escape(str)
|
74
|
-
return nil unless str
|
75
|
-
return str.gsub( /'/, "''" )
|
76
|
-
end
|
77
|
-
|
78
|
-
# Convert a ruby time to an sql timestamp.
|
79
|
-
# TODO: Optimize this
|
80
|
-
|
81
|
-
def self.timestamp(time = Time.now)
|
82
|
-
return nil unless time
|
83
|
-
return time.strftime("%Y-%m-%d %H:%M:%S")
|
84
|
-
end
|
85
|
-
|
86
|
-
# Output YYY-mm-dd
|
87
|
-
# TODO: Optimize this
|
88
|
-
|
89
|
-
def self.date(date)
|
90
|
-
return nil unless date
|
91
|
-
return "#{date.year}-#{date.month}-#{date.mday}"
|
92
|
-
end
|
93
|
-
|
94
|
-
# Parse sql datetime
|
95
|
-
# TODO: Optimize this
|
96
|
-
|
97
|
-
def self.parse_timestamp(str)
|
98
|
-
return nil unless str
|
99
|
-
return Time.parse(str)
|
100
|
-
end
|
101
|
-
|
102
|
-
# Input YYYY-mm-dd
|
103
|
-
# TODO: Optimize this
|
104
|
-
|
105
|
-
def self.parse_date(str)
|
106
|
-
return nil unless str
|
107
|
-
return Date.strptime(str)
|
108
|
-
end
|
109
|
-
|
110
|
-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
111
|
-
# :section: Database methods
|
112
|
-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
113
|
-
|
114
|
-
# Create the database.
|
115
|
-
|
116
|
-
def create_db(database, user = nil, password = nil)
|
117
|
-
Logger.info "Creating database '#{database}'."
|
118
|
-
end
|
119
|
-
|
120
|
-
# Drop the database.
|
121
|
-
|
122
|
-
def drop_db(database, user = nil, password = nil)
|
123
|
-
Logger.info "Dropping database '#{database}'."
|
124
|
-
end
|
125
|
-
|
126
|
-
# Create a new connection to the backend.
|
127
|
-
|
128
|
-
def new_connection(db)
|
129
|
-
return Connection.new(db)
|
130
|
-
end
|
131
|
-
|
132
|
-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
133
|
-
# :section: O->R mapping methods and utilities.
|
134
|
-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
135
|
-
|
136
|
-
# Encode the name of the klass as an sql safe string.
|
137
|
-
# The Module separators are replaced with _ and NOT stripped
|
138
|
-
# out so that we can convert back to the original notation if
|
139
|
-
# needed. The leading module if available is removed.
|
140
|
-
|
141
|
-
def self.encode(klass)
|
142
|
-
"#{klass.name.gsub(/^.*::/, "")}".gsub(/::/, "_").downcase
|
143
|
-
end
|
144
|
-
|
145
|
-
# The name of the SQL table where objects of this class
|
146
|
-
# are stored. A prefix is needed to avoid colision with
|
147
|
-
# reserved prefices (for example User maps to user which
|
148
|
-
# is reserved in postgresql). The prefix should start
|
149
|
-
# with an alphanumeric character to be compatible with
|
150
|
-
# all RDBMS (most notable Oracle).
|
151
|
-
#
|
152
|
-
# You may want to override this method to map an existing
|
153
|
-
# database schema using Og.
|
154
|
-
|
155
|
-
def self.table(klass)
|
156
|
-
"og_#{Og.table_prefix}#{encode(klass)}"
|
157
|
-
end
|
158
|
-
|
159
|
-
# The name of the join table for the two given classes.
|
160
|
-
# A prefix is needed to avoid colision with reserved
|
161
|
-
# prefices (for example User maps to user which
|
162
|
-
# is reserved in postgresql). The prefix should start
|
163
|
-
# with an alphanumeric character to be compatible with
|
164
|
-
# all RDBMS (most notable Oracle).
|
165
|
-
#
|
166
|
-
# You may want to override this method to map an existing
|
167
|
-
# database schema using Og.
|
168
|
-
|
169
|
-
def self.join_table(klass1, klass2, field)
|
170
|
-
"og_#{Og.table_prefix}j_#{encode(klass1)}_#{encode(klass2)}_#{field}"
|
171
|
-
end
|
172
|
-
|
173
|
-
# Return an sql string evaluator for the property.
|
174
|
-
# No need to optimize this, used only to precalculate code.
|
175
|
-
# YAML is used to store general Ruby objects to be more
|
176
|
-
# portable.
|
177
|
-
#--
|
178
|
-
# FIXME: add extra handling for float.
|
179
|
-
#++
|
180
|
-
|
181
|
-
def write_prop(p)
|
182
|
-
if p.klass.ancestors.include?(Integer)
|
183
|
-
return "#\{@#{p.symbol} || 'NULL'\}"
|
184
|
-
elsif p.klass.ancestors.include?(Float)
|
185
|
-
return "#\{@#{p.symbol} || 'NULL'\}"
|
186
|
-
elsif p.klass.ancestors.include?(String)
|
187
|
-
return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : 'NULL'\}|
|
188
|
-
elsif p.klass.ancestors.include?(Time)
|
189
|
-
return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
|
190
|
-
elsif p.klass.ancestors.include?(Date)
|
191
|
-
return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
|
192
|
-
elsif p.klass.ancestors.include?(TrueClass)
|
193
|
-
return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
|
194
|
-
else
|
195
|
-
# gmosx: keep the '' for nil symbols.
|
196
|
-
return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
# Return an evaluator for reading the property.
|
201
|
-
# No need to optimize this, used only to precalculate code.
|
202
|
-
|
203
|
-
def read_prop(p, idx)
|
204
|
-
if p.klass.ancestors.include?(Integer)
|
205
|
-
return "res[#{idx}].to_i"
|
206
|
-
elsif p.klass.ancestors.include?(Float)
|
207
|
-
return "res[#{idx}].to_f"
|
208
|
-
elsif p.klass.ancestors.include?(String)
|
209
|
-
return "res[#{idx}]"
|
210
|
-
elsif p.klass.ancestors.include?(Time)
|
211
|
-
return "#{self.class}.parse_timestamp(res[#{idx}])"
|
212
|
-
elsif p.klass.ancestors.include?(Date)
|
213
|
-
return "#{self.class}.parse_date(res[#{idx}])"
|
214
|
-
elsif p.klass.ancestors.include?(TrueClass)
|
215
|
-
return "('0' != res[#{idx}])"
|
216
|
-
else
|
217
|
-
return "YAML::load(res[#{idx}])"
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
# Create the fields that correpsond to the klass properties.
|
222
|
-
# The generated fields array is used in create_table.
|
223
|
-
# If the property has an :sql metadata this overrides the
|
224
|
-
# default mapping. If the property has an :extra_sql metadata
|
225
|
-
# the extra sql is appended after the default mapping.
|
226
|
-
|
227
|
-
def create_fields(klass)
|
228
|
-
fields = []
|
229
|
-
|
230
|
-
klass.__props.each do |p|
|
231
|
-
klass.sql_index(p.symbol) if p.meta[:sql_index]
|
232
|
-
|
233
|
-
field = "#{p.symbol}"
|
234
|
-
|
235
|
-
if p.meta and p.meta[:sql]
|
236
|
-
field << " #{p.meta[:sql]}"
|
237
|
-
else
|
238
|
-
field << " #{@typemap[p.klass]}"
|
239
|
-
|
240
|
-
if p.meta
|
241
|
-
# set default value (gmosx: not that useful in the
|
242
|
-
# current implementation).
|
243
|
-
if default = p.meta[:default]
|
244
|
-
field << " DEFAULT #{default.inspect} NOT NULL"
|
245
|
-
end
|
246
|
-
|
247
|
-
# set unique
|
248
|
-
field << " UNIQUE" if p.meta[:unique]
|
249
|
-
|
250
|
-
# attach extra sql
|
251
|
-
if extra_sql = p.meta[:extra_sql]
|
252
|
-
field << " #{extra_sql}"
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
fields << field
|
258
|
-
end
|
259
|
-
|
260
|
-
return fields
|
261
|
-
end
|
262
|
-
|
263
|
-
# Create the managed object table. The properties of the
|
264
|
-
# object are mapped to the table columns. Additional sql relations
|
265
|
-
# and constrains are created (indicices, sequences, etc).
|
266
|
-
|
267
|
-
def create_table(klass)
|
268
|
-
raise 'Not implemented!'
|
269
|
-
end
|
270
|
-
|
271
|
-
# Returns the props that will be included in the insert query.
|
272
|
-
# For some backends the oid should be stripped.
|
273
|
-
|
274
|
-
def props_for_insert(klass)
|
275
|
-
klass.__props
|
276
|
-
end
|
277
|
-
|
278
|
-
# Returns the code that actually inserts the object into the
|
279
|
-
# database. Returns the code as String.
|
280
|
-
|
281
|
-
def insert_code(klass, sql, pre_cb, post_cb)
|
282
|
-
raise 'Not implemented!'
|
283
|
-
end
|
284
|
-
|
285
|
-
# Generate the mapping of the database fields to the
|
286
|
-
# object properties.
|
287
|
-
|
288
|
-
def calc_field_index(klass, og)
|
289
|
-
# Implement if needed.
|
290
|
-
end
|
291
|
-
|
292
|
-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
293
|
-
# :section: Precompile lifecycle methods.
|
294
|
-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
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
|
-
|
306
|
-
# Generate the property for oid.
|
307
|
-
|
308
|
-
def eval_og_oid(klass)
|
309
|
-
klass.class_eval %{
|
310
|
-
prop_accessor :oid, Fixnum, :sql => "integer PRIMARY KEY"
|
311
|
-
}
|
312
|
-
end
|
313
|
-
|
314
|
-
# Precompile the insert code for the given class.
|
315
|
-
# The generated code sets the oid when inserting!
|
316
|
-
|
317
|
-
def eval_og_insert(klass, db)
|
318
|
-
|
319
|
-
# Attach object callbacks.
|
320
|
-
|
321
|
-
if klass.instance_methods.include?('og_pre_insert')
|
322
|
-
pre_cb = 'og_pre_insert(conn);'
|
323
|
-
else
|
324
|
-
pre_cb = ''
|
325
|
-
end
|
326
|
-
|
327
|
-
if klass.instance_methods.include?('og_post_insert')
|
328
|
-
post_cb = 'og_post_insert(conn);'
|
329
|
-
else
|
330
|
-
post_cb = ''
|
331
|
-
end
|
332
|
-
|
333
|
-
if klass.instance_methods.include?('og_pre_insert_update')
|
334
|
-
pre_cb << 'og_pre_insert_update(conn);'
|
335
|
-
end
|
336
|
-
|
337
|
-
if klass.instance_methods.include?('og_post_insert_update')
|
338
|
-
post_cb << 'og_post_insert_update(conn);'
|
339
|
-
end
|
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
|
-
|
370
|
-
klass.class_eval %{
|
371
|
-
def og_insert(conn)
|
372
|
-
#{insert_code(klass, db, pre_cb, post_cb)}
|
373
|
-
end
|
374
|
-
}
|
375
|
-
end
|
376
|
-
|
377
|
-
# Precompile the update code for the given class.
|
378
|
-
# Ignore the oid when updating!
|
379
|
-
|
380
|
-
def eval_og_update(klass, db)
|
381
|
-
props = klass.__props.reject { |p| :oid == p.symbol }
|
382
|
-
|
383
|
-
updates = props.collect { |p|
|
384
|
-
"#{p.name}=#{write_prop(p)}"
|
385
|
-
}
|
386
|
-
|
387
|
-
sql = "UPDATE #{klass::DBTABLE} SET #{updates.join(', ')} WHERE oid=#\{@oid\}"
|
388
|
-
|
389
|
-
# Attach object callbacks.
|
390
|
-
|
391
|
-
if klass.instance_methods.include?('og_pre_update')
|
392
|
-
pre_cb = 'og_pre_update(conn);'
|
393
|
-
else
|
394
|
-
pre_cb = ''
|
395
|
-
end
|
396
|
-
|
397
|
-
if klass.instance_methods.include?('og_post_update')
|
398
|
-
post_cb = 'og_post_update(conn);'
|
399
|
-
else
|
400
|
-
post_cb = ''
|
401
|
-
end
|
402
|
-
|
403
|
-
if klass.instance_methods.include?('og_pre_insert_update')
|
404
|
-
pre_cb << 'og_pre_insert_update(conn);'
|
405
|
-
end
|
406
|
-
|
407
|
-
if klass.instance_methods.include?('og_post_insert_update')
|
408
|
-
post_cb << 'og_post_insert_update(conn);'
|
409
|
-
end
|
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
|
-
|
440
|
-
klass.class_eval %{
|
441
|
-
def og_update(conn)
|
442
|
-
#{pre_cb}
|
443
|
-
conn.exec "#{sql}"
|
444
|
-
#{post_cb}
|
445
|
-
end
|
446
|
-
}
|
447
|
-
end
|
448
|
-
|
449
|
-
# Precompile the code to read (deserialize) objects of the
|
450
|
-
# given class from the backend. In order to allow for changing
|
451
|
-
# field/attribute orders we have to use a field mapping hash.
|
452
|
-
|
453
|
-
def eval_og_read(klass, db)
|
454
|
-
calc_field_index(klass, db)
|
455
|
-
|
456
|
-
props = klass.__props
|
457
|
-
code = []
|
458
|
-
|
459
|
-
props.each do |p|
|
460
|
-
if idx = db.managed_classes[klass].field_index[p.name]
|
461
|
-
# more fault tolerant if a new field is added and it
|
462
|
-
# doesnt exist in the database.
|
463
|
-
code << "@#{p.name} = #{read_prop(p, idx)}"
|
464
|
-
end
|
465
|
-
end
|
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
|
-
|
502
|
-
klass.class_eval %{
|
503
|
-
def og_read(res, tuple = nil)
|
504
|
-
#{pre_cb}
|
505
|
-
#{code.join('; ')}
|
506
|
-
#{post_cb}
|
507
|
-
end
|
508
|
-
}
|
509
|
-
end
|
510
|
-
|
511
|
-
end
|
512
|
-
|
513
|
-
end
|