smalruby-editor 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of smalruby-editor might be problematic. Click here for more details.

Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -1
  4. data/.rubocop.yml +18 -0
  5. data/.travis.yml +24 -23
  6. data/LEGAL +13 -0
  7. data/README.rdoc +1 -0
  8. data/Rakefile +3 -0
  9. data/app/assets/javascripts/application.js +1 -1
  10. data/app/assets/javascripts/editor.js.coffee +60 -15
  11. data/app/assets/stylesheets/application.css +1 -2
  12. data/app/assets/stylesheets/flatstrap-custom.css.scss +2 -0
  13. data/app/controllers/editor_controller.rb +0 -24
  14. data/app/controllers/source_codes_controller.rb +67 -0
  15. data/app/models/source_code.rb +42 -0
  16. data/app/views/editor/index.html.erb +5 -3
  17. data/bin/smalruby-editor +11 -0
  18. data/config/application.rb +8 -20
  19. data/config/boot.rb +3 -0
  20. data/config/environments/development.rb +2 -1
  21. data/config/environments/production.rb +12 -6
  22. data/config/environments/standalone.rb +3 -2
  23. data/config/environments/test.rb +14 -4
  24. data/config/initializers/backtrace_silencers.rb +4 -2
  25. data/config/initializers/session_store.rb +2 -1
  26. data/config/initializers/wrap_parameters.rb +2 -1
  27. data/config/routes.rb +10 -6
  28. data/config/unicorn.rb +7 -6
  29. data/db/migrate/20131216121603_create_source_codes.rb +9 -0
  30. data/db/migrate/20131219045113_add_filename_to_source_code.rb +5 -0
  31. data/db/schema.rb +8 -1
  32. data/lib/smalruby_editor.rb +29 -15
  33. data/lib/smalruby_editor/version.rb +1 -1
  34. data/lib/tasks/rspec.rake +2 -2
  35. data/lib/tasks/rubocop.rake +3 -0
  36. data/public/fonts/FontAwesome.otf +0 -0
  37. data/public/fonts/fontawesome-webfont.eot +0 -0
  38. data/public/fonts/fontawesome-webfont.ttf +0 -0
  39. data/public/fonts/fontawesome-webfont.woff +0 -0
  40. data/smalruby-editor.gemspec +3 -3
  41. data/spec/acceptance/readme.md +3 -0
  42. data/spec/acceptance/text_editor/base.feature +24 -0
  43. data/spec/acceptance/text_editor/check.feature +20 -0
  44. data/spec/acceptance/text_editor/load.feature +74 -0
  45. data/spec/acceptance/text_editor/save.feature +31 -0
  46. data/spec/controllers/editor_controller_spec.rb +2 -63
  47. data/spec/controllers/source_codes_controller_spec.rb +296 -0
  48. data/spec/models/source_code_spec.rb +44 -0
  49. data/spec/spec_helper.rb +123 -65
  50. data/spec/steps/ace_steps.rb +60 -0
  51. data/spec/steps/global_variable.rb +39 -0
  52. data/spec/steps/text_editor_steps.rb +160 -0
  53. data/spec/support/capybara.rb +17 -0
  54. metadata +49 -19
  55. data/spec/acceptance/editor.feature +0 -68
  56. data/spec/steps/editor_steps.rb +0 -123
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ # language: ja
3
+ @javascript
4
+ 機能: Check - プログラムのエラーチェック
5
+ シナリオ: チェックボタンを押してプログラムの正しいかチェックできる
6
+ 前提 "エディタ" 画面を表示する
7
+ かつ テキストエディタに "puts 'Hello, World!'" を入力済みである
8
+
9
+ もし "チェックボタン" をクリックする
10
+ かつ JavaScriptによるリクエストが終わるまで待つ
11
+
12
+ ならば "メッセージ" に "エラー" を含まないこと
13
+
14
+ もし テキストエディタに "puts Hello, World!'" を入力済みである
15
+ かつ "チェックボタン" をクリックする
16
+ かつ JavaScriptによるリクエストが終わるまで待つ
17
+
18
+ ならば "メッセージ" に "エラー" を含むこと
19
+ かつ "メッセージ" に "1行、19文字: syntax error, unexpected tSTRING_BEG, expecting keyword_do or '{' or '('" を含むこと
20
+ かつ "メッセージ" に "1行: unterminated string meets end of file" を含むこと
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ # language: ja
3
+ @javascript
4
+ 機能: Load - プログラムのロード
5
+ シナリオ: プログラムを読み込む
6
+ 前提 "エディタ" 画面を表示する
7
+
8
+ もし "files/01.rb" をロードする
9
+ かつ JavaScriptによるリクエストが終わるまで待つ
10
+
11
+ ならば テキストエディタに "files/01.rb" を読み込むこと
12
+ かつ "プログラム名の入力欄" は "01.rb" であること
13
+
14
+ シナリオ: プログラムを入力済み状態でプログラムを読み込む
15
+ 前提 "エディタ" 画面を表示する
16
+ かつ テキストエディタに "puts 'Hello, World!'" を入力済みである
17
+
18
+ もし "files/01.rb" をロードする
19
+ かつ JavaScriptによるリクエストが終わるまで待つ
20
+
21
+ ならば テキストエディタに "files/01.rb" を読み込むこと
22
+ かつ "プログラム名の入力欄" は "01.rb" であること
23
+
24
+ シナリオ: 間違って画像を読み込もうとする
25
+ 前提 "エディタ" 画面を表示する
26
+
27
+ もし "files/favicon.ico" をロードする
28
+ かつ JavaScriptによるリクエストが終わるまで待つ
29
+
30
+ ならば テキストエディタのプログラムは "" であること
31
+ かつ "プログラム名の入力欄" は "" であること
32
+ かつ "メッセージ" に "favicon.icoはRubyのプログラムではありません" を含むこと
33
+
34
+ シナリオ: プログラムを修正後にロードボタンを押す
35
+ 前提 "エディタ" 画面を表示する
36
+ かつ テキストエディタに "puts 'Hello, World!'" を入力済みである
37
+
38
+ もし "ロードボタン" をクリックする
39
+
40
+ ならば 確認メッセージ "まだセーブしていないのでロードするとプログラムが消えてしまうよ!それでもロードしますか?" を表示すること
41
+
42
+ シナリオ: プログラムを修正後にセーブしてからロードボタンを押す
43
+ 前提 "エディタ" 画面を表示する
44
+ かつ テキストエディタに "puts 'Hello, World!'" を入力済みである
45
+ かつ プログラムの名前に "01.rb" を指定する
46
+ かつ "セーブボタン" をクリックする
47
+ かつ JavaScriptによるリクエストが終わるまで待つ
48
+ かつ ダウンロードが完了するまで待つ
49
+ かつ 実際にはファイルをロードしないようにしておく
50
+
51
+ もし "ロードボタン" をクリックする
52
+
53
+ ならば 確認メッセージを表示しないこと
54
+
55
+ シナリオ: プログラムを修正後にプログラムをロードしてからロードボタンを押す
56
+ 前提 "エディタ" 画面を表示する
57
+ かつ テキストエディタに "puts 'Hello, World!'" を入力済みである
58
+ かつ "files/01.rb" をロードする
59
+ かつ JavaScriptによるリクエストが終わるまで待つ
60
+ かつ 実際にはファイルをロードしないようにしておく
61
+
62
+ もし "ロードボタン" をクリックする
63
+
64
+ ならば 確認メッセージを表示しないこと
65
+
66
+ シナリオ: プログラムを修正後に間違って画像をロードしてからロードボタンを押す
67
+ 前提 "エディタ" 画面を表示する
68
+ かつ テキストエディタに "puts 'Hello, World!'" を入力済みである
69
+ かつ "files/favicon.ico" をロードする
70
+ かつ JavaScriptによるリクエストが終わるまで待つ
71
+
72
+ もし "ロードボタン" をクリックする
73
+
74
+ ならば 確認メッセージ "まだセーブしていないのでロードするとプログラムが消えてしまうよ!それでもロードしますか?" を表示すること
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ # language: ja
3
+ @javascript
4
+ 機能: Save - プログラムのセーブ
5
+ シナリオ: セーブボタンを押してプログラムを保存できる
6
+ 前提 "エディタ" 画面を表示する
7
+ かつ テキストエディタに "puts 'Hello, World!'" を入力済みである
8
+ かつ プログラムの名前に "01.rb" を指定する
9
+
10
+ もし "セーブボタン" をクリックする
11
+ かつ JavaScriptによるリクエストが終わるまで待つ
12
+
13
+ ならば "01.rb" をダウンロードすること
14
+
15
+ シナリオ: 日本語のファイル名でプログラムを保存できる
16
+ 前提 "エディタ" 画面を表示する
17
+ かつ テキストエディタに "puts 'Hello, World!'" を入力済みである
18
+ かつ プログラムの名前に "日本語.rb" を指定する
19
+
20
+ もし "セーブボタン" をクリックする
21
+ かつ JavaScriptによるリクエストが終わるまで待つ
22
+
23
+ ならば "日本語.rb" をダウンロードすること
24
+
25
+ シナリオ: ファイル名を入力せずにセーブボタンを押す
26
+ 前提 "エディタ" 画面を表示する
27
+
28
+ もし "セーブボタン" をクリックする
29
+
30
+ ならば ダウンロードしないこと
31
+ かつ "プログラム名の入力欄" にフォーカスがあること
@@ -3,71 +3,10 @@ require 'spec_helper'
3
3
  require 'nkf'
4
4
 
5
5
  describe EditorController do
6
- describe "エディタ画面を表示する (GET 'index')" do
6
+ describe 'エディタ画面を表示する (GET index)' do
7
7
  it 'returns http success' do
8
- get 'index'
8
+ get :index
9
9
  response.should be_success
10
10
  end
11
11
  end
12
-
13
- describe "プログラムを保存する (GET 'save_file')" do
14
- it 'アップロードされたプログラムをダウンロードできる' do
15
- params = {
16
- filename: '01.rb',
17
- source: 'puts "Hello, World!"',
18
- }.with_indifferent_access
19
-
20
- expect(@controller).to receive(:send_data)
21
- .with(params[:source], filename: params[:filename],
22
- disposition: 'attachment',
23
- type: 'text/plain; charset=utf-8')
24
- .once.and_call_original
25
-
26
- get 'save_file', params
27
-
28
- expect(response).to be_success
29
- end
30
- end
31
-
32
- describe "プログラムを読み込む (POST 'load_file')" do
33
- before do
34
- post 'load_file', load_file: load_file
35
- end
36
-
37
- describe '正常系' do
38
- let(:load_file) { fixture_file_upload('files/01.rb', 'text/plain') }
39
-
40
- it { expect(response).to be_success }
41
-
42
- it 'アップロードされたプログラムの情報をJSON形式でダウンロードできる' do
43
- load_file.rewind
44
- file = {
45
- name: load_file.original_filename,
46
- type: load_file.content_type,
47
- size: load_file.size,
48
- source: load_file.read.force_encoding('utf-8'),
49
- }
50
- expect(response.body).to be_json_eql(file.to_json)
51
- end
52
- end
53
-
54
- describe '異常系' do
55
- context '画像をアップロードした場合' do
56
- let(:load_file) { fixture_file_upload('files/favicon.ico',
57
- 'application/octet-stream') }
58
-
59
- it { expect(response).to be_success }
60
-
61
- it 'エラーを返す' do
62
- file = {
63
- name: load_file.original_filename,
64
- error: 'Rubyのプログラムではありません',
65
- }
66
- file.each do |path, value|
67
- expect(response.body).to include_json(value.to_json).at_path(path.to_s)
68
- end
69
- end
70
- end
71
- end
72
- end
73
12
  end
@@ -0,0 +1,296 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe SourceCodesController do
5
+ describe 'プログラムが正しいかチェックする (XHR POST check)' do
6
+ before do
7
+ params = {
8
+ source_code: {
9
+ data: data,
10
+ }
11
+ }.with_indifferent_access
12
+
13
+ xhr :post, :check, params
14
+ end
15
+
16
+ context 'プログラムが正しい場合' do
17
+ let(:data) { 'puts "Hello, World!"' }
18
+
19
+ it { expect(response).to be_success }
20
+
21
+ it do
22
+ expected = parse_json(SourceCode.new(data: data).check_syntax.to_json)
23
+ expect(parse_json(response.body)).to eq(expected)
24
+ end
25
+ end
26
+
27
+ context 'プログラムが正しくない場合' do
28
+ let(:data) { 'puts Hello, World!"' }
29
+
30
+ it { expect(response).to be_success }
31
+
32
+ it do
33
+ expected = parse_json(SourceCode.new(data: data).check_syntax.to_json)
34
+ expect(parse_json(response.body)).to eq(expected)
35
+ end
36
+ end
37
+ end
38
+
39
+ describe 'プログラムを保存する (XHR POST create)' do
40
+ let(:params) {
41
+ {
42
+ source_code: {
43
+ filename: '01.rb',
44
+ data: 'puts "Hello, World!"',
45
+ }
46
+ }
47
+ }
48
+
49
+ describe 'レスポンス' do
50
+ subject {
51
+ xhr :post, :create, params
52
+ response
53
+ }
54
+
55
+ it { should be_success }
56
+ it {
57
+ ids = SourceCode.all.map(&:id)
58
+ expect(parse_json(subject.body))
59
+ .to eq('source_code' => {
60
+ 'id' => SourceCode.find(SourceCode.all.map(&:id) - ids)
61
+ .first.id
62
+ })
63
+ }
64
+ end
65
+
66
+ it 'アップロードされたプログラムを保存できる' do
67
+ expect {
68
+ xhr :post, :create, params
69
+ }.to change { SourceCode.count }.by(1)
70
+ end
71
+
72
+ describe 'アップロードされたプログラム' do
73
+ subject {
74
+ ids = SourceCode.all.map(&:id)
75
+ xhr :post, :create, params
76
+ SourceCode.find((SourceCode.all.map(&:id) - ids).first)
77
+ }
78
+
79
+ its(:data) { should eq(params[:source_code][:data]) }
80
+ its(:filename) { should eq(params[:source_code][:filename]) }
81
+ end
82
+
83
+ describe 'session' do
84
+ before {
85
+ ids = SourceCode.all.map(&:id)
86
+ xhr :post, :create, params
87
+ @created_source_code =
88
+ SourceCode.find((SourceCode.all.map(&:id) - ids).first)
89
+ }
90
+
91
+ subject { session }
92
+
93
+ it '[:source_code][:id]はアップロードしたプログラムのIDである' do
94
+ expect(subject[:source_code][:id]).to eq(@created_source_code.id)
95
+ end
96
+
97
+ it '[:source_code][:digest]はアップロードしたプログラムのSHA256のハッシュ値である' do
98
+ expect(subject[:source_code][:digest])
99
+ .to eq(@created_source_code.digest)
100
+ end
101
+ end
102
+ end
103
+
104
+ describe 'プログラムをダウンロードしてサーバ上から削除する (DELETE download)' do
105
+ let(:source_code) {
106
+ SourceCode.create!(filename: '01.rb', data: 'puts "Hello, World!"')
107
+ }
108
+
109
+ context 'セッションが正しい場合' do
110
+ let(:_session) {
111
+ {
112
+ source_code: {
113
+ id: source_code.id,
114
+ digest: source_code.digest,
115
+ }
116
+ }
117
+ }
118
+
119
+ context 'ファイル名が半角英数字の場合' do
120
+ before do
121
+ allow(@controller).to receive(:send_data).and_call_original
122
+ delete :download, {}, _session
123
+ end
124
+
125
+ specify 'プログラムをダウンロードする' do
126
+ expect(@controller).to have_received(:send_data)
127
+ .with(source_code.data,
128
+ filename: source_code.filename,
129
+ disposition: 'attachment',
130
+ type: 'text/plain; charset=utf-8')
131
+ .once
132
+ end
133
+
134
+ specify 'プログラムをサーバ上から削除する' do
135
+ expect(SourceCode).to have(0).records
136
+ end
137
+
138
+ specify 'セッションから[:source_code]を削除する' do
139
+ expect(session[:source_code]).to be_nil
140
+ end
141
+ end
142
+
143
+ context 'ファイル名が日本語の場合' do
144
+ # rubocop:disable LineLength
145
+ USER_AGENT = {
146
+ IE11: 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko',
147
+ IE10: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Touch)',
148
+ Chrome20: 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11',
149
+ Firefox14: 'Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1',
150
+ }
151
+ # rubocop:enable LineLength
152
+
153
+ let(:source_code) {
154
+ SourceCode.create!(filename: '日本語.rb', data: 'puts "Hello, World!"')
155
+ }
156
+
157
+ [:IE10, :IE11].each do |browser|
158
+ context "ブラウザが#{browser}の場合" do
159
+ before do
160
+ @request.env['HTTP_USER_AGENT'] = USER_AGENT[browser]
161
+ allow(@controller).to receive(:send_data).and_call_original
162
+ delete :download, {}, _session
163
+ end
164
+
165
+ specify 'プログラムをダウンロードする' do
166
+ expect(@controller).to have_received(:send_data)
167
+ .with(source_code.data,
168
+ filename: ERB::Util.url_encode(source_code.filename),
169
+ disposition: 'attachment',
170
+ type: 'text/plain; charset=utf-8')
171
+ .once
172
+ end
173
+ end
174
+ end
175
+
176
+ [:Chrome20, :Firefox14].each do |browser|
177
+ context "ブラウザが#{browser}の場合" do
178
+ before do
179
+ @request.env['HTTP_USER_AGENT'] = USER_AGENT[browser]
180
+ allow(@controller).to receive(:send_data).and_call_original
181
+ delete :download, {}, _session
182
+ end
183
+
184
+ specify 'プログラムをダウンロードする' do
185
+ expect(@controller).to have_received(:send_data)
186
+ .with(source_code.data,
187
+ filename: source_code.filename,
188
+ disposition: 'attachment',
189
+ type: 'text/plain; charset=utf-8')
190
+ .once
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ context 'セッションが不正な場合' do
198
+ shared_examples 'raise exception' do
199
+ it {
200
+ expect {
201
+ delete :download, {}, _session
202
+ }.to raise_exception
203
+ }
204
+ end
205
+
206
+ context 'セッションに[:source_code]がない場合' do
207
+ let(:_session) { {} }
208
+
209
+ include_examples 'raise exception'
210
+ end
211
+
212
+ context 'セッションに[:source_code][:id]がない場合' do
213
+ let(:_session) {
214
+ {
215
+ source_code: {
216
+ digest: source_code.digest,
217
+ }
218
+ }
219
+ }
220
+
221
+ include_examples 'raise exception'
222
+ end
223
+
224
+ context 'セッションに[:source_code][:digest]がない場合' do
225
+ let(:_session) {
226
+ {
227
+ source_code: {
228
+ id: source_code.id,
229
+ }
230
+ }
231
+ }
232
+
233
+ include_examples 'raise exception'
234
+ end
235
+
236
+ context 'セッションの[:source_code][:digest]が不正な場合' do
237
+ let(:_session) {
238
+ {
239
+ source_code: {
240
+ id: source_code.id,
241
+ digest: 'invalid_digest',
242
+ }
243
+ }
244
+ }
245
+
246
+ include_examples 'raise exception'
247
+ end
248
+ end
249
+ end
250
+
251
+ describe 'プログラムを読み込む (POST load)' do
252
+ before do
253
+ post :load, source_code: { file: load_file }
254
+ end
255
+
256
+ describe '正常系' do
257
+ let(:load_file) { fixture_file_upload('files/01.rb', 'text/plain') }
258
+
259
+ it { expect(response).to be_success }
260
+
261
+ it 'アップロードされたプログラムの情報をJSON形式でダウンロードできる' do
262
+ load_file.rewind
263
+ expected = {
264
+ source_code: {
265
+ filename: load_file.original_filename,
266
+ type: load_file.content_type,
267
+ size: load_file.size,
268
+ data: load_file.read.force_encoding('utf-8'),
269
+ }
270
+ }.to_json
271
+ expect(response.body).to be_json_eql(expected)
272
+ end
273
+ end
274
+
275
+ describe '異常系' do
276
+ context '画像をアップロードした場合' do
277
+ let(:load_file) {
278
+ fixture_file_upload('files/favicon.ico', 'application/octet-stream')
279
+ }
280
+
281
+ it { expect(response).to be_success }
282
+
283
+ it 'エラーを返す' do
284
+ info = {
285
+ filename: load_file.original_filename,
286
+ error: 'Rubyのプログラムではありません',
287
+ }
288
+ info.each do |path, value|
289
+ expect(response.body)
290
+ .to include_json(value.to_json).at_path("source_code/#{path}")
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end