reflexive 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/Gemfile +16 -0
  2. data/Rakefile +31 -0
  3. data/config.ru +3 -0
  4. data/reflexive.gemspec +1 -1
  5. data/spec/coderay_html_encoder_spec.rb +57 -0
  6. data/spec/coderay_ruby_scanner_spec.rb +194 -0
  7. data/spec/io_interceptor.rb +64 -0
  8. data/spec/method_lookup_spec.rb +0 -0
  9. data/spec/methods_spec.rb +202 -0
  10. data/spec/rails_integration_spec.rb +139 -0
  11. data/spec/rails_integration_spec_helper.rb +115 -0
  12. data/spec/reflexive_ripper_spec.rb +351 -0
  13. data/spec/reflexive_spec.rb +98 -0
  14. data/spec/ripper_events_recorder.rb +50 -0
  15. data/spec/ripper_spec.rb +758 -0
  16. data/spec/sexp_builder_with_scanner_events.rb +39 -0
  17. data/spec/shell_out.rb +417 -0
  18. data/spec/spec_helper.rb +4 -0
  19. data/spec/test_apps/rails2_test_app/README +243 -0
  20. data/spec/test_apps/rails2_test_app/Rakefile +10 -0
  21. data/spec/test_apps/rails2_test_app/app/controllers/application_controller.rb +10 -0
  22. data/spec/test_apps/rails2_test_app/app/controllers/posts_controller.rb +85 -0
  23. data/spec/test_apps/rails2_test_app/app/helpers/application_helper.rb +3 -0
  24. data/spec/test_apps/rails2_test_app/app/helpers/posts_helper.rb +2 -0
  25. data/spec/test_apps/rails2_test_app/app/models/post.rb +5 -0
  26. data/spec/test_apps/rails2_test_app/app/views/layouts/posts.html.erb +17 -0
  27. data/spec/test_apps/rails2_test_app/app/views/posts/edit.html.erb +12 -0
  28. data/spec/test_apps/rails2_test_app/app/views/posts/index.html.erb +18 -0
  29. data/spec/test_apps/rails2_test_app/app/views/posts/new.html.erb +11 -0
  30. data/spec/test_apps/rails2_test_app/app/views/posts/show.html.erb +3 -0
  31. data/spec/test_apps/rails2_test_app/config/boot.rb +110 -0
  32. data/spec/test_apps/rails2_test_app/config/database.yml +22 -0
  33. data/spec/test_apps/rails2_test_app/config/environment.rb +43 -0
  34. data/spec/test_apps/rails2_test_app/config/environments/development.rb +17 -0
  35. data/spec/test_apps/rails2_test_app/config/environments/production.rb +28 -0
  36. data/spec/test_apps/rails2_test_app/config/environments/test.rb +28 -0
  37. data/spec/test_apps/rails2_test_app/config/initializers/backtrace_silencers.rb +7 -0
  38. data/spec/test_apps/rails2_test_app/config/initializers/inflections.rb +10 -0
  39. data/spec/test_apps/rails2_test_app/config/initializers/mime_types.rb +5 -0
  40. data/spec/test_apps/rails2_test_app/config/initializers/new_rails_defaults.rb +21 -0
  41. data/spec/test_apps/rails2_test_app/config/initializers/session_store.rb +15 -0
  42. data/spec/test_apps/rails2_test_app/config/locales/en.yml +5 -0
  43. data/spec/test_apps/rails2_test_app/config/routes.rb +45 -0
  44. data/spec/test_apps/rails2_test_app/db/development.sqlite3 +0 -0
  45. data/spec/test_apps/rails2_test_app/db/migrate/20100512073155_create_posts.rb +12 -0
  46. data/spec/test_apps/rails2_test_app/db/schema.rb +19 -0
  47. data/spec/test_apps/rails2_test_app/db/seeds.rb +7 -0
  48. data/spec/test_apps/rails2_test_app/doc/README_FOR_APP +2 -0
  49. data/spec/test_apps/rails2_test_app/log/development.log +251 -0
  50. data/spec/test_apps/rails2_test_app/log/production.log +0 -0
  51. data/spec/test_apps/rails2_test_app/log/server.log +0 -0
  52. data/spec/test_apps/rails2_test_app/log/test.log +0 -0
  53. data/spec/test_apps/rails2_test_app/public/404.html +30 -0
  54. data/spec/test_apps/rails2_test_app/public/422.html +30 -0
  55. data/spec/test_apps/rails2_test_app/public/500.html +30 -0
  56. data/spec/test_apps/rails2_test_app/public/favicon.ico +0 -0
  57. data/spec/test_apps/rails2_test_app/public/images/rails.png +0 -0
  58. data/spec/test_apps/rails2_test_app/public/index.html +275 -0
  59. data/spec/test_apps/rails2_test_app/public/javascripts/application.js +2 -0
  60. data/spec/test_apps/rails2_test_app/public/javascripts/controls.js +963 -0
  61. data/spec/test_apps/rails2_test_app/public/javascripts/dragdrop.js +973 -0
  62. data/spec/test_apps/rails2_test_app/public/javascripts/effects.js +1128 -0
  63. data/spec/test_apps/rails2_test_app/public/javascripts/prototype.js +4320 -0
  64. data/spec/test_apps/rails2_test_app/public/robots.txt +5 -0
  65. data/spec/test_apps/rails2_test_app/public/stylesheets/1.css +0 -0
  66. data/spec/test_apps/rails2_test_app/public/stylesheets/scaffold.css +54 -0
  67. data/spec/test_apps/rails2_test_app/script/about +4 -0
  68. data/spec/test_apps/rails2_test_app/script/console +3 -0
  69. data/spec/test_apps/rails2_test_app/script/dbconsole +3 -0
  70. data/spec/test_apps/rails2_test_app/script/destroy +3 -0
  71. data/spec/test_apps/rails2_test_app/script/generate +3 -0
  72. data/spec/test_apps/rails2_test_app/script/performance/benchmarker +3 -0
  73. data/spec/test_apps/rails2_test_app/script/performance/profiler +3 -0
  74. data/spec/test_apps/rails2_test_app/script/plugin +3 -0
  75. data/spec/test_apps/rails2_test_app/script/runner +3 -0
  76. data/spec/test_apps/rails2_test_app/script/server +3 -0
  77. data/spec/test_apps/rails2_test_app/test/fixtures/posts.yml +7 -0
  78. data/spec/test_apps/rails2_test_app/test/functional/posts_controller_test.rb +45 -0
  79. data/spec/test_apps/rails2_test_app/test/performance/browsing_test.rb +9 -0
  80. data/spec/test_apps/rails2_test_app/test/test_helper.rb +38 -0
  81. data/spec/test_apps/rails2_test_app/test/unit/helpers/posts_helper_test.rb +4 -0
  82. data/spec/test_apps/rails2_test_app/test/unit/post_test.rb +8 -0
  83. data/spec/test_apps/rails3_test_app/Gemfile +28 -0
  84. data/spec/test_apps/rails3_test_app/README +244 -0
  85. data/spec/test_apps/rails3_test_app/Rakefile +10 -0
  86. data/spec/test_apps/rails3_test_app/app/controllers/application_controller.rb +4 -0
  87. data/spec/test_apps/rails3_test_app/app/controllers/posts_controller.rb +83 -0
  88. data/spec/test_apps/rails3_test_app/app/helpers/application_helper.rb +2 -0
  89. data/spec/test_apps/rails3_test_app/app/helpers/posts_helper.rb +2 -0
  90. data/spec/test_apps/rails3_test_app/app/models/post.rb +2 -0
  91. data/spec/test_apps/rails3_test_app/app/views/layouts/application.html.erb +14 -0
  92. data/spec/test_apps/rails3_test_app/app/views/posts/_form.html.erb +16 -0
  93. data/spec/test_apps/rails3_test_app/app/views/posts/edit.html.erb +6 -0
  94. data/spec/test_apps/rails3_test_app/app/views/posts/index.html.erb +21 -0
  95. data/spec/test_apps/rails3_test_app/app/views/posts/new.html.erb +5 -0
  96. data/spec/test_apps/rails3_test_app/app/views/posts/show.html.erb +5 -0
  97. data/spec/test_apps/rails3_test_app/config/application.rb +49 -0
  98. data/spec/test_apps/rails3_test_app/config/boot.rb +6 -0
  99. data/spec/test_apps/rails3_test_app/config/database.yml +22 -0
  100. data/spec/test_apps/rails3_test_app/config/environment.rb +5 -0
  101. data/spec/test_apps/rails3_test_app/config/environments/development.rb +19 -0
  102. data/spec/test_apps/rails3_test_app/config/environments/production.rb +42 -0
  103. data/spec/test_apps/rails3_test_app/config/environments/test.rb +32 -0
  104. data/spec/test_apps/rails3_test_app/config/initializers/backtrace_silencers.rb +7 -0
  105. data/spec/test_apps/rails3_test_app/config/initializers/inflections.rb +10 -0
  106. data/spec/test_apps/rails3_test_app/config/initializers/mime_types.rb +5 -0
  107. data/spec/test_apps/rails3_test_app/config/initializers/secret_token.rb +7 -0
  108. data/spec/test_apps/rails3_test_app/config/initializers/session_store.rb +8 -0
  109. data/spec/test_apps/rails3_test_app/config/locales/en.yml +5 -0
  110. data/spec/test_apps/rails3_test_app/config/routes.rb +60 -0
  111. data/spec/test_apps/rails3_test_app/config.ru +4 -0
  112. data/spec/test_apps/rails3_test_app/db/development.sqlite3 +0 -0
  113. data/spec/test_apps/rails3_test_app/db/migrate/20100512075428_create_posts.rb +12 -0
  114. data/spec/test_apps/rails3_test_app/db/schema.rb +19 -0
  115. data/spec/test_apps/rails3_test_app/db/seeds.rb +7 -0
  116. data/spec/test_apps/rails3_test_app/doc/README_FOR_APP +2 -0
  117. data/spec/test_apps/rails3_test_app/log/development.log +349 -0
  118. data/spec/test_apps/rails3_test_app/log/production.log +0 -0
  119. data/spec/test_apps/rails3_test_app/log/server.log +0 -0
  120. data/spec/test_apps/rails3_test_app/log/test.log +0 -0
  121. data/spec/test_apps/rails3_test_app/public/404.html +26 -0
  122. data/spec/test_apps/rails3_test_app/public/422.html +26 -0
  123. data/spec/test_apps/rails3_test_app/public/500.html +26 -0
  124. data/spec/test_apps/rails3_test_app/public/favicon.ico +0 -0
  125. data/spec/test_apps/rails3_test_app/public/images/rails.png +0 -0
  126. data/spec/test_apps/rails3_test_app/public/index.html +279 -0
  127. data/spec/test_apps/rails3_test_app/public/javascripts/application.js +2 -0
  128. data/spec/test_apps/rails3_test_app/public/javascripts/controls.js +965 -0
  129. data/spec/test_apps/rails3_test_app/public/javascripts/dragdrop.js +974 -0
  130. data/spec/test_apps/rails3_test_app/public/javascripts/effects.js +1123 -0
  131. data/spec/test_apps/rails3_test_app/public/javascripts/prototype.js +4874 -0
  132. data/spec/test_apps/rails3_test_app/public/javascripts/rails.js +118 -0
  133. data/spec/test_apps/rails3_test_app/public/robots.txt +5 -0
  134. data/spec/test_apps/rails3_test_app/public/stylesheets/scaffold.css +60 -0
  135. data/spec/test_apps/rails3_test_app/script/rails +9 -0
  136. data/spec/test_apps/rails3_test_app/test/fixtures/posts.yml +11 -0
  137. data/spec/test_apps/rails3_test_app/test/functional/posts_controller_test.rb +49 -0
  138. data/spec/test_apps/rails3_test_app/test/performance/browsing_test.rb +9 -0
  139. data/spec/test_apps/rails3_test_app/test/test_helper.rb +13 -0
  140. data/spec/test_apps/rails3_test_app/test/unit/helpers/posts_helper_test.rb +4 -0
  141. data/spec/test_apps/rails3_test_app/test/unit/post_test.rb +8 -0
  142. metadata +142 -2
@@ -0,0 +1,39 @@
1
+ require File.expand_path("../ripper_events_recorder", __FILE__)
2
+
3
+ # reports scanner events as hashes
4
+ class SexpBuilderWithScannerEvents < RipperEventsRecorder
5
+ SCANNER_EVENTS.each do |event|
6
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
7
+ def on_#{event}(tok)
8
+ super
9
+ { :#{ event } => tok }
10
+ end
11
+ End
12
+ end
13
+
14
+ PARSER_EVENT_TABLE.each do |event, arity|
15
+ if /_new\z/ =~ event.to_s and arity == 0
16
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
17
+ def on_#{event}
18
+ super
19
+ []
20
+ end
21
+ End
22
+ elsif /_add\z/ =~ event.to_s
23
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
24
+ def on_#{event}(list, item)
25
+ super
26
+ list.push item
27
+ list
28
+ end
29
+ End
30
+ else
31
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
32
+ def on_#{event}(*args)
33
+ super
34
+ [:#{event}, *args]
35
+ end
36
+ End
37
+ end
38
+ end
39
+ end
data/spec/shell_out.rb ADDED
@@ -0,0 +1,417 @@
1
+ # ## ShellOut
2
+ #
3
+ # Provides a convenient feature-rich way to "shell out" to external commands.
4
+ # Most useful features come from using `PTY` to execute the command. Not available
5
+ # on Windows, `Kernel#system` will be used instead.
6
+ #
7
+ # ## Features
8
+ #
9
+ # ### Interruption
10
+ #
11
+ # The external command can be easily interrupted and `Interrupt` exception
12
+ # will propagate to the calling program.
13
+ #
14
+ # For example while something like this can hang your terminal
15
+ #
16
+ # loop { system("ls -R /") } # => lists directories indefinitely,
17
+ # # Ctrl-C only stops ls
18
+ #
19
+ # That won't be the case with ShellOut:
20
+ #
21
+ # require "shell_out"
22
+ # include ShellOut
23
+ # loop { shell_out("ls -R /") } # => when Ctrl-C is pressed ls is terminated
24
+ # # and Interrupt exception is propagated
25
+ #
26
+ # Yes it's possible to examine the `$CHILD_STATUS.exitstatus` variable but that's
27
+ # not nearly as robust and flexible as `PTY` solution.
28
+ #
29
+ # ### TTY-like Output
30
+ #
31
+ # External command is running in pseudo TTY provided by `PTY` library on Unix,
32
+ # which means that commands like `ffmpeg`, `git` can report progress and
33
+ # **otherwise interact with user as usual**.
34
+ #
35
+ # ### Output Capturing
36
+ #
37
+ # Output of the command can be captured using `:out` option
38
+ #
39
+ # io = StringIO.new
40
+ # shell_out("echo 42", :out => io) # doesn't print anything
41
+ # io.string.chomp # => "42"
42
+ #
43
+ # If `:out => :return` option is passed - the `shell_out` return the output
44
+ # of the command instead of exit status.
45
+ #
46
+ # ### :raise_exceptions, :verbose, :noop, and :dry_run Options
47
+ #
48
+ # * `:raise_exceptions => true` will raise `ShellOutException` for any non-zero
49
+ # exit status of the command
50
+ #
51
+ # Following options have the same semantics as `FileUtils` method options do
52
+ #
53
+ # * `:verbose => true` will echo command before execution
54
+ #
55
+ # * `:noop => true` will just return zero exit status without executing
56
+ # the command
57
+ #
58
+ # * `:dry_run => true` equivalent to `:verbose => true, :noop => true`
59
+ #
60
+ module ShellOut
61
+ class ShellOutException < Exception
62
+ end
63
+
64
+ CTRL_C_CODE = ?\C-c
65
+ SUCCESS_EXIT_STATUS = 0
66
+
67
+ class << self
68
+ def before(*args)
69
+ if args.last.is_a?(Hash)
70
+ options = args.last
71
+
72
+ verbose, dry_run, noop = options.delete(:verbose), options.delete(:dry_run), options.delete(:noop)
73
+ verbose = noop = true if dry_run
74
+
75
+ puts "Executing: #{ args[0..-2].join(" ") }" if verbose
76
+
77
+ return false if noop
78
+ end
79
+
80
+ true
81
+ end
82
+
83
+ def after(exitstatus, out_stream, *args)
84
+ if args.last.is_a?(Hash) && args.last[:raise_exceptions] == true
85
+ unless exitstatus == SUCCESS_EXIT_STATUS
86
+ raise ShellOutException, "`#{ args[0..-2].join(" ") }' command finished with non-zero (#{ exitstatus }) exit status"
87
+ end
88
+ end
89
+ if args.last.is_a?(Hash) && args.last[:out] == :return
90
+ out_stream.rewind if out_stream.is_a?(StringIO)
91
+ out_stream.read
92
+ else
93
+ exitstatus
94
+ end
95
+ end
96
+
97
+ def command(*args)
98
+ stripped_command = args.dup
99
+ stripped_command.pop if stripped_command[-1].is_a?(Hash) # remove options
100
+ stripped_command.shift if stripped_command[0].is_a?(Hash) # remove env
101
+ stripped_command.join(" ")
102
+ end
103
+
104
+ def with_env(*args)
105
+ yield unless (env = args[0]).is_a?(Hash)
106
+ stored_env = {}
107
+ for name, value in env
108
+ stored_env[name] = ENV[name]
109
+ value == nil ? ENV.delete(name) : ENV[name] = value
110
+ end
111
+ begin
112
+ yield
113
+ ensure
114
+ for name, value in stored_env
115
+ ENV[name] = value
116
+ end
117
+ end
118
+ end
119
+
120
+ def getopt(opt, default, *args)
121
+ if args.last.is_a?(Hash)
122
+ if opt == :out && args.last[:out] == :return
123
+ StringIO.new
124
+ else
125
+ args.last.fetch(opt, default)
126
+ end
127
+ else
128
+ default
129
+ end
130
+ end
131
+ end
132
+
133
+ module_function
134
+
135
+ def shell_out_with_pty(*args)
136
+ old_state = `stty -g`
137
+
138
+ return SUCCESS_EXIT_STATUS unless ShellOut::before(*args)
139
+
140
+ begin
141
+ # stolen from ruby/ext/pty/script.rb
142
+ # disable echoing and enable raw (not having to press enter)
143
+ system "stty -echo raw lnext ^_"
144
+
145
+ in_stream = ShellOut.getopt(:in, STDIN, *args)
146
+ out_stream = ShellOut.getopt(:out, STDOUT, *args)
147
+ writer = nil
148
+ ShellOut.with_env(*args) do
149
+ PTY.spawn(ShellOut.command(*args)) do |r_pty, w_pty, pid|
150
+ reader = Thread.current
151
+ writer = Thread.new do
152
+ while true
153
+ break if (ch = in_stream.getc).nil?
154
+ ch = ch.chr
155
+ if ch == ShellOut::CTRL_C_CODE
156
+ reader.raise Interrupt, "Interrupted by user"
157
+ else
158
+ w_pty.print ch
159
+ w_pty.flush
160
+ end
161
+ end
162
+ end
163
+ writer.abort_on_exception = true
164
+
165
+ loop do
166
+ c = begin
167
+ r_pty.sysread(512)
168
+ rescue Errno::EIO, EOFError
169
+ nil
170
+ end
171
+ break if c.nil?
172
+
173
+ out_stream.print c
174
+ out_stream.flush
175
+ end
176
+
177
+ begin
178
+ # try to invoke waitpid() before the signal handler does it
179
+ return ShellOut::after(Process::waitpid2(pid)[1].exitstatus, out_stream, *args)
180
+ rescue Errno::ECHILD
181
+ # the signal handler managed to call waitpid() first;
182
+ # PTY::ChildExited will be delivered pretty soon, so just wait for it
183
+ sleep 1
184
+ end
185
+ end
186
+ end
187
+ rescue PTY::ChildExited => e
188
+ return ShellOut::after(e.status.exitstatus, out_stream, *args)
189
+ ensure
190
+ writer && writer.kill
191
+ system "stty #{ old_state }"
192
+ end
193
+ end
194
+
195
+ def shell_out_with_system(*args)
196
+ return SUCCESS_EXIT_STATUS unless ShellOut::before(*args)
197
+
198
+ cleaned_args = if args.last.is_a?(Hash)
199
+ cleaned_options = args.last.dup.delete_if { |k, | [ :verbose, :raise_exceptions ].include?(k) }
200
+ require "stringio"
201
+ if cleaned_options[:out].is_a?(StringIO) ||
202
+ cleaned_options[:out] == :return
203
+ r, w = IO.pipe
204
+ cleaned_options[:out] = w
205
+ cleaned_options[:err] = [ :child, :out ]
206
+ end
207
+ if cleaned_options[:in].is_a?(StringIO)
208
+ in_r, in_w = IO.pipe
209
+ in_w.write cleaned_options[:in].read
210
+ in_w.close
211
+ cleaned_options[:in] = in_r
212
+ end
213
+ cleaned_options.empty? ? args[0..-2] : args[0..-2].dup << cleaned_options
214
+ else
215
+ args
216
+ end
217
+
218
+ exitstatus = if Kernel.system(*cleaned_args)
219
+ SUCCESS_EXIT_STATUS
220
+ else
221
+ require "English"
222
+ $CHILD_STATUS.exitstatus
223
+ end
224
+
225
+ if r
226
+ w.close
227
+ unless args.last[:out] == :return
228
+ args.last[:out] << r.read
229
+ end
230
+ end
231
+
232
+ ShellOut::after(exitstatus, r, *args)
233
+ end
234
+
235
+ begin
236
+ require "pty"
237
+ alias shell_out shell_out_with_pty
238
+ rescue LoadError
239
+ alias shell_out shell_out_with_system
240
+ end
241
+
242
+ module_function :shell_out
243
+ end
244
+
245
+ def ShellOut(*args)
246
+ ShellOut.shell_out(*args)
247
+ end
248
+
249
+ if $PROGRAM_NAME == __FILE__
250
+ require "spec"
251
+ require "stringio"
252
+ require "./io_interceptor.rb"
253
+
254
+ describe "ShellOut" do
255
+ before do
256
+ STDOUT.extend(IoInterceptor)
257
+ end
258
+
259
+ share_examples_for "having base capabilities" do
260
+
261
+ it "shells out to successful command and returns 0 exit code" do
262
+ ShellOut("ruby -e ''").should == 0
263
+ end
264
+
265
+ it "passes arbitrary exit codes" do
266
+ ShellOut("ruby -e 'exit(42)'").should == 42
267
+ end
268
+
269
+ it "passes STDIN (or options[:in]) stream to the command" do
270
+ fake_stdin = StringIO.new
271
+ fake_stdin.write "testing\n"
272
+ fake_stdin.rewind
273
+
274
+ STDOUT.supress do # TODO still mystery why "testing\n" gets written to the output
275
+ ShellOut("ruby -e 'exit(123) if gets.chomp == \"testing\"'", :in => fake_stdin).should == 123
276
+ end
277
+ end
278
+
279
+ it "alters command environment when first argument is a Hash" do
280
+ ShellOut({ "ENV_VAR" => "42" },
281
+ "ruby -e 'puts ENV.inspect'",
282
+ :out => :return).should include('"ENV_VAR"=>"42"')
283
+ end
284
+
285
+ describe ":raise_exceptions option" do
286
+ it "raises exception for non-zero exit codes" do
287
+ lambda do
288
+ ShellOut("false", :raise_exceptions => true)
289
+ end.should raise_error(ShellOut::ShellOutException)
290
+ end
291
+
292
+ it "raises exception for non-existing command" do
293
+ lambda do
294
+ ShellOut("nonexisting", :raise_exceptions => true)
295
+ end.should raise_error(ShellOut::ShellOutException)
296
+ end
297
+ end
298
+
299
+ describe ":out option" do
300
+ it "redirects output of command" do
301
+ fake_stdout = StringIO.new
302
+ ShellOut("echo 42", :out => fake_stdout)
303
+ fake_stdout.string.chomp.should == "42"
304
+ end
305
+ end
306
+
307
+ describe ":out => :return option" do
308
+ it "return the output of command" do
309
+ ShellOut("echo 42", :out => :return).chomp.should == "42"
310
+ end
311
+
312
+ it "return STDERR output of command" do
313
+ ShellOut("ruby -e 'STDERR.puts 42'", :out => :return).chomp.should == "42"
314
+ end
315
+ end
316
+
317
+ describe ":verbose option" do
318
+ it "echoes the command name" do
319
+ STDOUT.intercept { ShellOut("true", :verbose => true) }.should include("true")
320
+ end
321
+ end
322
+
323
+ def with_vanishing_file(f)
324
+ yield
325
+ ensure
326
+ File.delete(f) rescue nil
327
+ end
328
+
329
+ describe ":noop option" do
330
+ it "always returns 0 status" do
331
+ ShellOut("ruby -e 'exit 123'", :noop => true).should == 0
332
+ end
333
+
334
+ it "never executes command" do
335
+ f = "never_executes_command.test"
336
+ with_vanishing_file(f) do
337
+ ShellOut("touch #{ f }", :noop => true)
338
+ File.exist?(f).should == false
339
+ end
340
+ end
341
+ end
342
+
343
+ describe ":dry_run option" do
344
+ it "echoes the command name" do
345
+ STDOUT.intercept { ShellOut("true", :dry_run => true) }.should include("true")
346
+ end
347
+
348
+ it "always returns 0 status" do
349
+ STDOUT.supress { ShellOut("ruby -e 'exit 123'", :dry_run => true) }.should == 0
350
+ end
351
+
352
+ it "never executes command" do
353
+ f = "never_executes_command.test"
354
+ with_vanishing_file(f) do
355
+ STDOUT.supress { ShellOut("touch #{ f }", :dry_run => true) }
356
+ File.exist?(f).should == false
357
+ end
358
+ end
359
+ end
360
+ end
361
+
362
+ describe "#shell_out_with_system" do
363
+ before do
364
+ def ShellOut(*args)
365
+ ShellOut.shell_out_with_system(*args);
366
+ end
367
+ end
368
+
369
+ it_should_behave_like "having base capabilities"
370
+ end
371
+
372
+ describe "#shell_out_with_pty" do
373
+ begin
374
+ require "pty"
375
+ before do
376
+ def ShellOut(*args)
377
+ ShellOut.shell_out_with_pty(*args);
378
+ end
379
+ end
380
+
381
+ it_should_behave_like "having base capabilities"
382
+
383
+ it "uses pseudo-tty" do
384
+ ShellOut("ruby -e 'exit(123) if [STDIN, STDOUT, STDERR].all? { |stream| stream.tty? }'").should == 123
385
+ end
386
+
387
+ it "shows the output of the executing command on own STDOUT" do
388
+ STDOUT.intercept { ShellOut("echo '42'") }.should include("42")
389
+ end
390
+
391
+ it "doesn't pass Ctrl-C to the command and raises Interrupt exception when Ctrl-C is sent" do
392
+ fake_stdin = StringIO.new
393
+ fake_stdin.write ShellOut::CTRL_C_CODE
394
+ fake_stdin.rewind
395
+ int_trap = "ruby -e 'trap(\"INT\") { puts \"SIGINT received\" }; sleep 999'"
396
+ lambda do
397
+ ShellOut(int_trap, :in => fake_stdin)
398
+ end.should raise_error(Interrupt)
399
+
400
+ fake_stdin = StringIO.new
401
+ fake_stdin.write ShellOut::CTRL_C_CODE
402
+ fake_stdin.rewind
403
+ STDOUT.intercept do
404
+ begin
405
+ ShellOut(int_trap, :in => fake_stdin)
406
+ rescue Interrupt
407
+ end
408
+ end.should_not include("SIGINT received")
409
+ end
410
+ rescue LoadError
411
+ it "cannot be tested because 'pty' is not available on this system"
412
+ end
413
+ end
414
+ end
415
+
416
+ exit ::Spec::Runner::CommandLine.run
417
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'rspec/expectations'
3
+ require 'rspec/matchers'
4
+ require 'rspec/core'
@@ -0,0 +1,243 @@
1
+ == Welcome to Rails
2
+
3
+ Rails is a web-application framework that includes everything needed to create
4
+ database-backed web applications according to the Model-View-Control pattern.
5
+
6
+ This pattern splits the view (also called the presentation) into "dumb" templates
7
+ that are primarily responsible for inserting pre-built data in between HTML tags.
8
+ The model contains the "smart" domain objects (such as Account, Product, Person,
9
+ Post) that holds all the business logic and knows how to persist themselves to
10
+ a database. The controller handles the incoming requests (such as Save New Account,
11
+ Update Product, Show Post) by manipulating the model and directing data to the view.
12
+
13
+ In Rails, the model is handled by what's called an object-relational mapping
14
+ layer entitled Active Record. This layer allows you to present the data from
15
+ database rows as objects and embellish these data objects with business logic
16
+ methods. You can read more about Active Record in
17
+ link:files/vendor/rails/activerecord/README.html.
18
+
19
+ The controller and view are handled by the Action Pack, which handles both
20
+ layers by its two parts: Action View and Action Controller. These two layers
21
+ are bundled in a single package due to their heavy interdependence. This is
22
+ unlike the relationship between the Active Record and Action Pack that is much
23
+ more separate. Each of these packages can be used independently outside of
24
+ Rails. You can read more about Action Pack in
25
+ link:files/vendor/rails/actionpack/README.html.
26
+
27
+
28
+ == Getting Started
29
+
30
+ 1. At the command prompt, start a new Rails application using the <tt>rails</tt> command
31
+ and your application name. Ex: rails myapp
32
+ 2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
33
+ 3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!"
34
+ 4. Follow the guidelines to start developing your application
35
+
36
+
37
+ == Web Servers
38
+
39
+ By default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails
40
+ with a variety of other web servers.
41
+
42
+ Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is
43
+ suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
44
+ getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
45
+ More info at: http://mongrel.rubyforge.org
46
+
47
+ Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or
48
+ Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use
49
+ FCGI or proxy to a pack of Mongrels/Thin/Ebb servers.
50
+
51
+ == Apache .htaccess example for FCGI/CGI
52
+
53
+ # General Apache options
54
+ AddHandler fastcgi-script .fcgi
55
+ AddHandler cgi-script .cgi
56
+ Options +FollowSymLinks +ExecCGI
57
+
58
+ # If you don't want Rails to look in certain directories,
59
+ # use the following rewrite rules so that Apache won't rewrite certain requests
60
+ #
61
+ # Example:
62
+ # RewriteCond %{REQUEST_URI} ^/notrails.*
63
+ # RewriteRule .* - [L]
64
+
65
+ # Redirect all requests not available on the filesystem to Rails
66
+ # By default the cgi dispatcher is used which is very slow
67
+ #
68
+ # For better performance replace the dispatcher with the fastcgi one
69
+ #
70
+ # Example:
71
+ # RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
72
+ RewriteEngine On
73
+
74
+ # If your Rails application is accessed via an Alias directive,
75
+ # then you MUST also set the RewriteBase in this htaccess file.
76
+ #
77
+ # Example:
78
+ # Alias /myrailsapp /path/to/myrailsapp/public
79
+ # RewriteBase /myrailsapp
80
+
81
+ RewriteRule ^$ index.html [QSA]
82
+ RewriteRule ^([^.]+)$ $1.html [QSA]
83
+ RewriteCond %{REQUEST_FILENAME} !-f
84
+ RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
85
+
86
+ # In case Rails experiences terminal errors
87
+ # Instead of displaying this message you can supply a file here which will be rendered instead
88
+ #
89
+ # Example:
90
+ # ErrorDocument 500 /500.html
91
+
92
+ ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
93
+
94
+
95
+ == Debugging Rails
96
+
97
+ Sometimes your application goes wrong. Fortunately there are a lot of tools that
98
+ will help you debug it and get it back on the rails.
99
+
100
+ First area to check is the application log files. Have "tail -f" commands running
101
+ on the server.log and development.log. Rails will automatically display debugging
102
+ and runtime information to these files. Debugging info will also be shown in the
103
+ browser on requests from 127.0.0.1.
104
+
105
+ You can also log your own messages directly into the log file from your code using
106
+ the Ruby logger class from inside your controllers. Example:
107
+
108
+ class WeblogController < ActionController::Base
109
+ def destroy
110
+ @weblog = Weblog.find(params[:id])
111
+ @weblog.destroy
112
+ logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
113
+ end
114
+ end
115
+
116
+ The result will be a message in your log file along the lines of:
117
+
118
+ Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1
119
+
120
+ More information on how to use the logger is at http://www.ruby-doc.org/core/
121
+
122
+ Also, Ruby documentation can be found at http://www.ruby-lang.org/ including:
123
+
124
+ * The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/
125
+ * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
126
+
127
+ These two online (and free) books will bring you up to speed on the Ruby language
128
+ and also on programming in general.
129
+
130
+
131
+ == Debugger
132
+
133
+ Debugger support is available through the debugger command when you start your Mongrel or
134
+ Webrick server with --debugger. This means that you can break out of execution at any point
135
+ in the code, investigate and change the model, AND then resume execution!
136
+ You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'
137
+ Example:
138
+
139
+ class WeblogController < ActionController::Base
140
+ def index
141
+ @posts = Post.find(:all)
142
+ debugger
143
+ end
144
+ end
145
+
146
+ So the controller will accept the action, run the first line, then present you
147
+ with a IRB prompt in the server window. Here you can do things like:
148
+
149
+ >> @posts.inspect
150
+ => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
151
+ #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
152
+ >> @posts.first.title = "hello from a debugger"
153
+ => "hello from a debugger"
154
+
155
+ ...and even better is that you can examine how your runtime objects actually work:
156
+
157
+ >> f = @posts.first
158
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
159
+ >> f.
160
+ Display all 152 possibilities? (y or n)
161
+
162
+ Finally, when you're ready to resume execution, you enter "cont"
163
+
164
+
165
+ == Console
166
+
167
+ You can interact with the domain model by starting the console through <tt>script/console</tt>.
168
+ Here you'll have all parts of the application configured, just like it is when the
169
+ application is running. You can inspect domain models, change values, and save to the
170
+ database. Starting the script without arguments will launch it in the development environment.
171
+ Passing an argument will specify a different environment, like <tt>script/console production</tt>.
172
+
173
+ To reload your controllers and models after launching the console run <tt>reload!</tt>
174
+
175
+ == dbconsole
176
+
177
+ You can go to the command line of your database directly through <tt>script/dbconsole</tt>.
178
+ You would be connected to the database with the credentials defined in database.yml.
179
+ Starting the script without arguments will connect you to the development database. Passing an
180
+ argument will connect you to a different database, like <tt>script/dbconsole production</tt>.
181
+ Currently works for mysql, postgresql and sqlite.
182
+
183
+ == Description of Contents
184
+
185
+ app
186
+ Holds all the code that's specific to this particular application.
187
+
188
+ app/controllers
189
+ Holds controllers that should be named like weblogs_controller.rb for
190
+ automated URL mapping. All controllers should descend from ApplicationController
191
+ which itself descends from ActionController::Base.
192
+
193
+ app/models
194
+ Holds models that should be named like post.rb.
195
+ Most models will descend from ActiveRecord::Base.
196
+
197
+ app/views
198
+ Holds the template files for the view that should be named like
199
+ weblogs/index.html.erb for the WeblogsController#index action. All views use eRuby
200
+ syntax.
201
+
202
+ app/views/layouts
203
+ Holds the template files for layouts to be used with views. This models the common
204
+ header/footer method of wrapping views. In your views, define a layout using the
205
+ <tt>layout :default</tt> and create a file named default.html.erb. Inside default.html.erb,
206
+ call <% yield %> to render the view using this layout.
207
+
208
+ app/helpers
209
+ Holds view helpers that should be named like weblogs_helper.rb. These are generated
210
+ for you automatically when using script/generate for controllers. Helpers can be used to
211
+ wrap functionality for your views into methods.
212
+
213
+ config
214
+ Configuration files for the Rails environment, the routing map, the database, and other dependencies.
215
+
216
+ db
217
+ Contains the database schema in schema.rb. db/migrate contains all
218
+ the sequence of Migrations for your schema.
219
+
220
+ doc
221
+ This directory is where your application documentation will be stored when generated
222
+ using <tt>rake doc:app</tt>
223
+
224
+ lib
225
+ Application specific libraries. Basically, any kind of custom code that doesn't
226
+ belong under controllers, models, or helpers. This directory is in the load path.
227
+
228
+ public
229
+ The directory available for the web server. Contains subdirectories for images, stylesheets,
230
+ and javascripts. Also contains the dispatchers and the default HTML files. This should be
231
+ set as the DOCUMENT_ROOT of your web server.
232
+
233
+ script
234
+ Helper scripts for automation and generation.
235
+
236
+ test
237
+ Unit and functional tests along with fixtures. When using the script/generate scripts, template
238
+ test files will be generated for you and placed in this directory.
239
+
240
+ vendor
241
+ External libraries that the application depends on. Also includes the plugins subdirectory.
242
+ If the app has frozen rails, those gems also go here, under vendor/rails/.
243
+ This directory is in the load path.