nemo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/INSTALL ADDED
@@ -0,0 +1,5 @@
1
+ = Installation
2
+
3
+ 1. Download and Install RubyGems: http://rubygems.rubyforge.org
4
+
5
+ 2. Install Wee: <tt>gem install nemo</tt>
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,4 @@
1
+ module Nemo::Components; end
2
+ require 'nemo/components/calendar'
3
+ require 'nemo/components/confirm'
4
+ require 'nemo/components/multiattr'
@@ -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,5 @@
1
+ # Example Applications
2
+ #
3
+ module Nemo::Examples; end
4
+ require 'nemo/examples/date_picker'
5
+ require 'nemo/examples/person_editor'
@@ -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