nemo 0.1.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/INSTALL +5 -0
- data/README +25 -0
- data/lib/nemo.rb +47 -0
- data/lib/nemo/components.rb +4 -0
- data/lib/nemo/components/calendar.rb +191 -0
- data/lib/nemo/components/confirm.rb +30 -0
- data/lib/nemo/components/multiattr.rb +93 -0
- data/lib/nemo/examples.rb +5 -0
- data/lib/nemo/examples/date_picker.rb +74 -0
- data/lib/nemo/examples/person_editor.rb +293 -0
- data/lib/nemo/metaobject.rb +5 -0
- data/lib/nemo/metaobject/acollector.rb +79 -0
- data/lib/nemo/metaobject/attributes.rb +282 -0
- data/lib/nemo/metaobject/metaobject.rb +86 -0
- data/lib/nemo/metaobject/validation.rb +32 -0
- data/lib/nemo/util.rb +31 -0
- data/lib/nemo/util/accessors.rb +85 -0
- data/lib/nemo/util/blankslate.rb +6 -0
- data/lib/nemo/util/collector.rb +54 -0
- data/lib/nemo/util/date.rb +41 -0
- data/lib/nemo/visitors.rb +4 -0
- data/lib/nemo/visitors/editor.rb +186 -0
- data/lib/nemo/visitors/viewer.rb +63 -0
- data/lib/nemo/visitors/visitor.rb +14 -0
- data/nemo.gemspec +23 -0
- data/nemo_rdoc.bat +1 -0
- data/test/date_picker.rb +14 -0
- data/test/nemo/calendar.css +26 -0
- data/test/nemo/calendar.gif +0 -0
- data/test/nemo/person_editor.css +40 -0
- data/test/person_editor.rb +14 -0
- data/test/unit_test.rb +128 -0
- metadata +86 -0
data/INSTALL
ADDED
data/README
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
== Nemo
|
2
|
+
|
3
|
+
== Copyright and License
|
4
|
+
|
5
|
+
Copyright (c) 2004, 2005 by Kevin Howe (kh@newclear.ca).
|
6
|
+
|
7
|
+
Released under the same terms of license as Ruby.
|
8
|
+
|
9
|
+
== Introduction
|
10
|
+
|
11
|
+
Nemo is a Ruby port of Mewa
|
12
|
+
|
13
|
+
http://www.adrian-lienhard.ch/files/mewa.pdf
|
14
|
+
|
15
|
+
using Michael Neumann's Wee
|
16
|
+
|
17
|
+
http://rubyforge.org/projects/wee
|
18
|
+
|
19
|
+
as the Seaside2 equivalent.
|
20
|
+
|
21
|
+
Nemo is a web-application platform that uses object metadata to automatically construct web-interfaces (Editors and Viewers). It is highly object-oriented with strong emphasis on reusable components.
|
22
|
+
|
23
|
+
== Status
|
24
|
+
|
25
|
+
Nemo is build on top of Wee, and should not be considered production ready until Wee itself is ready.
|
data/lib/nemo.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Ruby port of Mewa
|
2
|
+
#
|
3
|
+
# Meta-level Architecture for Generic Web-Application Construction
|
4
|
+
#
|
5
|
+
# http://www.adrian-lienhard.ch/files/mewa.pdf
|
6
|
+
#
|
7
|
+
# Author:: Kevin Howe
|
8
|
+
# License:: Distributes under the same terms as Ruby
|
9
|
+
#
|
10
|
+
module Nemo
|
11
|
+
|
12
|
+
Version = "0.1.0"
|
13
|
+
|
14
|
+
require 'date'
|
15
|
+
require 'wee'
|
16
|
+
require 'wee/utils/cache'
|
17
|
+
require 'nemo/util'
|
18
|
+
require 'nemo/components'
|
19
|
+
require 'nemo/metaobject'
|
20
|
+
require 'nemo/visitors'
|
21
|
+
require 'nemo/examples'
|
22
|
+
|
23
|
+
module_function
|
24
|
+
|
25
|
+
# Define a new MetaObject
|
26
|
+
#
|
27
|
+
def metaobject_for(*args, &block)
|
28
|
+
Nemo::MetaObject::MetaObject.new(*args, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class Wee::HtmlCanvas
|
34
|
+
|
35
|
+
# Nemo's standard calendar button
|
36
|
+
#
|
37
|
+
# class MyComponent < Wee::Component
|
38
|
+
# def render
|
39
|
+
# r.nemo_calendar_button.callback {}
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
def nemo_calendar_button
|
44
|
+
image_button.src('/nemo/calendar.gif').width(16).height(16).alt('Calendar').style('border: 0')
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# Browsable Calendar component
|
2
|
+
#
|
3
|
+
# =Use as a date picker
|
4
|
+
# By default, clicking a day will <tt>answer</tt> with an associated Date object.
|
5
|
+
#
|
6
|
+
# =Use as a calendar viewer
|
7
|
+
# No <tt>answer</tt> is given when in browse mode:
|
8
|
+
#
|
9
|
+
# call( Nemo::Components::MiniCalendar.new(date).browse )
|
10
|
+
#
|
11
|
+
# =CSS Styles
|
12
|
+
# Overload render_styles to add a custom <link> or <style> tag.
|
13
|
+
#
|
14
|
+
class Nemo::Components::MiniCalendar < Wee::Component
|
15
|
+
|
16
|
+
# Browse mode: if true, no answer will be given
|
17
|
+
attr_accessor :browse
|
18
|
+
|
19
|
+
# Holds the current chosen date
|
20
|
+
attr_accessor :date
|
21
|
+
|
22
|
+
# Initialize the MiniCalendar
|
23
|
+
#
|
24
|
+
def initialize(date=Date.today)
|
25
|
+
super()
|
26
|
+
@month = Date.new(date.year, date.month, 1)
|
27
|
+
@day = date
|
28
|
+
@browse = false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Backtrack state
|
32
|
+
#
|
33
|
+
def backtrack_state(snapshot)
|
34
|
+
super
|
35
|
+
snapshot.add(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set to browse-only (no answer will be given)
|
39
|
+
#
|
40
|
+
def browse(value=true)
|
41
|
+
@browse = (value && true)
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# True if in browser-only mode
|
46
|
+
#
|
47
|
+
def browse?
|
48
|
+
@browse
|
49
|
+
end
|
50
|
+
|
51
|
+
# True if the given date is the currently selected month
|
52
|
+
#
|
53
|
+
def current_month?(date)
|
54
|
+
Date.new(date.year, date.month, 1) == @month
|
55
|
+
end
|
56
|
+
|
57
|
+
# True if the given date is the currently selected day
|
58
|
+
#
|
59
|
+
def selected_day?(date)
|
60
|
+
date == @day
|
61
|
+
end
|
62
|
+
|
63
|
+
# Date object representing the previous month
|
64
|
+
#
|
65
|
+
def prev_month
|
66
|
+
@month << 1
|
67
|
+
end
|
68
|
+
|
69
|
+
# Date object representing the next month
|
70
|
+
#
|
71
|
+
def next_month
|
72
|
+
@month >> 1
|
73
|
+
end
|
74
|
+
|
75
|
+
# String to be displayed as the month heading
|
76
|
+
#
|
77
|
+
def month_heading
|
78
|
+
@month.strftime('%B %Y')
|
79
|
+
end
|
80
|
+
|
81
|
+
# String to be displayed indicating the current date
|
82
|
+
#
|
83
|
+
def today_string
|
84
|
+
Date.today.strftime('Today is %A, %b %d %Y')
|
85
|
+
end
|
86
|
+
|
87
|
+
# Render a given day
|
88
|
+
#
|
89
|
+
def render_day(date)
|
90
|
+
if current_month?(date)
|
91
|
+
selected_day?(date) ? render_selected_day(date) : render_month_day(date)
|
92
|
+
else
|
93
|
+
render_other_day(date)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Render a day of the currently selected month
|
98
|
+
#
|
99
|
+
def render_month_day(date)
|
100
|
+
r.table_data { r.anchor.callback { save(date) }.with(date.day) }
|
101
|
+
end
|
102
|
+
|
103
|
+
# Render the currently selected day
|
104
|
+
#
|
105
|
+
def render_selected_day(date)
|
106
|
+
r.table_data.style('border: 1px solid black').with do
|
107
|
+
r.anchor.style('font-weight: bold').callback { save(date) }.with(date.day)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Render days of the previous or next month
|
112
|
+
#
|
113
|
+
def render_other_day(date)
|
114
|
+
r.table_data do
|
115
|
+
r.anchor.style('color: silver').callback { save(date) }.with(date.day)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Render CSS styles
|
120
|
+
#
|
121
|
+
def render_styles
|
122
|
+
r.link.type('text/css').rel('stylesheet').href('/nemo/calendar.css')
|
123
|
+
end
|
124
|
+
|
125
|
+
# Render Calender header
|
126
|
+
#
|
127
|
+
def render_header
|
128
|
+
r.table_row do
|
129
|
+
r.table_header.colspan(4).with { r.encoded_text(month_heading) }
|
130
|
+
r.table_header { r.anchor.callback { go_prev }.with(prev_month.strftime('%b')) }
|
131
|
+
r.table_header { r.anchor.callback { go_next }.with(next_month.strftime('%b')) }
|
132
|
+
r.table_header { browse? ? r.space : r.anchor.callback { back }.style('color: black').with('X') }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Render Calendar footer
|
137
|
+
#
|
138
|
+
def render_footer
|
139
|
+
r.table_row { r.table_header.colspan(7).with { r.encoded_text(today_string) } }
|
140
|
+
end
|
141
|
+
|
142
|
+
# Render Calendar
|
143
|
+
#
|
144
|
+
def render
|
145
|
+
r.html do
|
146
|
+
r.head { r.title('Calendar'); render_styles }
|
147
|
+
r.body do
|
148
|
+
r.text(sprintf('<!--Month: %s, Day: %s-->', @month, @day))
|
149
|
+
r.table { r.table_row { r.table_header {
|
150
|
+
r.table do
|
151
|
+
render_header
|
152
|
+
r.table_row { Date::ABBR_DAYNAMES.each { |day| r.table_header(day) } }
|
153
|
+
@month.calendar.each do |week|
|
154
|
+
r.table_row do
|
155
|
+
week.each { |day| render_day(day) }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
render_footer
|
159
|
+
end
|
160
|
+
}}}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Return without changes
|
166
|
+
#
|
167
|
+
def back
|
168
|
+
answer nil unless browse?
|
169
|
+
end
|
170
|
+
|
171
|
+
# Select the previous month
|
172
|
+
#
|
173
|
+
def go_prev
|
174
|
+
@month = prev_month
|
175
|
+
end
|
176
|
+
|
177
|
+
# Select the next month
|
178
|
+
#
|
179
|
+
def go_next
|
180
|
+
@month = next_month
|
181
|
+
end
|
182
|
+
|
183
|
+
# Save the given day
|
184
|
+
#
|
185
|
+
def save(day)
|
186
|
+
@day = day
|
187
|
+
@month = Date.new(day.year, day.month, 1)
|
188
|
+
answer(day) unless browse?
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Confirmation dialog box
|
2
|
+
#
|
3
|
+
# Displays a message with OK(true) and Cancel(false) buttons.
|
4
|
+
#
|
5
|
+
class Nemo::Components::ConfirmDialog < Wee::Component
|
6
|
+
|
7
|
+
attr_accessor :text
|
8
|
+
|
9
|
+
def initialize(text)
|
10
|
+
super()
|
11
|
+
@text = text
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
r.html do
|
16
|
+
r.head { r.title('Confirm') }
|
17
|
+
r.body.style('text-align: center').with do
|
18
|
+
r.break
|
19
|
+
r.encode_text(@text)
|
20
|
+
r.form do
|
21
|
+
r.submit_button.value('OK').callback{ answer true }
|
22
|
+
r.space
|
23
|
+
r.submit_button.value('Cancel').callback{ answer false }
|
24
|
+
end
|
25
|
+
r.break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Multiple Attribute container
|
2
|
+
#
|
3
|
+
# Interface to Add/Edit/Delete/View the related data of the given attribute.
|
4
|
+
#
|
5
|
+
class Nemo::Components::MultipleAttributeContainer < Wee::Component
|
6
|
+
|
7
|
+
attr_accessor :attribute, :components
|
8
|
+
|
9
|
+
def initialize(a)
|
10
|
+
super()
|
11
|
+
@attribute = a
|
12
|
+
@components = Array.new
|
13
|
+
a.cache.each { |obj| @components << viewer_for(obj.metaobject) }
|
14
|
+
@components << create_empty_editor
|
15
|
+
end
|
16
|
+
|
17
|
+
def children
|
18
|
+
@components
|
19
|
+
end
|
20
|
+
|
21
|
+
def backtrack_state(snapshot)
|
22
|
+
super
|
23
|
+
snapshot.add(self.children)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(component)
|
27
|
+
@attribute.remove_from_cache(component.metaobject.baseobject)
|
28
|
+
@components.delete(component)
|
29
|
+
@components.pop
|
30
|
+
@components << create_empty_editor
|
31
|
+
end
|
32
|
+
|
33
|
+
def switch_to_edit_mode(viewer)
|
34
|
+
viewer.viewer = false
|
35
|
+
viewer.call( editor_for(viewer.metaobject) )
|
36
|
+
viewer.viewer = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_empty_editor
|
40
|
+
metaobject = @attribute.base_class.new.metaobject
|
41
|
+
if @components.size < @attribute.item_limit
|
42
|
+
editor = editor_for(metaobject)
|
43
|
+
answer = Wee::AnswerDecoration.new
|
44
|
+
answer.on_answer = method(:on_edit)
|
45
|
+
editor.add_decoration(answer)
|
46
|
+
editor
|
47
|
+
else
|
48
|
+
Nemo::Visitors::Visitor.new(metaobject) # placeholder
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_edit(obj)
|
53
|
+
if obj.nil?
|
54
|
+
@components.pop
|
55
|
+
@components << create_empty_editor
|
56
|
+
else
|
57
|
+
@attribute.add_to_cache(obj)
|
58
|
+
@components.pop
|
59
|
+
@components << viewer_for(obj.metaobject)
|
60
|
+
@components << create_empty_editor
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def editor_for(metaobject)
|
65
|
+
obj = Nemo::Visitors::Editor.new(metaobject)
|
66
|
+
obj.subform = true
|
67
|
+
obj
|
68
|
+
end
|
69
|
+
|
70
|
+
def viewer_for(metaobject)
|
71
|
+
Nemo::Visitors::Viewer.new(metaobject)
|
72
|
+
end
|
73
|
+
|
74
|
+
def render
|
75
|
+
@components.each do |component|
|
76
|
+
if component.viewer?
|
77
|
+
r.render(component)
|
78
|
+
r.submit_button.value('edit').callback{ switch_to_edit_mode(component) }
|
79
|
+
if @attribute.item_delete?
|
80
|
+
r.space
|
81
|
+
r.submit_button.value('delete').callback { delete(component) }
|
82
|
+
end
|
83
|
+
if @attribute.item_limit > 1
|
84
|
+
r.break
|
85
|
+
r.break
|
86
|
+
end
|
87
|
+
else
|
88
|
+
r.render(component)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Demonstrates the MiniCalendar component
|
2
|
+
#
|
3
|
+
module Nemo::Examples::DatePicker; end
|
4
|
+
|
5
|
+
# Set the value of a text field to a date string using a MiniCalendar.
|
6
|
+
#
|
7
|
+
class Nemo::Examples::DatePicker::Root < Wee::Component
|
8
|
+
|
9
|
+
# Holds the currently chosen date
|
10
|
+
attr_accessor :date
|
11
|
+
|
12
|
+
# Initialize with a Date object (defaults to today)
|
13
|
+
#
|
14
|
+
def initialize(date=Date.today)
|
15
|
+
super()
|
16
|
+
@date = date
|
17
|
+
end
|
18
|
+
|
19
|
+
# Backtrack state
|
20
|
+
#
|
21
|
+
def backtrack_state(snapshot)
|
22
|
+
super
|
23
|
+
snapshot.add(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Render CSS styles
|
27
|
+
#
|
28
|
+
def render_styles
|
29
|
+
r.link.type('text/css').rel('stylesheet').href('/nemo/calendar.css')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Render date field, allow user to choose a Date using a MiniCalendar
|
33
|
+
#
|
34
|
+
def render
|
35
|
+
super
|
36
|
+
r.html do
|
37
|
+
r.head { r.title('Calendar Demo'); render_styles }
|
38
|
+
r.body do
|
39
|
+
r.form do
|
40
|
+
r.table { r.table_row { r.table_header {
|
41
|
+
r.table do
|
42
|
+
r.table_row { r.table_header('Calendar Demo') }
|
43
|
+
r.table_row { r.table_data {
|
44
|
+
r.text_input.value(@date.strftime('%b %d, %Y')).callback { |input| @date = begin Date.parse(input) rescue Date.today end }
|
45
|
+
r.space
|
46
|
+
r.nemo_calendar_button.callback { calendar }
|
47
|
+
}}
|
48
|
+
end
|
49
|
+
}}}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Call the calendar component
|
56
|
+
#
|
57
|
+
def calendar
|
58
|
+
if date = call( Nemo::Components::MiniCalendar.new(@date) )
|
59
|
+
@date = date
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
class Nemo::Examples::DatePicker::Session < Wee::Session
|
66
|
+
|
67
|
+
def initialize
|
68
|
+
super do
|
69
|
+
self.root_component = Nemo::Examples::DatePicker::Root.new
|
70
|
+
self.page_store = Wee::Utils::LRUCache.new(25) # backtrack up to 25 pages
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|