win_gui 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +43 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/book_code/early_success/bundle.rb +34 -0
- data/book_code/early_success/english.txt +1 -0
- data/book_code/early_success/jruby_basics.rb +47 -0
- data/book_code/early_success/windows_basics.rb +97 -0
- data/book_code/guessing/locknote.rb +379 -0
- data/book_code/guessing/monkeyshines.rb +14 -0
- data/book_code/guessing/note.rb +120 -0
- data/book_code/guessing/note_spec.rb +175 -0
- data/book_code/guessing/replay.rb +21 -0
- data/book_code/guessing/seed.rb +9 -0
- data/book_code/guessing/spec_helper.rb +69 -0
- data/book_code/guessing/windows_gui.rb +247 -0
- data/book_code/home_stretch/junquenote.rb +151 -0
- data/book_code/home_stretch/locknote.rb +180 -0
- data/book_code/home_stretch/note.rb +144 -0
- data/book_code/home_stretch/note_spec.rb +191 -0
- data/book_code/home_stretch/spec_helper.rb +55 -0
- data/book_code/home_stretch/swing_gui.rb +50 -0
- data/book_code/home_stretch/windows_gui.rb +232 -0
- data/book_code/junquenote/exports.sh +10 -0
- data/book_code/junquenote/jruby_mac.sh +10 -0
- data/book_code/junquenote/junquenote_app.rb +262 -0
- data/book_code/novite/Rakefile +10 -0
- data/book_code/novite/app/controllers/application.rb +18 -0
- data/book_code/novite/app/controllers/guests_controller.rb +28 -0
- data/book_code/novite/app/controllers/parties_controller.rb +77 -0
- data/book_code/novite/app/helpers/application_helper.rb +11 -0
- data/book_code/novite/app/helpers/guests_helper.rb +10 -0
- data/book_code/novite/app/helpers/parties_helper.rb +10 -0
- data/book_code/novite/app/models/guest.rb +11 -0
- data/book_code/novite/app/models/party.rb +32 -0
- data/book_code/novite/app/models/party_mailer.rb +19 -0
- data/book_code/novite/app/views/layouts/application.rhtml +44 -0
- data/book_code/novite/app/views/parties/new.html.erb +42 -0
- data/book_code/novite/app/views/parties/show.html.erb +43 -0
- data/book_code/novite/app/views/party_mailer/invite.erb +17 -0
- data/book_code/novite/config/boot.rb +117 -0
- data/book_code/novite/config/database.yml +19 -0
- data/book_code/novite/config/environment.rb +67 -0
- data/book_code/novite/config/environments/development.rb +29 -0
- data/book_code/novite/config/environments/production.rb +27 -0
- data/book_code/novite/config/environments/test.rb +30 -0
- data/book_code/novite/config/initializers/inflections.rb +18 -0
- data/book_code/novite/config/initializers/mime_types.rb +13 -0
- data/book_code/novite/config/routes.rb +47 -0
- data/book_code/novite/db/migrate/001_create_parties.rb +26 -0
- data/book_code/novite/db/migrate/002_create_guests.rb +23 -0
- data/book_code/novite/db/schema.rb +41 -0
- data/book_code/novite/log/empty.txt +0 -0
- data/book_code/novite/public/.htaccess +40 -0
- data/book_code/novite/public/404.html +38 -0
- data/book_code/novite/public/422.html +38 -0
- data/book_code/novite/public/500.html +38 -0
- data/book_code/novite/public/dispatch.cgi +10 -0
- data/book_code/novite/public/dispatch.fcgi +24 -0
- data/book_code/novite/public/dispatch.rb +18 -0
- data/book_code/novite/public/favicon.ico +0 -0
- data/book_code/novite/public/images/rails.png +0 -0
- data/book_code/novite/public/index.html +285 -0
- data/book_code/novite/public/javascripts/application.js +10 -0
- data/book_code/novite/public/javascripts/controls.js +971 -0
- data/book_code/novite/public/javascripts/dragdrop.js +980 -0
- data/book_code/novite/public/javascripts/effects.js +1128 -0
- data/book_code/novite/public/javascripts/prototype.js +4233 -0
- data/book_code/novite/public/robots.txt +5 -0
- data/book_code/novite/script/about +3 -0
- data/book_code/novite/script/console +3 -0
- data/book_code/novite/script/destroy +3 -0
- data/book_code/novite/script/generate +3 -0
- data/book_code/novite/script/performance/benchmarker +3 -0
- data/book_code/novite/script/performance/profiler +3 -0
- data/book_code/novite/script/performance/request +3 -0
- data/book_code/novite/script/plugin +3 -0
- data/book_code/novite/script/process/inspector +3 -0
- data/book_code/novite/script/process/reaper +3 -0
- data/book_code/novite/script/process/spawner +3 -0
- data/book_code/novite/script/runner +3 -0
- data/book_code/novite/script/server +3 -0
- data/book_code/novite/test/test_helper.rb +46 -0
- data/book_code/one_more_thing/applescript.rb +68 -0
- data/book_code/one_more_thing/note_spec.rb +50 -0
- data/book_code/one_more_thing/spec_helper.rb +17 -0
- data/book_code/one_more_thing/textedit-pure.rb +28 -0
- data/book_code/one_more_thing/textedit.applescript +26 -0
- data/book_code/one_more_thing/textedit.rb +32 -0
- data/book_code/one_more_thing/textnote.rb +87 -0
- data/book_code/simplify/junquenote.rb +48 -0
- data/book_code/simplify/locknote.rb +46 -0
- data/book_code/simplify/note.rb +35 -0
- data/book_code/simplify/note_spec.rb +28 -0
- data/book_code/simplify/swing_gui.rb +45 -0
- data/book_code/simplify/windows_gui.rb +232 -0
- data/book_code/simplify/windows_gui_spec.rb +35 -0
- data/book_code/story/invite.story +19 -0
- data/book_code/story/journal.txt +29 -0
- data/book_code/story/novite_stories.rb +156 -0
- data/book_code/story/party.rb +149 -0
- data/book_code/story/password.rb +61 -0
- data/book_code/story/password.story +26 -0
- data/book_code/story/rsvp.story +29 -0
- data/book_code/tables/TestTime.html +93 -0
- data/book_code/tables/TestTimeSample.html +63 -0
- data/book_code/tables/calculate_time.rb +39 -0
- data/book_code/tables/calculator.rb +108 -0
- data/book_code/tables/calculator_actions.rb +27 -0
- data/book_code/tables/calculator_spec.rb +47 -0
- data/book_code/tables/fit.rb +32 -0
- data/book_code/tables/matrix.rb +109 -0
- data/book_code/tables/pseudocode.rb +17 -0
- data/book_code/tubes/book_selenium.rb +67 -0
- data/book_code/tubes/book_watir.rb +60 -0
- data/book_code/tubes/dragdrop.html +81 -0
- data/book_code/tubes/html_capture.rb +33 -0
- data/book_code/tubes/joke_list.rb +67 -0
- data/book_code/tubes/list_spec.rb +41 -0
- data/book_code/tubes/search_spec.rb +32 -0
- data/book_code/tubes/selenium_example.rb +66 -0
- data/book_code/tubes/selenium_link.rb +23 -0
- data/book_code/tubes/web_server.rb +14 -0
- data/book_code/windows/wgui.rb +29 -0
- data/book_code/windows/wobj.rb +25 -0
- data/book_code/windows/wsh.rb +25 -0
- data/book_code/with_rspec/empty_spec.rb +13 -0
- data/book_code/with_rspec/junquenote.rb +60 -0
- data/book_code/with_rspec/locknote.rb +129 -0
- data/book_code/with_rspec/note_spec.rb +32 -0
- data/book_code/with_rspec/should_examples.rb +18 -0
- data/exp/exp.rb +6 -0
- data/exp/exp_encodings.rb +40 -0
- data/exp/exp_enum_windows.rb +60 -0
- data/exp/exp_quik.rb +38 -0
- data/exp/exp_wsh.rb +115 -0
- data/exp/old/windows_basics.rb +80 -0
- data/exp/old/wnote.rb +80 -0
- data/exp/old/wnote_spec.rb +20 -0
- data/features/step_definitions/win_gui_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/features/win_gui.feature +9 -0
- data/lib/note/java/jemmy.jar +0 -0
- data/lib/note/java/jnote.rb +48 -0
- data/lib/note/java/jruby_basics.rb +37 -0
- data/lib/note/java/junquenote_app.rb +262 -0
- data/lib/note/java/note_spec.rb +20 -0
- data/lib/note/win/locknote.rb +19 -0
- data/lib/note.rb +15 -0
- data/lib/win_gui/constants.rb +66 -0
- data/lib/win_gui/string_extensions.rb +24 -0
- data/lib/win_gui/win_gui.rb +274 -0
- data/lib/win_gui/window.rb +70 -0
- data/lib/win_gui.rb +3 -0
- data/spec/note/win/locknote_spec.rb +7 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/test_apps/locknote/LockNote.exe +0 -0
- data/spec/win_gui/string_extensions_spec.rb +61 -0
- data/spec/win_gui/win_gui_spec.rb +733 -0
- data/spec/win_gui/window_spec.rb +124 -0
- metadata +251 -0
@@ -0,0 +1,262 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
require 'java'
|
10
|
+
require 'rubygems'
|
11
|
+
require 'cheri/swing'
|
12
|
+
require 'crypt/gost'
|
13
|
+
|
14
|
+
include_class javax.swing.JOptionPane
|
15
|
+
|
16
|
+
class String
|
17
|
+
def encrypt(pw)
|
18
|
+
padded = pw + "\0" * [0, 8 - pw.length].max
|
19
|
+
Crypt::Gost.new(padded).encrypt_string(self)
|
20
|
+
rescue
|
21
|
+
return '(wrong password)'
|
22
|
+
end
|
23
|
+
|
24
|
+
def decrypt(pw)
|
25
|
+
padded = pw + "\0" * [0, 8 - pw.length].max
|
26
|
+
Crypt::Gost.new(padded).decrypt_string(self)
|
27
|
+
rescue
|
28
|
+
return '(wrong password)'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class JunqueNoteApp
|
33
|
+
include Cheri::Swing
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
swing[:auto]
|
37
|
+
|
38
|
+
@frame = swing.frame('JunqueNote') do |f|
|
39
|
+
size 400, 300
|
40
|
+
box_layout f, :Y_AXIS
|
41
|
+
|
42
|
+
menu_bar do
|
43
|
+
menu('File') do
|
44
|
+
menu_item('Open...') {on_click {open} }
|
45
|
+
menu_item('Change Password...') {on_click {change_password} }
|
46
|
+
menu_item('Save As...') {on_click {save_as} }
|
47
|
+
menu_item('Exit') {on_click {exit_app} }
|
48
|
+
end
|
49
|
+
|
50
|
+
menu('Edit') do
|
51
|
+
menu_item('Undo') {on_click {@text_area.text = @state.pop} }
|
52
|
+
menu_item('Cut') {on_click {@state.push(@text_area.text); @text_area.cut} }
|
53
|
+
menu_item('Copy') {on_click {@state.push(@text_area.text); @text_area.copy} }
|
54
|
+
menu_item('Paste') {on_click {@state.push(@text_area.text); @text_area.paste} }
|
55
|
+
|
56
|
+
menu_item('Find...') {on_click {find} }
|
57
|
+
menu_item('Find Exact Case...') {on_click {find :case} }
|
58
|
+
menu_item('Reverse Find...') {on_click {find :reverse} }
|
59
|
+
menu_item('Reverse Find Exact Case...') {on_click {find :case_reverse} }
|
60
|
+
menu_item('Find Next') {on_click {find @how, @term} }
|
61
|
+
end
|
62
|
+
|
63
|
+
menu('Help') do
|
64
|
+
menu_item('About JunqueNote...') {on_click {about} }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
@text_area = text_area('Welcome to JunqueNote!') do
|
69
|
+
on_key_pressed {|event| @state.push(@text_area.text); @dirty = true}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
@state = [@text_area.text]
|
74
|
+
@frame.visible = true
|
75
|
+
end
|
76
|
+
|
77
|
+
def request_filename(button = 'Save')
|
78
|
+
pane = JOptionPane.new \
|
79
|
+
'Please enter a filename',
|
80
|
+
JOptionPane::PLAIN_MESSAGE,
|
81
|
+
JOptionPane::OK_CANCEL_OPTION,
|
82
|
+
nil,
|
83
|
+
[button,'Cancel'].to_java,
|
84
|
+
button
|
85
|
+
pane.wants_input = true
|
86
|
+
pane.create_dialog(@frame, 'Input').show
|
87
|
+
|
88
|
+
button == pane.value ? pane.input_value : nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def save_as(filename = nil)
|
92
|
+
save_name = filename || request_filename
|
93
|
+
return if save_name.nil?
|
94
|
+
|
95
|
+
@filename = save_name
|
96
|
+
|
97
|
+
if @password.nil?
|
98
|
+
@password = JOptionPane.show_input_dialog "Please assign a password"
|
99
|
+
return if @password.nil?
|
100
|
+
confirmation = JOptionPane.show_input_dialog "Please confirm the password"
|
101
|
+
return if confirmation.nil?
|
102
|
+
|
103
|
+
if @password != confirmation
|
104
|
+
JOptionPane.show_message_dialog(
|
105
|
+
@frame,
|
106
|
+
"The password and confirmation don't match",
|
107
|
+
"Oops",
|
108
|
+
JOptionPane::YES_NO_OPTION)
|
109
|
+
|
110
|
+
return
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
File.delete @filename if File.exists? @filename
|
115
|
+
File.open @filename, 'wb' do |f|
|
116
|
+
plaintext = "JunqueNote\n#{@text_area.text.length}\n#{@text_area.text}"
|
117
|
+
encrypted = plaintext.encrypt(@password)
|
118
|
+
f.write encrypted
|
119
|
+
end
|
120
|
+
|
121
|
+
@dirty = false
|
122
|
+
end
|
123
|
+
|
124
|
+
def open
|
125
|
+
@filename = request_filename 'Open'
|
126
|
+
if @filename.nil?
|
127
|
+
@frame.dispose
|
128
|
+
return
|
129
|
+
end
|
130
|
+
|
131
|
+
@password = JOptionPane.show_input_dialog "Please enter the password"
|
132
|
+
if @password.nil?
|
133
|
+
@frame.dispose
|
134
|
+
return
|
135
|
+
end
|
136
|
+
|
137
|
+
encrypted = File.open(@filename, 'rb') {|f| f.read}
|
138
|
+
init, length, contents = encrypted.decrypt(@password).split($;, 3)
|
139
|
+
|
140
|
+
if init == 'JunqueNote'
|
141
|
+
contents = contents[0, length.to_i]
|
142
|
+
@text_area.text = contents
|
143
|
+
@state = [contents]
|
144
|
+
@dirty = false
|
145
|
+
else
|
146
|
+
JOptionPane.show_message_dialog(
|
147
|
+
@frame,
|
148
|
+
"The password doesn't match",
|
149
|
+
"Oops",
|
150
|
+
JOptionPane::YES_NO_OPTION)
|
151
|
+
|
152
|
+
@frame.dispose
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def change_password
|
157
|
+
old_password = JOptionPane.show_input_dialog "Please enter the password"
|
158
|
+
return if old_password.nil?
|
159
|
+
|
160
|
+
if old_password != @password
|
161
|
+
JOptionPane.show_message_dialog(
|
162
|
+
@frame,
|
163
|
+
"The password doesn't match",
|
164
|
+
"Oops",
|
165
|
+
JOptionPane::YES_NO_OPTION)
|
166
|
+
|
167
|
+
return
|
168
|
+
end
|
169
|
+
|
170
|
+
new_password = JOptionPane.show_input_dialog "Please enter a password"
|
171
|
+
return if new_password.nil?
|
172
|
+
confirmation = JOptionPane.show_input_dialog "Please confirm the password"
|
173
|
+
return if confirmation.nil?
|
174
|
+
|
175
|
+
if new_password != confirmation
|
176
|
+
JOptionPane.show_message_dialog(
|
177
|
+
@frame,
|
178
|
+
"The password and confirmation don't match",
|
179
|
+
"Oops",
|
180
|
+
JOptionPane::YES_NO_OPTION)
|
181
|
+
|
182
|
+
return
|
183
|
+
end
|
184
|
+
|
185
|
+
@password = new_password
|
186
|
+
|
187
|
+
File.delete @filename if File.exists? @filename
|
188
|
+
File.open @filename, 'wb' do |f|
|
189
|
+
plaintext = "JunqueNote\n#{@text_area.text.length}\n#{@text_area.text}"
|
190
|
+
encrypted = plaintext.encrypt(@password)
|
191
|
+
f.write encrypted
|
192
|
+
end
|
193
|
+
|
194
|
+
@dirty = false
|
195
|
+
end
|
196
|
+
|
197
|
+
def exit_app
|
198
|
+
should_save = if @dirty
|
199
|
+
0 == JOptionPane.show_confirm_dialog(
|
200
|
+
nil,
|
201
|
+
"Wanna save first?",
|
202
|
+
"Quittin' time",
|
203
|
+
JOptionPane::YES_NO_OPTION)
|
204
|
+
else
|
205
|
+
false
|
206
|
+
end
|
207
|
+
|
208
|
+
save_as(@filename) if should_save
|
209
|
+
@frame.dispose
|
210
|
+
end
|
211
|
+
|
212
|
+
def about
|
213
|
+
JOptionPane.show_message_dialog(
|
214
|
+
@frame,
|
215
|
+
"A hypothetical JRuby port of LockNote",
|
216
|
+
"About JunqueNote",
|
217
|
+
JOptionPane::INFORMATION_MESSAGE)
|
218
|
+
end
|
219
|
+
|
220
|
+
def find(how = nil, term = nil)
|
221
|
+
term ||= JOptionPane.show_input_dialog "Please enter the search term"
|
222
|
+
return unless term
|
223
|
+
|
224
|
+
@how = how
|
225
|
+
@term = term
|
226
|
+
|
227
|
+
pattern, backwards = case how
|
228
|
+
when :case
|
229
|
+
[/#{term}/, false]
|
230
|
+
when :reverse
|
231
|
+
[/#{term.reverse}/i, true]
|
232
|
+
when :case_reverse
|
233
|
+
[/#{term.reverse}/, true]
|
234
|
+
else
|
235
|
+
[/#{term}/i, false]
|
236
|
+
end
|
237
|
+
|
238
|
+
position = @text_area.get_selection_start || 0
|
239
|
+
contents = @text_area.text
|
240
|
+
|
241
|
+
if backwards
|
242
|
+
contents.reverse!
|
243
|
+
position = contents.length - position
|
244
|
+
end
|
245
|
+
|
246
|
+
next_position = contents.index pattern, position + 1
|
247
|
+
|
248
|
+
if next_position
|
249
|
+
next_position =
|
250
|
+
contents.length -
|
251
|
+
next_position -
|
252
|
+
term.length if backwards
|
253
|
+
|
254
|
+
@text_area.set_selection_start next_position
|
255
|
+
@text_area.set_selection_end next_position + term.length
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
if __FILE__ == $0
|
261
|
+
JunqueNoteApp.new
|
262
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
|
+
|
4
|
+
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
5
|
+
|
6
|
+
require 'rake'
|
7
|
+
require 'rake/testtask'
|
8
|
+
require 'rake/rdoctask'
|
9
|
+
|
10
|
+
require 'tasks/rails'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
# Filters added to this controller apply to all controllers in the application.
|
10
|
+
# Likewise, all the methods added will be available for all controllers.
|
11
|
+
|
12
|
+
class ApplicationController < ActionController::Base
|
13
|
+
helper :all # include all helpers, all the time
|
14
|
+
|
15
|
+
# See ActionController::RequestForgeryProtection for details
|
16
|
+
# Uncomment the :secret if you're not using the cookie session store
|
17
|
+
protect_from_forgery # :secret => '7243747ccf7fcd6980929d8a49f62572'
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
class GuestsController < ApplicationController
|
10
|
+
# POST /guests
|
11
|
+
# POST /guests.xml
|
12
|
+
def create
|
13
|
+
party_permalink = params[:guest].delete :party_permalink
|
14
|
+
party = Party.find_by_permalink party_permalink
|
15
|
+
@guest = Guest.new(params[:guest].merge :party_id => party.id)
|
16
|
+
|
17
|
+
respond_to do |format|
|
18
|
+
if @guest.save
|
19
|
+
flash[:notice] = 'You have successfully RSVPed.'
|
20
|
+
format.html { redirect_to(@guest.party) }
|
21
|
+
format.xml { render :xml => @guest, :status => :created, :location => @guest }
|
22
|
+
else
|
23
|
+
format.html { redirect_to(@guest.party) }
|
24
|
+
format.xml { render :xml => @guest.errors, :status => :unprocessable_entity }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
class PartiesController < ApplicationController
|
10
|
+
# GET /parties
|
11
|
+
def index
|
12
|
+
redirect_to new_party_url
|
13
|
+
end
|
14
|
+
|
15
|
+
# GET /parties/1
|
16
|
+
# GET /parties/1.xml
|
17
|
+
def show
|
18
|
+
@party = Party.find_by_permalink(params[:id])
|
19
|
+
|
20
|
+
guest_name = params[:accept] || params[:decline]
|
21
|
+
if guest_name
|
22
|
+
guest = Guest.new \
|
23
|
+
:party_id => @party.id,
|
24
|
+
:name => guest_name,
|
25
|
+
:attending => params.has_key?(:accept)
|
26
|
+
guest.save
|
27
|
+
end
|
28
|
+
|
29
|
+
respond_to do |format|
|
30
|
+
format.html # show.html.erb
|
31
|
+
format.xml { render :xml => @party }
|
32
|
+
format.text do
|
33
|
+
email = PartyMailer.create_invite @party, params[:email]
|
34
|
+
render :text => email.encoded
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# GET /parties/new
|
40
|
+
# GET /parties/new.xml
|
41
|
+
def new
|
42
|
+
@party = Party.new
|
43
|
+
|
44
|
+
respond_to do |format|
|
45
|
+
format.html # new.html.erb
|
46
|
+
format.xml { render :xml => @party }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# POST /parties
|
51
|
+
# POST /parties.xml
|
52
|
+
def create
|
53
|
+
@party = Party.new(params[:party])
|
54
|
+
|
55
|
+
respond_to do |format|
|
56
|
+
if @party.save
|
57
|
+
recipients = params[:recipients]
|
58
|
+
|
59
|
+
if !recipients || recipients.empty?
|
60
|
+
flash[:notice] = 'Paste the text below into your e-mail program.'
|
61
|
+
else
|
62
|
+
recipients.split(',').each do |address|
|
63
|
+
email = PartyMailer.deliver_invite @party, address
|
64
|
+
end
|
65
|
+
|
66
|
+
flash[:notice] = "Invitations successfully sent to #{recipients}."
|
67
|
+
end
|
68
|
+
|
69
|
+
format.html { redirect_to(@party) }
|
70
|
+
format.xml { render :xml => @party, :status => :created, :location => @party }
|
71
|
+
else
|
72
|
+
format.html { render :action => "new" }
|
73
|
+
format.xml { render :xml => @party.errors, :status => :unprocessable_entity }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
# Methods added to this helper will be available to all templates in the application.
|
10
|
+
module ApplicationHelper
|
11
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
module GuestsHelper
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
module PartiesHelper
|
10
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
class Guest < ActiveRecord::Base
|
10
|
+
belongs_to :party
|
11
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
require 'digest/sha1'
|
10
|
+
|
11
|
+
class Party < ActiveRecord::Base
|
12
|
+
has_many :guests
|
13
|
+
after_create :assign_permalink
|
14
|
+
|
15
|
+
def to_param
|
16
|
+
permalink
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_initialize
|
20
|
+
if new_record?
|
21
|
+
self.begins_at ||= Time.now.monday + 5.days + 22.hours
|
22
|
+
self.ends_at ||= begins_at + 4.hours
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def assign_permalink
|
29
|
+
plaintext = "#{id}-#{Time.now.to_f}-{rand}"
|
30
|
+
update_attribute :permalink, Digest::SHA1.hexdigest(plaintext)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#---
|
2
|
+
# Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
# published by The Pragmatic Bookshelf.
|
4
|
+
# Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
# courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
# We make no guarantees that this code is fit for any purpose.
|
7
|
+
# Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
#---
|
9
|
+
class PartyMailer < ActionMailer::Base
|
10
|
+
def invite(party, address)
|
11
|
+
@subject = "Invitation to #{party.name}"
|
12
|
+
@from = 'no-reply@example.com'
|
13
|
+
@recipients = address
|
14
|
+
@sent_on = Time.now
|
15
|
+
@body['party'] = party
|
16
|
+
@body['url'] = party_url party, :host => 'localhost:3000'
|
17
|
+
@body['address'] = address
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<!--
|
2
|
+
! Excerpted from "Scripted GUI Testing With Ruby",
|
3
|
+
! published by The Pragmatic Bookshelf.
|
4
|
+
! Copyrights apply to this code. It may not be used to create training material,
|
5
|
+
! courses, books, articles, and the like. Contact us if you are in doubt.
|
6
|
+
! We make no guarantees that this code is fit for any purpose.
|
7
|
+
! Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
|
8
|
+
-->
|
9
|
+
<html>
|
10
|
+
<head>
|
11
|
+
<title>novite - hassle-free invites</title>
|
12
|
+
<style>
|
13
|
+
body {
|
14
|
+
font-family: "Frutiger", "Univers", "Helvetica Neue", arial, helvetica, sans-serif;
|
15
|
+
}
|
16
|
+
|
17
|
+
ul {
|
18
|
+
list-style-image: none;
|
19
|
+
list-style-type: none;
|
20
|
+
margin-left: 0px;
|
21
|
+
padding-left: 0px;
|
22
|
+
}
|
23
|
+
|
24
|
+
ul li {
|
25
|
+
margin-bottom: 5px;
|
26
|
+
}
|
27
|
+
|
28
|
+
span.drag {
|
29
|
+
background-color: black;
|
30
|
+
color: white;
|
31
|
+
cursor: move;
|
32
|
+
padding: 1px;
|
33
|
+
font-size: small;
|
34
|
+
}
|
35
|
+
</style>
|
36
|
+
</head>
|
37
|
+
<body>
|
38
|
+
<% if flash[:notice] %>
|
39
|
+
<div id="party_notice"><%= flash[:notice] %></div>
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
<%= yield %>
|
43
|
+
</body>
|
44
|
+
</html>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<h1>New party</h1>
|
2
|
+
|
3
|
+
<%= error_messages_for :party %>
|
4
|
+
|
5
|
+
<% form_for(@party) do |f| %>
|
6
|
+
<p>
|
7
|
+
<h3>What:</h3>
|
8
|
+
<%= f.text_field :name %>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<p>
|
12
|
+
<h3>Why:</h3>
|
13
|
+
<%= f.text_area :description, :rows => 5 %>
|
14
|
+
</p>
|
15
|
+
|
16
|
+
<p>
|
17
|
+
<h3>Where:</h3>
|
18
|
+
<%= f.text_area :location, :rows => 5 %>
|
19
|
+
</p>
|
20
|
+
|
21
|
+
<p>
|
22
|
+
<h3>When:</h3>
|
23
|
+
<h3>From</b><br />
|
24
|
+
<%= f.datetime_select :begins_at %>
|
25
|
+
</p>
|
26
|
+
|
27
|
+
<p>
|
28
|
+
<b>to</b><br />
|
29
|
+
<%= f.datetime_select :ends_at %>
|
30
|
+
</p>
|
31
|
+
|
32
|
+
<p>
|
33
|
+
<h3>Who:</h3>
|
34
|
+
<textarea name="recipients" id="party_recipients" rows="5" cols="40"></textarea>
|
35
|
+
</p>
|
36
|
+
|
37
|
+
<p>
|
38
|
+
<%= f.submit "Create" %>
|
39
|
+
</p>
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
<%= link_to 'Back', parties_path %>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<h1>Invitation to <%=h @party.name %></h1>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
You're invited to <span id="party_name"><%=h @party.name %></span>
|
5
|
+
on <span id="party_begins_on"><%=h @party.begins_at.strftime '%A, %B %d, %Y' %></span>,
|
6
|
+
from <span id="party_begins_at"><%=h @party.begins_at.strftime '%I:%M %p' %></span>
|
7
|
+
to <span id="party_ends_at"><%=h @party.ends_at.strftime '%I:%M %p' %></span>!
|
8
|
+
</p>
|
9
|
+
|
10
|
+
<p>
|
11
|
+
There are many parties, but only this one lives at:
|
12
|
+
<br /><%= link_to party_url(@party), {}, :id => 'party_link' %>
|
13
|
+
</p>
|
14
|
+
|
15
|
+
<h2>Details</h2>
|
16
|
+
|
17
|
+
<p>
|
18
|
+
<b>Why:</b>
|
19
|
+
<span id="party_description"/><%=h @party.description %></span>
|
20
|
+
</p>
|
21
|
+
|
22
|
+
<p>
|
23
|
+
<b>Where:</b>
|
24
|
+
<span id="party_location"><%=h @party.location %></span>
|
25
|
+
</p>
|
26
|
+
|
27
|
+
<p>
|
28
|
+
<b>Who:</b>
|
29
|
+
<% form_for :guest, :url => guests_url do |f| %>
|
30
|
+
<%= f.hidden_field :party_permalink, :value => @party.permalink %>
|
31
|
+
<%= f.text_field :name %>
|
32
|
+
<%= f.check_box :attending, :checked => true %>
|
33
|
+
attending
|
34
|
+
<%= submit_tag 'RSVP', :id => 'rsvp' %>
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
<ul id="guests">
|
38
|
+
<% @party.guests.each do |rsvp| %>
|
39
|
+
<li><span class="rsvp_name"><%=h rsvp.name %></span>
|
40
|
+
is <span class="rsvp_attending"><%= rsvp.attending ? 'attending' : 'not attending' %></span></li>
|
41
|
+
<% end %>
|
42
|
+
</ul>
|
43
|
+
</p>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Invitation to <%=h @party.name %>
|
2
|
+
|
3
|
+
You're invited to <%=h @party.name %> on <%=h @party.begins_at.strftime '%A, %B %d, %Y' %>, from <%=h @party.begins_at.strftime '%I:%M %p' %> to <%=h @party.ends_at.strftime '%I:%M %p' %>!
|
4
|
+
|
5
|
+
You can RSVP by visiting <%= @url %>, or clicking one of the RSVP links below.
|
6
|
+
|
7
|
+
Details
|
8
|
+
|
9
|
+
Why:
|
10
|
+
<%=h @party.description %>
|
11
|
+
|
12
|
+
Where:
|
13
|
+
<%=h @party.location %>
|
14
|
+
|
15
|
+
RSVP:
|
16
|
+
Yes - <%= @url %>?accept=<%= CGI::escape @address %>
|
17
|
+
No - <%= @url %>?decline=<%= CGI::escape @address %>
|