smalruby-editor 0.0.5 → 0.0.6

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.

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