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 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