nitro 0.22.0 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +109 -1250
- data/INSTALL +3 -2
- data/README +5 -4
- data/Rakefile +1 -1
- data/bin/nitrogen +1 -1
- data/doc/AUTHORS +20 -6
- data/doc/CHANGELOG.3 +1314 -0
- data/doc/RELEASES +90 -0
- data/lib/nitro.rb +6 -17
- data/lib/nitro/adapter/cgi.rb +11 -0
- data/lib/nitro/adapter/webrick.rb +7 -1
- data/lib/nitro/caching/stores.rb +4 -6
- data/lib/nitro/compiler.rb +1 -1
- data/lib/nitro/compiler/errors.rb +1 -1
- data/lib/nitro/context.rb +11 -4
- data/lib/nitro/controller.rb +0 -3
- data/lib/nitro/cookie.rb +4 -3
- data/lib/nitro/dispatcher.rb +7 -1
- data/lib/nitro/dispatcher/nice.rb +6 -1
- data/lib/nitro/element.rb +2 -2
- data/lib/nitro/mixin/benchmark.rb +16 -0
- data/lib/nitro/mixin/form.rb +171 -55
- data/lib/nitro/mixin/rss.rb +1 -1
- data/lib/nitro/mixin/xhtml.rb +91 -4
- data/lib/nitro/render.rb +25 -6
- data/lib/nitro/request.rb +13 -3
- data/lib/nitro/scaffold.rb +91 -68
- data/lib/nitro/scaffold/relations.rb +54 -0
- data/lib/nitro/server.rb +8 -5
- data/lib/nitro/server/runner.rb +4 -3
- data/lib/nitro/service.rb +3 -1
- data/lib/nitro/service/xmlrpc.rb +1 -1
- data/lib/nitro/session.rb +69 -51
- data/lib/nitro/session/drb.rb +5 -7
- data/lib/nitro/session/drbserver.rb +4 -6
- data/lib/nitro/session/memory.rb +4 -6
- data/lib/part/admin.rb +6 -0
- data/lib/part/admin/controller.rb +24 -0
- data/lib/part/admin/skin.rb +21 -0
- data/lib/part/admin/template/index.xhtml +9 -0
- data/proto/public/js/cookies.js +122 -0
- data/proto/public/scaffold/edit.xhtml +4 -0
- data/proto/public/scaffold/form.xhtml +7 -0
- data/proto/public/scaffold/list.xhtml +15 -0
- data/proto/public/scaffold/new.xhtml +4 -0
- data/proto/public/scaffold/view.xhtml +0 -0
- data/proto/script/benchmark +19 -0
- data/test/nitro/adapter/tc_cgi.rb +32 -2
- data/test/nitro/mixin/tc_xhtml.rb +6 -0
- data/test/nitro/tc_dispatcher.rb +0 -17
- data/test/nitro/tc_render.rb +58 -0
- data/test/nitro/tc_server.rb +2 -0
- data/test/nitro/tc_session.rb +16 -0
- metadata +104 -85
data/doc/RELEASES
CHANGED
@@ -1,3 +1,93 @@
|
|
1
|
+
== Version 0.23.0
|
2
|
+
|
3
|
+
The summer vacations are over and there is a brand new Nitro
|
4
|
+
release. There is a preview of the new Scaffolder (also handles
|
5
|
+
Og relations), support for Tagging (folksonomy), lots of small
|
6
|
+
features and improvements and many bug fixes. Additionally, the
|
7
|
+
code has been restructured to utilize the excellent Nano and Mega
|
8
|
+
support libraries.
|
9
|
+
|
10
|
+
Most notable additions:
|
11
|
+
|
12
|
+
* Scaffolding reloaded. The scaffolding infrastructure is
|
13
|
+
reimplemented to generate more flexible code. The automatically
|
14
|
+
generated forms allow for visualization and editing of
|
15
|
+
Og relations such as HasMany and BelongsTo.
|
16
|
+
|
17
|
+
For example when rendering a BelongsTo relation all possible
|
18
|
+
parents are presented with a select element. When rendering a
|
19
|
+
HasMany relation a list of all children is presented.
|
20
|
+
|
21
|
+
Moreover, an experimental admin component is provided. Just add the
|
22
|
+
line:
|
23
|
+
|
24
|
+
require 'part/admin'
|
25
|
+
|
26
|
+
and surf
|
27
|
+
|
28
|
+
http://www.mysite.com/admin
|
29
|
+
|
30
|
+
To enter a simple administration screen.
|
31
|
+
|
32
|
+
WARNING: This feature is considered a preview and will be
|
33
|
+
improved in a future version.
|
34
|
+
|
35
|
+
* Major cleanup in the Glue subproject. Some files are moved
|
36
|
+
to the nano/mega project. Nano/Mega is now used throughout
|
37
|
+
Nitro and really makes development so much easier.
|
38
|
+
|
39
|
+
* Introduced Og Taggable mixin. It was never easier to add
|
40
|
+
tagging to your application.
|
41
|
+
|
42
|
+
class Article
|
43
|
+
include Og::Taggable
|
44
|
+
..
|
45
|
+
end
|
46
|
+
|
47
|
+
article.tag('navel', 'gmosx', 'nitro')
|
48
|
+
article.tags
|
49
|
+
article.tag_names
|
50
|
+
Article.find_with_tags('navel', 'gmosx')
|
51
|
+
Article.find_with_any_tag('name', 'gmosx')
|
52
|
+
|
53
|
+
t = Article::Tag.find_by_name('ruby')
|
54
|
+
t.articles
|
55
|
+
t.articles.count
|
56
|
+
|
57
|
+
For an example usage of this Mixin, consult the Spark sources.
|
58
|
+
|
59
|
+
* Added support for relative and absolute URLs in redirects
|
60
|
+
and renders. This feature simplifies the creation of reusable
|
61
|
+
components.
|
62
|
+
|
63
|
+
* Support for assigning compound objects from the request. Here
|
64
|
+
is an example:
|
65
|
+
|
66
|
+
class Article
|
67
|
+
property :title, String
|
68
|
+
property :body, String
|
69
|
+
end
|
70
|
+
|
71
|
+
<form>
|
72
|
+
<input type="text" name="article.title" />
|
73
|
+
<input type="text" name="article.body" />
|
74
|
+
</form>
|
75
|
+
|
76
|
+
article = request.assign('article')
|
77
|
+
|
78
|
+
Alternatively you can use the article[title] article[body]
|
79
|
+
notation.
|
80
|
+
|
81
|
+
* Added simple Benchmarking mixin.
|
82
|
+
|
83
|
+
* Added support for 'evolving' a single Og managed class. Useful
|
84
|
+
when you are in development mode and change your schema.
|
85
|
+
|
86
|
+
* Added support for session garbage collection.
|
87
|
+
|
88
|
+
* Many many small bug fixes in Og and Nitro.
|
89
|
+
|
90
|
+
|
1
91
|
== Version 0.22.0
|
2
92
|
|
3
93
|
A snapshot of the latest developments. Many requested features
|
data/lib/nitro.rb
CHANGED
@@ -1,21 +1,12 @@
|
|
1
1
|
# = Nitro
|
2
2
|
#
|
3
|
-
# Nitro is an efficient, yet simple engine for developing
|
4
|
-
# professional Web Applications using Ruby and Javascript.
|
5
|
-
# Nitro provides a robust infrastructure for scalable
|
6
|
-
# applications that can be distributed over a server
|
7
|
-
# cluster. However, Nitro can also power simple web
|
8
|
-
# applications for deployment on intranets or desktops.
|
9
|
-
#
|
10
|
-
# Nitro integrates the powerful Og Object-Relational mapping
|
11
|
-
# library.
|
12
|
-
#
|
13
|
-
# Copyright (c) 2004-2005, George Moschovitis (http://www.gmosx.com)
|
14
3
|
# Copyright (c) 2004-2005, Navel Ltd (http://www.navel.gr)
|
4
|
+
# Copyright (c) 2004-2005, George Moschovitis (http://www.gmosx.com)
|
15
5
|
#
|
16
|
-
# Nitro is copyrighted free software
|
17
|
-
# George Moschovitis (mailto:gm@navel.gr)
|
18
|
-
# standard BSD Licence. For details
|
6
|
+
# Nitro (http://www.nitrohq.com) is copyrighted free software
|
7
|
+
# created and maintained by George Moschovitis (mailto:gm@navel.gr)
|
8
|
+
# and released under the standard BSD Licence. For details
|
9
|
+
# consult the file doc/LICENCE.
|
19
10
|
|
20
11
|
require 'glue'
|
21
12
|
require 'glue/logger'
|
@@ -25,7 +16,7 @@ module Nitro
|
|
25
16
|
|
26
17
|
# The version.
|
27
18
|
|
28
|
-
Version = '0.
|
19
|
+
Version = '0.23.0'
|
29
20
|
|
30
21
|
# Library path.
|
31
22
|
|
@@ -75,5 +66,3 @@ module Nitro
|
|
75
66
|
end
|
76
67
|
|
77
68
|
end
|
78
|
-
|
79
|
-
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/adapter/cgi.rb
CHANGED
@@ -103,6 +103,9 @@ class CgiUtils
|
|
103
103
|
#
|
104
104
|
# Parameters in the form xxx[] are converted
|
105
105
|
# to arrays.
|
106
|
+
#
|
107
|
+
# Use the field.attr or field[attr] notation to pass
|
108
|
+
# compound objects.
|
106
109
|
|
107
110
|
def self.parse_query_string(query_string)
|
108
111
|
params = {}
|
@@ -117,12 +120,20 @@ class CgiUtils
|
|
117
120
|
val = CGI.unescape(val) unless val.nil?
|
118
121
|
|
119
122
|
if key =~ /(.*)\[\]$/
|
123
|
+
# Multiple values, for example a checkbox collection.
|
124
|
+
# Stored as an array.
|
120
125
|
if params.has_key?($1)
|
121
126
|
params[$1] << val
|
122
127
|
else
|
123
128
|
params[$1] = [val]
|
124
129
|
end
|
130
|
+
elsif key =~ /(.*)\[(.*)\]$/ or key =~ /(.*)\.(.*)$/
|
131
|
+
# A compound object with attributes.
|
132
|
+
# Stored as a Hash.
|
133
|
+
params[$1] ||= {}
|
134
|
+
params[$1][$2] = val
|
125
135
|
else
|
136
|
+
# Standard single valued parameter.
|
126
137
|
params[key] = val.nil? ? nil : val
|
127
138
|
end
|
128
139
|
end
|
@@ -26,7 +26,11 @@ class Webrick
|
|
26
26
|
else
|
27
27
|
wblog = STDERR
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
|
+
webrick_options = server.options.dup
|
31
|
+
require 'webrick/https' if webrick_options[:SSLEnable]
|
32
|
+
|
33
|
+
webrick_options.update(
|
30
34
|
:BindAddress => server.address,
|
31
35
|
:Port => server.port,
|
32
36
|
:DocumentRoot => server.public_root,
|
@@ -35,6 +39,7 @@ class Webrick
|
|
35
39
|
[wblog, WEBrick::AccessLog::REFERER_LOG_FORMAT]
|
36
40
|
]
|
37
41
|
)
|
42
|
+
@webrick = WEBrick::HTTPServer.new(webrick_options)
|
38
43
|
|
39
44
|
trap('INT') { @webrick.shutdown }
|
40
45
|
|
@@ -159,3 +164,4 @@ end
|
|
159
164
|
end
|
160
165
|
|
161
166
|
# * George Moschovitis <gm@navel.gr>
|
167
|
+
# * Guillaume Pierronnet <guillaume.pierronnet@laposte.net>
|
data/lib/nitro/caching/stores.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
# * George Moschovitis <gm@navel.gr>
|
2
|
-
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: stores.rb 182 2005-07-22 10:07:50Z gmosx $
|
4
|
-
|
5
1
|
require 'fileutils'
|
6
2
|
|
7
|
-
require '
|
3
|
+
require 'mega/synchash'
|
8
4
|
|
9
5
|
module Nitro
|
10
6
|
|
@@ -14,7 +10,7 @@ module Caching
|
|
14
10
|
|
15
11
|
# Cached fragments are stored in memory.
|
16
12
|
|
17
|
-
class MemoryStore <
|
13
|
+
class MemoryStore < SyncHash
|
18
14
|
|
19
15
|
def read(name, options = {})
|
20
16
|
self[name]
|
@@ -82,3 +78,5 @@ module Caching
|
|
82
78
|
end
|
83
79
|
|
84
80
|
end
|
81
|
+
|
82
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/compiler.rb
CHANGED
data/lib/nitro/context.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'nano/object/assign_with'
|
2
|
+
|
1
3
|
require 'nitro/request'
|
2
4
|
require 'nitro/response'
|
3
5
|
require 'nitro/render'
|
@@ -79,23 +81,28 @@ class Context
|
|
79
81
|
EXCLUDED_PARAMETERS = %w{ oid name }
|
80
82
|
|
81
83
|
def fill(obj, name = nil)
|
82
|
-
#
|
84
|
+
# If an class is passed create an instance.
|
83
85
|
obj = obj.new if obj.is_a?(Class)
|
84
86
|
|
85
87
|
@params.each do |param, val|
|
86
88
|
begin
|
87
89
|
# gmosx: DO NOT escape by default !!!
|
88
90
|
if not EXCLUDED_PARAMETERS.include?(param)
|
89
|
-
|
91
|
+
if val.is_a? Hash
|
92
|
+
obj.send("__force_hash_#{param}", val)
|
93
|
+
else
|
94
|
+
obj.send("__force_#{param}", val)
|
95
|
+
end
|
90
96
|
end
|
91
|
-
rescue NameError
|
97
|
+
rescue NameError => ex
|
92
98
|
next
|
93
99
|
end
|
94
100
|
end
|
95
|
-
|
101
|
+
|
96
102
|
return obj
|
97
103
|
end
|
98
104
|
alias_method :populate, :fill
|
105
|
+
alias_method :assign, :fill
|
99
106
|
end
|
100
107
|
|
101
108
|
end
|
data/lib/nitro/controller.rb
CHANGED
data/lib/nitro/cookie.rb
CHANGED
@@ -8,13 +8,14 @@ class Cookie
|
|
8
8
|
attr_accessor :domain, :path, :secure
|
9
9
|
attr_accessor :comment, :max_age
|
10
10
|
|
11
|
-
|
11
|
+
def initialize(name = nil, value = nil, expires = nil)
|
12
12
|
@name = name
|
13
13
|
@value = value
|
14
|
+
self.expires = expires
|
14
15
|
@version = 0 # Netscape Cookie
|
15
16
|
@path = '/' # gmosx: KEEP this!
|
16
|
-
@domain = @secure = @comment = @max_age =
|
17
|
-
@
|
17
|
+
@domain = @secure = @comment = @max_age = nil
|
18
|
+
@comment_url = @discard = @port = nil
|
18
19
|
end
|
19
20
|
|
20
21
|
def expires=(t)
|
data/lib/nitro/dispatcher.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'nano/object/singleton_class'
|
2
2
|
|
3
3
|
require 'nitro/controller'
|
4
4
|
require 'nitro/routing'
|
@@ -86,6 +86,12 @@ class Dispatcher
|
|
86
86
|
end
|
87
87
|
|
88
88
|
auto_mixin(c)
|
89
|
+
|
90
|
+
# Perform mount-time initialization of the controller.
|
91
|
+
|
92
|
+
if c.respond_to? :mounted
|
93
|
+
c.mounted
|
94
|
+
end
|
89
95
|
|
90
96
|
# Try to setup a template_root if none is defined:
|
91
97
|
|
@@ -8,6 +8,9 @@ class Dispatcher
|
|
8
8
|
|
9
9
|
# An alternative dispatching algorithm that handles
|
10
10
|
# implicit nice urls. Subdirectories are not supported.
|
11
|
+
#
|
12
|
+
# Returns the dispatcher class, the action name and the
|
13
|
+
# base url. For the root path, the base url is nil.
|
11
14
|
|
12
15
|
def dispatch(path, context)
|
13
16
|
path = route(path, context)
|
@@ -18,7 +21,7 @@ class Dispatcher
|
|
18
21
|
if klass = controller_class_for("/#{parts.first}")
|
19
22
|
base = "/#{parts.shift}"
|
20
23
|
else
|
21
|
-
base =
|
24
|
+
base = nil
|
22
25
|
klass = controller_class_for(ROOT)
|
23
26
|
end
|
24
27
|
|
@@ -30,6 +33,8 @@ class Dispatcher
|
|
30
33
|
context.headers['QUERY_STRING'] = "#{parts.join(';')};#{context.headers['QUERY_STRING']}"
|
31
34
|
end
|
32
35
|
|
36
|
+
base = nil if base == ROOT
|
37
|
+
|
33
38
|
return klass, "#{action}_action", base
|
34
39
|
end
|
35
40
|
end
|
data/lib/nitro/element.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
module Nitro
|
4
|
+
|
5
|
+
module BenchmarkMixin
|
6
|
+
|
7
|
+
def benchmark(message = 'Benchmarking')
|
8
|
+
real = Benchmark.realtime { yield }
|
9
|
+
Logger.info "#{message}: time = #{'%.5f' % real} ms."
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/mixin/form.rb
CHANGED
@@ -1,88 +1,204 @@
|
|
1
|
-
require '
|
1
|
+
require 'nano/inflect'
|
2
2
|
|
3
3
|
module Nitro
|
4
4
|
|
5
5
|
# A collection of useful helpers for creating and manipulating
|
6
6
|
# Forms.
|
7
|
+
#--
|
8
|
+
# FIXME: cleanup this file, move stuff to scaffold, allow
|
9
|
+
# overrides.
|
10
|
+
#++
|
7
11
|
|
8
12
|
module FormMixin
|
9
13
|
|
10
|
-
|
11
|
-
|
14
|
+
def self.included(base)
|
15
|
+
base.send :include, XhtmlMixin
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Render a standard form for the given Object. The object
|
21
|
+
# should include attribute metadata.
|
22
|
+
#--
|
23
|
+
# TODO: get info, for example localization mode from session,
|
24
|
+
# if this module is mixed in a Render.
|
25
|
+
#++
|
26
|
+
|
27
|
+
def form_for(obj, options = {})
|
28
|
+
method = options.fetch(:method, 'post')
|
29
|
+
action = options.fetch(:action, "save_#{obj.class.name.underscore}")
|
30
|
+
submit = options.fetch(:submit, 'Save')
|
31
|
+
|
32
|
+
action = "#{@base}/#{action}" unless action =~ /\//
|
33
|
+
|
34
|
+
str = %{<form action="#{action}" method="post">}
|
35
|
+
if obj.oid
|
36
|
+
str << %{
|
37
|
+
<input type="hidden" name="oid" value="#{obj.oid}" />}
|
38
|
+
end
|
39
|
+
str << %{
|
40
|
+
#{tags_for(obj, options)}
|
41
|
+
<br />
|
42
|
+
<input type="submit" value="#{submit}" /> or <a href="#@base/#{Scaffolding.class_to_list(obj.class)}">Cancel</a>
|
43
|
+
</form>
|
44
|
+
}
|
45
|
+
return str
|
46
|
+
end
|
47
|
+
|
48
|
+
# Render a standard form tags for the given Object. The object
|
49
|
+
# should include attribute metadata.
|
50
|
+
#
|
12
51
|
# If show_all is false then apply field filtering.
|
13
52
|
#
|
53
|
+
# For extra flexibility and to keep semantics this helper
|
54
|
+
# emits a <dl> structure. You can use CSS to style the
|
55
|
+
# list to fit your overal design.
|
56
|
+
#
|
14
57
|
# Example:
|
15
58
|
#
|
16
59
|
# <p>
|
17
60
|
# <form name="test">
|
18
|
-
# #{
|
61
|
+
# #{tags_for entry}
|
19
62
|
# </form>
|
20
63
|
# </p>
|
21
|
-
#--
|
22
|
-
# TODO: get info, for example localization mode from session,
|
23
|
-
# if this module is mixed in a Render.
|
24
|
-
#++
|
25
64
|
|
26
|
-
def
|
27
|
-
str =
|
65
|
+
def tags_for(obj, options = {})
|
66
|
+
str = prologue()
|
28
67
|
|
29
68
|
for p in obj.class.properties
|
30
|
-
unless
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
<input type="text" id="#{p.symbol}" name="#{p.symbol}" value="#{obj.send(p.symbol)}" />
|
40
|
-
</dd>
|
41
|
-
}
|
42
|
-
elsif p.klass.ancestors.include?(String)
|
43
|
-
str << %{
|
44
|
-
<dt><label for="#{p.symbol}">#{p.symbol}</label></dt>
|
45
|
-
<dd>
|
46
|
-
}
|
47
|
-
val = obj.send(p.symbol)
|
48
|
-
if :textarea == p.meta[:ui]
|
49
|
-
str << %{
|
50
|
-
<textarea id="#{p.symbol}" name="#{p.symbol}">#{val}</textarea>
|
51
|
-
}
|
69
|
+
next if :oid == p.symbol unless options[:all]
|
70
|
+
ancestors = p.klass.ancestors
|
71
|
+
if ancestors.include?(Numeric)
|
72
|
+
unless p.meta[:relation]
|
73
|
+
str << field_tag(obj, p, options)
|
74
|
+
end
|
75
|
+
elsif ancestors.include?(String)
|
76
|
+
if p.metadata[:editor] == :textarea
|
77
|
+
str << textarea_tag(obj, p, options)
|
52
78
|
else
|
53
|
-
str <<
|
54
|
-
<input type="text" id="#{p.symbol}" name="#{p.symbol}" value="#{val}" />
|
55
|
-
}
|
79
|
+
str << field_tag(obj, p, options)
|
56
80
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
81
|
+
elsif ancestors.include?(TrueClass)
|
82
|
+
str << checkbox_tag(obj, p, options)
|
83
|
+
elsif ancestors.include?(Time)
|
84
|
+
str << datetime_tag(obj, p, options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
for rel in obj.class.relations
|
89
|
+
case rel
|
90
|
+
when Og::BelongsTo
|
91
|
+
str << belongs_to_tag(obj, rel, options)
|
92
|
+
when Og::HasMany
|
93
|
+
str << has_many_tag(obj, rel, options)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
str << epilogue()
|
98
|
+
|
99
|
+
return str
|
100
|
+
end
|
101
|
+
|
102
|
+
def belongs_to_tag(obj, rel, options)
|
103
|
+
entities = rel.target_class.all
|
104
|
+
labels = entities.map { |e| e.to_s }
|
105
|
+
values = entities.map { |e| e.oid }
|
106
|
+
element(
|
107
|
+
label(rel.name),
|
108
|
+
%{
|
109
|
+
<select id="#{rel.name}" name="#{rel.name}">
|
110
|
+
#{options(:labels => labels, :values => values, :selected => 1)}
|
111
|
+
</select>
|
112
|
+
}
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
def has_many_tag(obj, rel, options)
|
117
|
+
entities = obj.send(rel.target_plural_name) if obj.saved?
|
118
|
+
unless entities.empty?
|
119
|
+
str = entities.inject('') do |acc, e|
|
120
|
+
acc << "<tr><td>#{e.to_edit_link(@base)}</td></tr>"
|
121
|
+
end
|
122
|
+
str = "<table>#{str}</table>"
|
123
|
+
else
|
124
|
+
str = 'No entities found.<br /><br />'
|
125
|
+
end
|
126
|
+
element(
|
127
|
+
label(rel.name) + ' <a href="#">Add</a>',
|
128
|
+
%{
|
129
|
+
#{str}
|
130
|
+
}
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
def field_tag(obj, p, options)
|
135
|
+
%{
|
136
|
+
<dt>#{label(p.symbol)}</dt>
|
63
137
|
<dd>
|
64
|
-
<input type="
|
138
|
+
<input type="text" id="#{p.symbol}" name="#{p.symbol}" value="#{obj.send(p.symbol)}" style="width: 250px" />
|
65
139
|
</dd>
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
def textarea_tag(obj, p, options)
|
144
|
+
%{
|
145
|
+
<dt>#{label(p.symbol)}</dt>
|
70
146
|
<dd>
|
71
|
-
<
|
147
|
+
<textarea id="#{p.symbol}" name="#{p.symbol}">#{obj.send(p.symbol)}</textarea>
|
72
148
|
</dd>
|
73
|
-
|
74
|
-
|
75
|
-
end
|
149
|
+
}
|
150
|
+
end
|
76
151
|
|
77
|
-
|
78
|
-
|
152
|
+
def checkbox_tag(obj, p, options)
|
153
|
+
%{
|
154
|
+
<dt>
|
155
|
+
<input type="checkbox" id="#{p.symbol}" name="#{p.symbol}" />
|
156
|
+
#{label(p.symbol)}
|
157
|
+
</dt>
|
158
|
+
<dd>#{}</dd>
|
159
|
+
}
|
160
|
+
end
|
79
161
|
|
80
|
-
|
162
|
+
def datetime_tag(obj, p, options)
|
163
|
+
element(
|
164
|
+
label(p.symbol),
|
165
|
+
datetime_select(obj.send(p.symbol), :name => p.symbol.to_s)
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
def label(sym)
|
170
|
+
%|<label for="#{sym}">#{sym.to_s.humanize}</label>|
|
171
|
+
end
|
172
|
+
|
173
|
+
# :section: Relations.
|
174
|
+
|
175
|
+
|
176
|
+
# :section: General formating methods. Override to customize.
|
177
|
+
|
178
|
+
# Emit form prologue.
|
179
|
+
|
180
|
+
def prologue
|
181
|
+
'<dl>'
|
182
|
+
end
|
183
|
+
|
184
|
+
# Emit form epilogue.
|
185
|
+
|
186
|
+
def epilogue
|
187
|
+
'</dl>'
|
81
188
|
end
|
82
|
-
alias_method :build_form, :form_for
|
83
189
|
|
190
|
+
# Emit a form element.
|
191
|
+
|
192
|
+
def element(label, element)
|
193
|
+
%{
|
194
|
+
<dt>#{label}</dt>
|
195
|
+
<dd>
|
196
|
+
#{element}
|
197
|
+
</dd>
|
198
|
+
}
|
199
|
+
end
|
84
200
|
end
|
85
201
|
|
86
202
|
end
|
87
203
|
|
88
|
-
# * George Moschovitis
|
204
|
+
# * George Moschovitis <gm@navel.gr>
|