win_gui 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/.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 %>
|