win_gui 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +43 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/book_code/early_success/bundle.rb +34 -0
  8. data/book_code/early_success/english.txt +1 -0
  9. data/book_code/early_success/jruby_basics.rb +47 -0
  10. data/book_code/early_success/windows_basics.rb +97 -0
  11. data/book_code/guessing/locknote.rb +379 -0
  12. data/book_code/guessing/monkeyshines.rb +14 -0
  13. data/book_code/guessing/note.rb +120 -0
  14. data/book_code/guessing/note_spec.rb +175 -0
  15. data/book_code/guessing/replay.rb +21 -0
  16. data/book_code/guessing/seed.rb +9 -0
  17. data/book_code/guessing/spec_helper.rb +69 -0
  18. data/book_code/guessing/windows_gui.rb +247 -0
  19. data/book_code/home_stretch/junquenote.rb +151 -0
  20. data/book_code/home_stretch/locknote.rb +180 -0
  21. data/book_code/home_stretch/note.rb +144 -0
  22. data/book_code/home_stretch/note_spec.rb +191 -0
  23. data/book_code/home_stretch/spec_helper.rb +55 -0
  24. data/book_code/home_stretch/swing_gui.rb +50 -0
  25. data/book_code/home_stretch/windows_gui.rb +232 -0
  26. data/book_code/junquenote/exports.sh +10 -0
  27. data/book_code/junquenote/jruby_mac.sh +10 -0
  28. data/book_code/junquenote/junquenote_app.rb +262 -0
  29. data/book_code/novite/Rakefile +10 -0
  30. data/book_code/novite/app/controllers/application.rb +18 -0
  31. data/book_code/novite/app/controllers/guests_controller.rb +28 -0
  32. data/book_code/novite/app/controllers/parties_controller.rb +77 -0
  33. data/book_code/novite/app/helpers/application_helper.rb +11 -0
  34. data/book_code/novite/app/helpers/guests_helper.rb +10 -0
  35. data/book_code/novite/app/helpers/parties_helper.rb +10 -0
  36. data/book_code/novite/app/models/guest.rb +11 -0
  37. data/book_code/novite/app/models/party.rb +32 -0
  38. data/book_code/novite/app/models/party_mailer.rb +19 -0
  39. data/book_code/novite/app/views/layouts/application.rhtml +44 -0
  40. data/book_code/novite/app/views/parties/new.html.erb +42 -0
  41. data/book_code/novite/app/views/parties/show.html.erb +43 -0
  42. data/book_code/novite/app/views/party_mailer/invite.erb +17 -0
  43. data/book_code/novite/config/boot.rb +117 -0
  44. data/book_code/novite/config/database.yml +19 -0
  45. data/book_code/novite/config/environment.rb +67 -0
  46. data/book_code/novite/config/environments/development.rb +29 -0
  47. data/book_code/novite/config/environments/production.rb +27 -0
  48. data/book_code/novite/config/environments/test.rb +30 -0
  49. data/book_code/novite/config/initializers/inflections.rb +18 -0
  50. data/book_code/novite/config/initializers/mime_types.rb +13 -0
  51. data/book_code/novite/config/routes.rb +47 -0
  52. data/book_code/novite/db/migrate/001_create_parties.rb +26 -0
  53. data/book_code/novite/db/migrate/002_create_guests.rb +23 -0
  54. data/book_code/novite/db/schema.rb +41 -0
  55. data/book_code/novite/log/empty.txt +0 -0
  56. data/book_code/novite/public/.htaccess +40 -0
  57. data/book_code/novite/public/404.html +38 -0
  58. data/book_code/novite/public/422.html +38 -0
  59. data/book_code/novite/public/500.html +38 -0
  60. data/book_code/novite/public/dispatch.cgi +10 -0
  61. data/book_code/novite/public/dispatch.fcgi +24 -0
  62. data/book_code/novite/public/dispatch.rb +18 -0
  63. data/book_code/novite/public/favicon.ico +0 -0
  64. data/book_code/novite/public/images/rails.png +0 -0
  65. data/book_code/novite/public/index.html +285 -0
  66. data/book_code/novite/public/javascripts/application.js +10 -0
  67. data/book_code/novite/public/javascripts/controls.js +971 -0
  68. data/book_code/novite/public/javascripts/dragdrop.js +980 -0
  69. data/book_code/novite/public/javascripts/effects.js +1128 -0
  70. data/book_code/novite/public/javascripts/prototype.js +4233 -0
  71. data/book_code/novite/public/robots.txt +5 -0
  72. data/book_code/novite/script/about +3 -0
  73. data/book_code/novite/script/console +3 -0
  74. data/book_code/novite/script/destroy +3 -0
  75. data/book_code/novite/script/generate +3 -0
  76. data/book_code/novite/script/performance/benchmarker +3 -0
  77. data/book_code/novite/script/performance/profiler +3 -0
  78. data/book_code/novite/script/performance/request +3 -0
  79. data/book_code/novite/script/plugin +3 -0
  80. data/book_code/novite/script/process/inspector +3 -0
  81. data/book_code/novite/script/process/reaper +3 -0
  82. data/book_code/novite/script/process/spawner +3 -0
  83. data/book_code/novite/script/runner +3 -0
  84. data/book_code/novite/script/server +3 -0
  85. data/book_code/novite/test/test_helper.rb +46 -0
  86. data/book_code/one_more_thing/applescript.rb +68 -0
  87. data/book_code/one_more_thing/note_spec.rb +50 -0
  88. data/book_code/one_more_thing/spec_helper.rb +17 -0
  89. data/book_code/one_more_thing/textedit-pure.rb +28 -0
  90. data/book_code/one_more_thing/textedit.applescript +26 -0
  91. data/book_code/one_more_thing/textedit.rb +32 -0
  92. data/book_code/one_more_thing/textnote.rb +87 -0
  93. data/book_code/simplify/junquenote.rb +48 -0
  94. data/book_code/simplify/locknote.rb +46 -0
  95. data/book_code/simplify/note.rb +35 -0
  96. data/book_code/simplify/note_spec.rb +28 -0
  97. data/book_code/simplify/swing_gui.rb +45 -0
  98. data/book_code/simplify/windows_gui.rb +232 -0
  99. data/book_code/simplify/windows_gui_spec.rb +35 -0
  100. data/book_code/story/invite.story +19 -0
  101. data/book_code/story/journal.txt +29 -0
  102. data/book_code/story/novite_stories.rb +156 -0
  103. data/book_code/story/party.rb +149 -0
  104. data/book_code/story/password.rb +61 -0
  105. data/book_code/story/password.story +26 -0
  106. data/book_code/story/rsvp.story +29 -0
  107. data/book_code/tables/TestTime.html +93 -0
  108. data/book_code/tables/TestTimeSample.html +63 -0
  109. data/book_code/tables/calculate_time.rb +39 -0
  110. data/book_code/tables/calculator.rb +108 -0
  111. data/book_code/tables/calculator_actions.rb +27 -0
  112. data/book_code/tables/calculator_spec.rb +47 -0
  113. data/book_code/tables/fit.rb +32 -0
  114. data/book_code/tables/matrix.rb +109 -0
  115. data/book_code/tables/pseudocode.rb +17 -0
  116. data/book_code/tubes/book_selenium.rb +67 -0
  117. data/book_code/tubes/book_watir.rb +60 -0
  118. data/book_code/tubes/dragdrop.html +81 -0
  119. data/book_code/tubes/html_capture.rb +33 -0
  120. data/book_code/tubes/joke_list.rb +67 -0
  121. data/book_code/tubes/list_spec.rb +41 -0
  122. data/book_code/tubes/search_spec.rb +32 -0
  123. data/book_code/tubes/selenium_example.rb +66 -0
  124. data/book_code/tubes/selenium_link.rb +23 -0
  125. data/book_code/tubes/web_server.rb +14 -0
  126. data/book_code/windows/wgui.rb +29 -0
  127. data/book_code/windows/wobj.rb +25 -0
  128. data/book_code/windows/wsh.rb +25 -0
  129. data/book_code/with_rspec/empty_spec.rb +13 -0
  130. data/book_code/with_rspec/junquenote.rb +60 -0
  131. data/book_code/with_rspec/locknote.rb +129 -0
  132. data/book_code/with_rspec/note_spec.rb +32 -0
  133. data/book_code/with_rspec/should_examples.rb +18 -0
  134. data/exp/exp.rb +6 -0
  135. data/exp/exp_encodings.rb +40 -0
  136. data/exp/exp_enum_windows.rb +60 -0
  137. data/exp/exp_quik.rb +38 -0
  138. data/exp/exp_wsh.rb +115 -0
  139. data/exp/old/windows_basics.rb +80 -0
  140. data/exp/old/wnote.rb +80 -0
  141. data/exp/old/wnote_spec.rb +20 -0
  142. data/features/step_definitions/win_gui_steps.rb +0 -0
  143. data/features/support/env.rb +4 -0
  144. data/features/win_gui.feature +9 -0
  145. data/lib/note/java/jemmy.jar +0 -0
  146. data/lib/note/java/jnote.rb +48 -0
  147. data/lib/note/java/jruby_basics.rb +37 -0
  148. data/lib/note/java/junquenote_app.rb +262 -0
  149. data/lib/note/java/note_spec.rb +20 -0
  150. data/lib/note/win/locknote.rb +19 -0
  151. data/lib/note.rb +15 -0
  152. data/lib/win_gui/constants.rb +66 -0
  153. data/lib/win_gui/string_extensions.rb +24 -0
  154. data/lib/win_gui/win_gui.rb +274 -0
  155. data/lib/win_gui/window.rb +70 -0
  156. data/lib/win_gui.rb +3 -0
  157. data/spec/note/win/locknote_spec.rb +7 -0
  158. data/spec/spec.opts +2 -0
  159. data/spec/spec_helper.rb +100 -0
  160. data/spec/test_apps/locknote/LockNote.exe +0 -0
  161. data/spec/win_gui/string_extensions_spec.rb +61 -0
  162. data/spec/win_gui/win_gui_spec.rb +733 -0
  163. data/spec/win_gui/window_spec.rb +124 -0
  164. 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 %>