at_coder_friends 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f97a6bf516dc7443c791b4e2ace991b34eaa47d5
4
- data.tar.gz: d48834719bdceceb66cd4f1476c54e7b1c5602af
3
+ metadata.gz: 7ea93eb5e8bef9fd743ff3f5767cc906264b878b
4
+ data.tar.gz: 6b41067f5105e20859e2a5e5c0d120ef52e29b9a
5
5
  SHA512:
6
- metadata.gz: b4ef816ece17840560b7b9bd39ac4ef56ae346fd859a3b1d737dd9a759faecf77a80244fdd56c97a7aa4542f3bdb65e4346f10239c1cd63e7163038cebc95271
7
- data.tar.gz: 044e53c76ac9789c2a049a3476141e0d81d103b0d66599bb18e13c0c0b66ec64c65b7fb6062e7ec761d6ba0c536f00d234aa060514eb833e69fc6b922692401e
6
+ metadata.gz: 963c99df359372cbc89cbe6efb4ccf8c3ffccfc50e68364dc62e7314574a1ad30cfa6e12086cb994a844f5ce71d93a1eb543fe76c50a8cf361b4f18cf7387669
7
+ data.tar.gz: 81468b9c45065d8c8de6ef3c6355e400ce56d784c0743ad2d44e50784c5ebc3529ab3d5f4808561feea5ec388d53697a5b5d34030360b85f8cd676627c4a7a3b
data/.gitignore CHANGED
@@ -7,7 +7,12 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  /vendor/
10
- /.at_coder_friends.yml
11
10
 
12
11
  # rspec failure tracking
13
12
  .rspec_status
13
+
14
+ # at_coder_friends
15
+ .at_coder_friends.yml
16
+
17
+ # rvm
18
+ .ruby-version
data/.rubocop.yml CHANGED
@@ -5,9 +5,6 @@ AllCops:
5
5
  - 'spec/fixtures/**/*'
6
6
  TargetRubyVersion: 2.3
7
7
 
8
- Layout/EndOfLine:
9
- EnforcedStyle: lf
10
-
11
8
  Lint/AmbiguousBlockAssociation:
12
9
  Exclude:
13
10
  - 'spec/**/*.rb'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- at_coder_friends (0.4.0)
4
+ at_coder_friends (0.5.0)
5
5
  launchy (~> 2.4.3)
6
6
  mechanize (~> 2.0)
7
7
 
@@ -43,7 +43,7 @@ GEM
43
43
  mini_portile2 (~> 2.4.0)
44
44
  ntlm-http (0.1.1)
45
45
  public_suffix (4.0.1)
46
- rake (12.3.3)
46
+ rake (13.0.0)
47
47
  rspec (3.8.0)
48
48
  rspec-core (~> 3.8.0)
49
49
  rspec-expectations (~> 3.8.0)
@@ -58,7 +58,7 @@ GEM
58
58
  rspec-support (~> 3.8.0)
59
59
  rspec-support (3.8.0)
60
60
  safe_yaml (1.0.5)
61
- simplecov (0.17.0)
61
+ simplecov (0.17.1)
62
62
  docile (~> 1.1)
63
63
  json (>= 1.8, < 3)
64
64
  simplecov-html (~> 0.10.0)
@@ -66,7 +66,7 @@ GEM
66
66
  unf (0.1.4)
67
67
  unf_ext
68
68
  unf_ext (0.0.7.6)
69
- webmock (3.7.3)
69
+ webmock (3.7.6)
70
70
  addressable (>= 2.3.6)
71
71
  crack (>= 0.3.2)
72
72
  hashdiff (>= 0.4.0, < 2.0.0)
@@ -79,7 +79,7 @@ PLATFORMS
79
79
  DEPENDENCIES
80
80
  at_coder_friends!
81
81
  bundler (~> 2.0)
82
- rake (~> 12.3)
82
+ rake (~> 13.0)
83
83
  rspec (~> 3.0)
84
84
  simplecov (~> 0.10)
85
85
  webmock (~> 3.0)
data/README.md CHANGED
@@ -35,17 +35,6 @@ Or install it yourself as:
35
35
 
36
36
  $ gem install at_coder_friends
37
37
 
38
- ## Configuration
39
-
40
- Create ```.at_coder_friends.yml``` and place it in the working directory (or parent of working directory).
41
-
42
- ```yaml
43
- user: <user>
44
- password: <password>
45
- ```
46
-
47
- See [CONFIGURATION.md](docs/CONFIGURATION.md) for details.
48
-
49
38
  ## Usage
50
39
 
51
40
  ### Setup
@@ -91,7 +80,11 @@ at_coder_friends submit /path/to/contest/source_file
91
80
  - Suffixes (start with underscore) may be added to the file name (e.g. ```A_v2.rb```),
92
81
  so that you can try multiple codes for one problem.
93
82
 
94
- ### Notes
83
+ ## Configuration
84
+
85
+ See [CONFIGURATION.md](docs/CONFIGURATION.md) for details.
86
+
87
+ ## Notes
95
88
 
96
89
  - Compilation is not supported.
97
90
  - Source generator supports only ruby and C++.
@@ -107,41 +100,64 @@ It is convenient to use AtCoderFriends from Sublime Text plugin.
107
100
  - Run **Configure Tasks** from the global Terminal menu.
108
101
  - Select the **Create tasks.json file from template** entry.
109
102
  - Select **Others** from the list.
110
- - Add following tasks to ```tasks.json```.
103
+ - Add following settings to ```tasks.json```.
111
104
 
112
105
  ```JSON
113
106
  {
114
107
  "version": "2.0.0",
115
108
  "tasks": [
116
109
  {
117
- "label": "af test-one",
110
+ "label": "AtCoderFriends:New Contest",
111
+ "type": "shell",
112
+ "command": "at_coder_friends",
113
+ "args": [
114
+ "setup",
115
+ "${input:contestName}"
116
+ ],
117
+ "problemMatcher": [],
118
+ "group": "none"
119
+ },
120
+ {
121
+ "label": "AtCoderFriends:Test One",
118
122
  "type": "shell",
119
123
  "command": "at_coder_friends",
120
124
  "args": [
121
125
  "test-one",
122
126
  "${file}"
123
127
  ],
124
- "group": "none",
128
+ "problemMatcher": [],
129
+ "group": "none"
125
130
  },
126
131
  {
127
- "label": "af test-all",
132
+ "label": "AtCoderFriends:Test All",
128
133
  "type": "shell",
129
134
  "command": "at_coder_friends",
130
135
  "args": [
131
136
  "test-all",
132
137
  "${file}"
133
138
  ],
139
+ "problemMatcher": [],
134
140
  "group": "none"
135
141
  },
136
142
  {
137
- "label": "af submit",
143
+ "label": "AtCoderFriends:Submit",
138
144
  "type": "shell",
139
145
  "command": "at_coder_friends",
140
146
  "args": [
141
147
  "submit",
142
148
  "${file}"
143
149
  ],
150
+ "problemMatcher": [],
144
151
  "group": "none",
152
+ },
153
+ ...
154
+ ],
155
+ "inputs": [
156
+ {
157
+ "id": "contestName",
158
+ "type": "promptString",
159
+ "default": "",
160
+ "description": "Contest Name"
145
161
  }
146
162
  ]
147
163
  }
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency 'mechanize', '~> 2.0'
35
35
 
36
36
  spec.add_development_dependency 'bundler', '~> 2.0'
37
- spec.add_development_dependency 'rake', '~> 12.3'
37
+ spec.add_development_dependency 'rake', '~> 13.0'
38
38
  spec.add_development_dependency 'rspec', '~> 3.0'
39
39
  spec.add_development_dependency 'simplecov', '~> 0.10'
40
40
  spec.add_development_dependency 'webmock', '~> 3.0'
@@ -2,15 +2,23 @@
2
2
 
3
3
  ## Configuration file
4
4
 
5
+ You can specify AtCoderFriends settings
6
+ in the configuration file ```.at_coder_friends.yml```.
7
+ Place it in your working directory (or higher).
8
+
5
9
  AtCoderFriends の動作に関する設定は
6
- 設定ファイル 「.at_coder_friends.yml に記述します。
10
+ 設定ファイル ```.at_coder_friends.yml``` に記述します。
7
11
  このファイルは作業ディレクトリ(またはその上位ディレクトリ)に配置します。
8
12
 
9
13
  ## Default settings
10
14
 
11
- デフォルトは以下のような設定になっています。
12
- 「.at_coder_friends.yml」が見つかると、その内容がデフォルト設定にマージされるので、
13
- 新しい設定項目の追加や、既存の設定項目の上書きが可能です。
15
+ The default settings are as follows.
16
+ If you add new settings or change existing settings in ```.at_coder_friends.yml```,
17
+ Its contents are merged with the default settings.
18
+
19
+ デフォルトの設定は以下のようになっています。
20
+ ```.at_coder_friends.yml```で新しい設定項目の追加や、既存の設定項目の変更をすると、
21
+ その内容がデフォルト設定にマージされます。
14
22
 
15
23
  ```YAML
16
24
  user: ~
@@ -106,34 +114,49 @@ ext_settings:
106
114
  ## Configuration options
107
115
 
108
116
  - user
109
- AtCoderのログインユーザーID
117
+ AtCoder username
118
+ If omitted, you are prompted to enter it on the first run.
119
+ AtCoderのユーザ名
120
+ 省略した場合、初回の実行時に入力を求められます
110
121
 
111
122
  - password
112
- AtCoderのログインパスワード
123
+ AtCoder password
124
+ If omitted, you are prompted to enter it on the first run.
125
+ AtCoderのパスワード
126
+ 省略した場合、初回の実行時に入力を求められます
113
127
 
114
128
  - ext_settings
129
+ Extension specific settings
115
130
  拡張子ごとの設定
116
131
 
117
132
  - _'ext'_
133
+ Target extension name
118
134
  対象となる拡張子
119
135
 
120
136
  - submit_lang
137
+ Language ID of submission language
121
138
  提出言語の言語ID
122
139
 
123
140
  - test_cmd
141
+ Execution command for test
142
+ If not set, the test will run using AtCoder "Custom Test" page.
124
143
  テストで使用する実行コマンドの設定
125
- 未設定の場合はAtCoderの「コードテスト」ページでテストをします
144
+ 未設定の場合、テストはAtCoderの「コードテスト」ページを使用して行われます
126
145
 
127
146
  - default
147
+ Default execution command
128
148
  デフォルトの実行コマンド
129
149
 
130
150
  - wndows
151
+ Execution command for Windows(optional)
131
152
  Windows専用実行コマンド(あれば設定)
132
153
 
133
154
  - macosx
155
+ Execution command for MacOS(optional)
134
156
  MacOS専用実行コマンド(あれば設定)
135
157
 
136
158
  - linux
159
+ Execution command for Linux(optional)
137
160
  Linux専用実行コマンド(あれば設定)
138
161
 
139
162
  ### Language ID list (2019/09/16)
@@ -197,19 +220,24 @@ ext_settings:
197
220
  |3525|COBOL - Fixed (OpenCOBOL 1.1.0)|
198
221
  |3526|COBOL - Free (OpenCOBOL 1.1.0)|
199
222
 
200
- ### Variables available in test_cmd
223
+ ### Variables in test_cmd string
224
+
225
+ The following variables in test_cmd string
226
+ is substituted with the path information of the target file
227
+ during execution.
201
228
 
202
- test_cmd の文字列中の以下のパターンは、
203
- テスト対象のソースファイルの情報に置換された後、実行されます。
229
+ test_cmd 文字列中の以下の変数には、
230
+ 実行時に対象ファイルのパス情報が展開されます。
204
231
 
205
- |パターン|設定値|
232
+ | Variable | Descripton |
206
233
  |---|---|
207
- |{dir}|ソースファイルの置かれているディレクトリ|
208
- |{base}|ソースファイルの拡張子を除いたファイル名|
234
+ |{dir}|the directory name of the file<br>ファイルの置かれているディレクトリ名|
235
+ |{base}|the file name excluding the extension<br>拡張子を除いたファイル名|
209
236
 
210
237
  ## Examples of overwriting settings
211
238
 
212
- - .pyをローカル環境のPythonでテスト
239
+ - Test .py with local Python
240
+ .pyをローカル環境のPythonでテスト
213
241
  ```YAML
214
242
  ext_settings:
215
243
  'py':
@@ -217,21 +245,24 @@ test_cmd の文字列中の以下のパターンは、
217
245
  default: 'python "{dir}/{base}.py"'
218
246
  ```
219
247
 
220
- - .rb を「コードテスト」ページでテスト
248
+ - Test .rb with "Custom Test" page
249
+ .rb を「コードテスト」ページでテスト
221
250
  ```YAML
222
251
  ext_settings:
223
252
  'rb':
224
253
  test_cmd: ~
225
254
  ```
226
255
 
227
- - .pl6(Perl6)の設定を追加
256
+ - Add .pl6(Perl6) settings
257
+ .pl6(Perl6)の設定を追加
228
258
  ```YAML
229
259
  ext_settings:
230
260
  'pl6':
231
261
  submit_lang: '3522' # Perl6
232
262
  ```
233
263
 
234
- - .cxxの提出言語をC++14(Clang)に変更
264
+ - Change submission language of .cxx to C++14(Clang)
265
+ .cxxの提出言語をC++14(Clang)に変更
235
266
  ```YAML
236
267
  ext_settings:
237
268
  'cxx':
@@ -14,4 +14,5 @@ require 'at_coder_friends/format_parser'
14
14
  require 'at_coder_friends/ruby_generator'
15
15
  require 'at_coder_friends/cxx_generator'
16
16
  require 'at_coder_friends/emitter'
17
+ require 'at_coder_friends/context'
17
18
  require 'at_coder_friends/cli'
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'optparse'
4
+ require 'launchy'
4
5
 
5
6
  module AtCoderFriends
6
7
  # command line interface
7
8
  class CLI
8
- include PathUtil
9
-
10
9
  EXITING_OPTIONS = %i[version].freeze
11
- OPTION_BANNER =
10
+ OPTION_BANNER =
12
11
  <<~TEXT
13
12
  Usage:
14
13
  at_coder_friends setup path/contest # setup contest folder
@@ -21,12 +20,13 @@ module AtCoderFriends
21
20
  STATUS_SUCCESS = 0
22
21
  STATUS_ERROR = 1
23
22
 
23
+ attr_reader :ctx
24
+
24
25
  def run(args = ARGV)
25
26
  parse_options!(args)
26
27
  handle_exiting_option
27
28
  raise ParamError, 'command or path is not specified.' if args.size < 2
28
29
 
29
- @config = ConfigLoader.load_config(args[1])
30
30
  exec_command(*args)
31
31
  STATUS_SUCCESS
32
32
  rescue AtCoderFriends::ParamError => e
@@ -46,6 +46,9 @@ module AtCoderFriends
46
46
  opts.on('-v', '--version', 'Display version.') do
47
47
  @options[:version] = true
48
48
  end
49
+ opts.on('-d', '--debug', 'Display debug info.') do
50
+ @options[:debug] = true
51
+ end
49
52
  end
50
53
  @usage = op.to_s
51
54
  @options = {}
@@ -61,73 +64,72 @@ module AtCoderFriends
61
64
  exit STATUS_SUCCESS
62
65
  end
63
66
 
64
- def exec_command(command, path, id = nil)
67
+ def exec_command(command, path, *args)
68
+ @ctx = Context.new(@options, path)
65
69
  case command
66
70
  when 'setup'
67
- setup(path)
71
+ setup
68
72
  when 'test-one'
69
- test_one(path, id)
73
+ test_one(*args)
70
74
  when 'test-all'
71
- test_all(path)
75
+ test_all
72
76
  when 'submit'
73
- submit(path)
77
+ submit
74
78
  when 'judge-one'
75
- judge_one(path, id)
79
+ judge_one(*args)
76
80
  when 'judge-all'
77
- judge_all(path)
81
+ judge_all
78
82
  when 'open-contest'
79
- open_contest(path)
83
+ open_contest
80
84
  else
81
85
  raise ParamError, "unknown command: #{command}"
82
86
  end
87
+ ctx.post_process
83
88
  end
84
89
 
85
- def setup(path)
90
+ def setup
91
+ path = ctx.path
86
92
  raise AppError, "#{path} is not empty." \
87
93
  if Dir.exist?(path) && !Dir["#{path}/*"].empty?
88
94
 
89
- agent = ScrapingAgent.new(contest_name(path), @config)
90
95
  parser = FormatParser.new
91
96
  rb_gen = RubyGenerator.new
92
97
  cxx_gen = CxxGenerator.new
93
- emitter = Emitter.new(path)
94
- agent.fetch_all do |pbm|
98
+ ctx.scraping_agent.fetch_all do |pbm|
95
99
  parser.process(pbm)
96
100
  rb_gen.process(pbm)
97
101
  cxx_gen.process(pbm)
98
- emitter.emit(pbm)
102
+ ctx.emitter.emit(pbm)
99
103
  end
100
104
  end
101
105
 
102
- def test_one(path, id)
103
- id ||= 1
104
- SampleTestRunner.new(path, @config).test_one(id)
106
+ def test_one(id = 1)
107
+ ctx.sample_test_runner.test_one(id)
105
108
  end
106
109
 
107
- def test_all(path)
108
- SampleTestRunner.new(path, @config).test_all
109
- Verifier.new(path).verify
110
+ def test_all
111
+ ctx.sample_test_runner.test_all
112
+ ctx.verifier.verify
110
113
  end
111
114
 
112
- def submit(path)
113
- vf = Verifier.new(path)
115
+ def submit
116
+ vf = ctx.verifier
114
117
  raise AppError, "#{vf.file} has not been tested." unless vf.verified?
115
118
 
116
- ScrapingAgent.new(contest_name(path), @config).submit(path)
119
+ ctx.scraping_agent.submit
117
120
  vf.unverify
118
121
  end
119
122
 
120
- def judge_one(path, id)
121
- id ||= ''
122
- JudgeTestRunner.new(path, @config).judge_one(id)
123
+ def judge_one(id = '')
124
+ ctx.judge_test_runner.judge_one(id)
123
125
  end
124
126
 
125
- def judge_all(path)
126
- JudgeTestRunner.new(path, @config).judge_all
127
+ def judge_all
128
+ ctx.judge_test_runner.judge_all
127
129
  end
128
130
 
129
- def open_contest(path)
130
- ScrapingAgent.new(contest_name(path), @config).open_contest
131
+ def open_contest
132
+ Launchy.open(ctx.scraping_agent.contest_url)
131
133
  end
132
134
  end
133
135
  end
@@ -7,12 +7,12 @@ module AtCoderFriends
7
7
  # loads configuration file from the specified directory.
8
8
  class ConfigLoader
9
9
  DOTFILE = '.at_coder_friends.yml'
10
- ACF_HOME = File.realpath(File.join(File.dirname(__FILE__), '..', '..'))
10
+ ACF_HOME = File.realpath(File.join(__dir__, '..', '..'))
11
11
  DEFAULT_FILE = File.join(ACF_HOME, 'config', 'default.yml')
12
12
 
13
13
  class << self
14
- def load_config(target_dir)
15
- path = config_file_for(target_dir)
14
+ def load_config(ctx)
15
+ path = config_file_for(ctx.path)
16
16
  config = load_yaml(path)
17
17
  return config if path == DEFAULT_FILE
18
18
 
@@ -43,7 +43,7 @@ module AtCoderFriends
43
43
  end
44
44
 
45
45
  def merge(base_hash, derived_hash)
46
- res = base_hash.merge(derived_hash) do |_, base_val, derived_val|
46
+ res = base_hash.merge(derived_hash || {}) do |_, base_val, derived_val|
47
47
  if base_val.is_a?(Hash) && derived_val.is_a?(Hash)
48
48
  merge(base_val, derived_val)
49
49
  else
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtCoderFriends
4
+ # Holds applicaion global information
5
+ # - command line options
6
+ # - target path
7
+ # - configuration
8
+ # - application modules
9
+ class Context
10
+ attr_reader :options, :path
11
+
12
+ def initialize(options, path)
13
+ @options = options
14
+ @path = File.expand_path(path)
15
+ end
16
+
17
+ def config
18
+ @config ||= ConfigLoader.load_config(self)
19
+ end
20
+
21
+ def scraping_agent
22
+ @scraping_agent ||= ScrapingAgent.new(self)
23
+ end
24
+
25
+ def sample_test_runner
26
+ @sample_test_runner ||= SampleTestRunner.new(self)
27
+ end
28
+
29
+ def judge_test_runner
30
+ @judge_test_runner ||= JudgeTestRunner.new(self)
31
+ end
32
+
33
+ def verifier
34
+ @verifier ||= Verifier.new(self)
35
+ end
36
+
37
+ def emitter
38
+ @emitter ||= Emitter.new(self)
39
+ end
40
+
41
+ def post_process
42
+ @scraping_agent&.save_session
43
+ end
44
+ end
45
+ end
@@ -8,9 +8,9 @@ module AtCoderFriends
8
8
  class Emitter
9
9
  include PathUtil
10
10
 
11
- def initialize(dir)
12
- @src_dir = dir
13
- @smp_dir = smp_dir(dir)
11
+ def initialize(ctx)
12
+ @src_dir = ctx.path
13
+ @smp_dir = smp_dir(@src_dir)
14
14
  end
15
15
 
16
16
  def emit(pbm)
@@ -5,8 +5,8 @@ module AtCoderFriends
5
5
  class JudgeTestRunner < TestRunner
6
6
  include PathUtil
7
7
 
8
- def initialize(path, config)
9
- super(path, config)
8
+ def initialize(ctx)
9
+ super(ctx)
10
10
  @cases_dir = cases_dir(@dir)
11
11
  @smp_dir = smp_dir(@dir)
12
12
  end
@@ -9,13 +9,11 @@ module AtCoderFriends
9
9
  CASES_DIR = 'cases'
10
10
 
11
11
  def contest_name(path)
12
- path = File.expand_path(path)
13
12
  dir = File.file?(path) ? File.dirname(path) : path
14
13
  File.basename(dir).delete('#').downcase
15
14
  end
16
15
 
17
16
  def split_prg_path(path)
18
- path = File.expand_path(path)
19
17
  dir, prg = File.split(path)
20
18
  base, ext = prg.split('.')
21
19
  q = base.split('_')[0]
@@ -5,8 +5,8 @@ module AtCoderFriends
5
5
  class SampleTestRunner < TestRunner
6
6
  include PathUtil
7
7
 
8
- def initialize(path, config)
9
- super(path, config)
8
+ def initialize(ctx)
9
+ super(ctx)
10
10
  @smp_dir = smp_dir(@dir)
11
11
  end
12
12
 
@@ -1,39 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'uri'
4
+ require 'cgi'
5
+ require 'json'
4
6
  require 'mechanize'
5
7
  require 'logger'
6
8
  require 'English'
7
- require 'launchy'
8
- require 'json'
9
+ require 'io/console'
9
10
 
10
11
  module AtCoderFriends
11
12
  # scrapes AtCoder contest site and
12
13
  # - fetches problems
13
14
  # - submits sources
15
+ # - runs tests on custom_test page
14
16
  class ScrapingAgent
15
17
  include PathUtil
16
- BASE_URL = 'https://atcoder.jp/'
18
+ BASE_URL = 'https://atcoder.jp/'
17
19
  XPATH_SECTION = '//h3[.="%<title>s"]/following-sibling::section'
20
+ XPATH_USERNAME = '//*[@id="navbar-collapse"]/ul[2]/li[2]/a'
21
+ SESSION_STORE =
22
+ File.join(Dir.home, '.at_coder_friends', '%<user>s_session.yml')
18
23
 
19
- attr_reader :contest, :config, :agent
24
+ attr_reader :ctx, :agent
20
25
 
21
- def initialize(contest, config)
22
- @contest = contest
23
- @config = config
26
+ def initialize(ctx)
27
+ @ctx = ctx
24
28
  @agent = Mechanize.new
25
- @agent.pre_connect_hooks << proc { sleep 0.1 }
26
- # @agent.log = Logger.new(STDERR)
29
+ agent.pre_connect_hooks << proc { sleep 0.1 }
30
+ agent.log = Logger.new(STDERR) if ctx.options[:debug]
31
+ agent.cookie_jar.load(session_store) if File.exist?(session_store)
32
+ end
33
+
34
+ def save_session
35
+ dir = File.dirname(session_store)
36
+ Dir.mkdir(dir) unless Dir.exist?(dir)
37
+ agent.cookie_jar.save_as(session_store)
38
+ end
39
+
40
+ def contest
41
+ @contest ||= contest_name(ctx.path)
42
+ end
43
+
44
+ def config
45
+ ctx.config
27
46
  end
28
47
 
29
48
  def common_url(path)
30
49
  File.join(BASE_URL, path)
31
50
  end
32
51
 
33
- def contest_url(path)
52
+ def contest_url(path = '')
34
53
  File.join(BASE_URL, 'contests', contest, path)
35
54
  end
36
55
 
56
+ def session_store
57
+ @session_store ||= format(SESSION_STORE, user: config['user'])
58
+ end
59
+
37
60
  def constraints_pat
38
61
  config['constraints_pat'] || '^制約$'
39
62
  end
@@ -50,47 +73,71 @@ module AtCoderFriends
50
73
  config['output_smp_pat'] || '^出力例\s*(?<no>[\d0-9]+)$'
51
74
  end
52
75
 
53
- def fetch_all
54
- puts "***** fetch_all #{@contest} *****"
55
- login
56
- fetch_assignments.map do |q, url|
57
- pbm = fetch_problem(q, url)
58
- yield pbm if block_given?
59
- pbm
76
+ def fetch_with_auth(url)
77
+ begin
78
+ page = agent.get(url)
79
+ rescue Mechanize::ResponseCodeError => e
80
+ raise e unless e.response_code == '404'
81
+ raise e if username_link(e.page)
82
+
83
+ page = agent.get(common_url('login') + '?continue=' + CGI.escape(url))
84
+ end
85
+
86
+ if page.uri.path == '/login'
87
+ user, pass = read_auth
88
+ form = page.forms[1]
89
+ form.field_with(name: 'username').value = user
90
+ form.field_with(name: 'password').value = pass
91
+ page = form.submit
60
92
  end
93
+
94
+ page.uri.path == '/login' && (raise AppError, 'Authentication failed.')
95
+ show_username(page)
96
+ page
61
97
  end
62
98
 
63
- def submit(path)
64
- path, _dir, prg, _base, ext, q = split_prg_path(path)
65
- puts "***** submit #{prg} *****"
66
- src = File.read(path, encoding: Encoding::UTF_8)
67
- login
68
- post_src(q, ext, src)
99
+ def read_auth
100
+ user = config['user'].to_s
101
+ if user.empty?
102
+ print('Enter username:')
103
+ user = STDIN.gets.chomp
104
+ end
105
+ pass = config['password'].to_s
106
+ if pass.empty?
107
+ print("Enter password for #{user}:")
108
+ pass = STDIN.noecho(&:gets).chomp
109
+ puts
110
+ end
111
+ [user, pass]
69
112
  end
70
113
 
71
- def code_test(path, infile)
72
- path, _dir, _prg, _base, ext, _q = split_prg_path(path)
73
- src = File.read(path, encoding: Encoding::UTF_8)
74
- data = File.read(infile)
75
- login
76
- code_test_loop(ext, src, data)
114
+ def show_username(page)
115
+ username_old = @username
116
+ link = username_link(page)
117
+ @username = (link ? link.text.strip : '-')
118
+ return if @username == username_old || @username == '-'
119
+
120
+ puts "Logged in as #{@username}"
77
121
  end
78
122
 
79
- def login
80
- return unless config['user'] && !config['user'].empty?
81
- return unless config['password'] && !config['password'].empty?
123
+ def username_link(page)
124
+ link = page.search(XPATH_USERNAME)[0]
125
+ link && link[:href] == '#' && link
126
+ end
82
127
 
83
- page = agent.get(common_url('login'))
84
- form = page.forms[1]
85
- form.field_with(name: 'username').value = config['user']
86
- form.field_with(name: 'password').value = config['password']
87
- form.submit
128
+ def fetch_all
129
+ puts "***** fetch_all #{contest} *****"
130
+ fetch_assignments.map do |q, url|
131
+ pbm = fetch_problem(q, url)
132
+ yield pbm if block_given?
133
+ pbm
134
+ end
88
135
  end
89
136
 
90
137
  def fetch_assignments
91
138
  url = contest_url('tasks')
92
139
  puts "fetch list from #{url} ..."
93
- page = agent.get(url)
140
+ page = fetch_with_auth(url)
94
141
  page
95
142
  .search('//table[1]//td[1]//a')
96
143
  .each_with_object({}) do |a, h|
@@ -100,7 +147,7 @@ module AtCoderFriends
100
147
 
101
148
  def fetch_problem(q, url)
102
149
  puts "fetch problem from #{url} ..."
103
- page = agent.get(url)
150
+ page = fetch_with_auth(url)
104
151
  Problem.new(q) do |pbm|
105
152
  pbm.html = page.body
106
153
  if contest == 'arc001'
@@ -136,8 +183,12 @@ module AtCoderFriends
136
183
  end
137
184
  end
138
185
 
139
- def post_src(q, ext, src)
140
- page = agent.get(contest_url('submit'))
186
+ def submit
187
+ path, _dir, prg, _base, ext, q = split_prg_path(ctx.path)
188
+ puts "***** submit #{prg} *****"
189
+ src = File.read(path, encoding: Encoding::UTF_8)
190
+
191
+ page = fetch_with_auth(contest_url('submit'))
141
192
  form = page.forms[1]
142
193
  form.field_with(name: 'data.TaskScreenName') do |sel|
143
194
  option = sel.options.find { |op| op.text.start_with?(q) }
@@ -148,18 +199,22 @@ module AtCoderFriends
148
199
  form.submit
149
200
  end
150
201
 
151
- def code_test_loop(ext, src, data)
152
- page = agent.get(contest_url('custom_test'))
202
+ def code_test(infile)
203
+ path, _dir, _prg, _base, ext, _q = split_prg_path(ctx.path)
204
+ src = File.read(path, encoding: Encoding::UTF_8)
205
+ data = File.read(infile)
206
+
207
+ page = fetch_with_auth(contest_url('custom_test'))
153
208
  script = page.search('script').text
154
209
  csrf_token = script.scan(/var csrfToken = "(.*)"/)[0][0]
155
- payload = {
210
+
211
+ page = agent.post(
212
+ contest_url('custom_test/submit/json'),
156
213
  'data.LanguageId' => lang_id(ext),
157
214
  'sourceCode' => src,
158
215
  'input' => data,
159
216
  'csrf_token' => csrf_token
160
- }
161
-
162
- page = agent.post(contest_url('custom_test/submit/json'), payload)
217
+ )
163
218
  msg = page.body
164
219
  raise AppError, msg unless msg.empty?
165
220
 
@@ -178,7 +233,7 @@ module AtCoderFriends
178
233
 
179
234
  def lang_list
180
235
  @lang_list ||= begin
181
- page = agent.get(contest_url('custom_test'))
236
+ page = fetch_with_auth(contest_url('custom_test'))
182
237
  form = page.forms[1]
183
238
  sel = form.field_with(name: 'data.LanguageId')
184
239
  sel && sel
@@ -206,9 +261,5 @@ module AtCoderFriends
206
261
  raise AppError, msg
207
262
  )
208
263
  end
209
-
210
- def open_contest
211
- Launchy.open(contest_url(''))
212
- end
213
264
  end
214
265
  end
@@ -7,10 +7,15 @@ module AtCoderFriends
7
7
  class TestRunner
8
8
  include PathUtil
9
9
 
10
- def initialize(path, config)
11
- @contest = contest_name(path)
12
- @path, @dir, @prg, @base, @ext, @q = split_prg_path(path)
13
- @config = config
10
+ attr_reader :ctx
11
+
12
+ def initialize(ctx)
13
+ @ctx = ctx
14
+ @path, @dir, @prg, @base, @ext, @q = split_prg_path(ctx.path)
15
+ end
16
+
17
+ def config
18
+ ctx.config
14
19
  end
15
20
 
16
21
  # rubocop:disable Metrics/MethodLength
@@ -53,8 +58,7 @@ module AtCoderFriends
53
58
  end
54
59
 
55
60
  def remote_test(infile, outfile)
56
- agent = ScrapingAgent.new(@contest, @config)
57
- res = agent.code_test(@path, infile)
61
+ res = ctx.scraping_agent.code_test(infile)
58
62
  unless res && res['Result']
59
63
  File.write(outfile, 'Remote test failed.')
60
64
  return false
@@ -72,7 +76,7 @@ module AtCoderFriends
72
76
 
73
77
  def test_cmd
74
78
  @test_cmd ||= begin
75
- cmds = @config.dig('ext_settings', @ext, 'test_cmd')
79
+ cmds = config.dig('ext_settings', @ext, 'test_cmd')
76
80
  cmd = cmds && (cmds[which_os.to_s] || cmds['default'])
77
81
  return nil unless cmd
78
82
 
@@ -6,11 +6,12 @@ module AtCoderFriends
6
6
  # marks and checks if the source has been verified.
7
7
  class Verifier
8
8
  attr_reader :path, :file, :vdir, :vpath
9
- def initialize(path)
10
- @path = File.expand_path(path)
11
- @file = File.basename(@path)
12
- @vdir = File.join(File.dirname(@path), '.tmp')
13
- @vpath = File.join(@vdir, "#{@file}.verified")
9
+
10
+ def initialize(ctx)
11
+ @path = ctx.path
12
+ @file = File.basename(path)
13
+ @vdir = File.join(File.dirname(path), '.tmp')
14
+ @vpath = File.join(vdir, "#{file}.verified")
14
15
  end
15
16
 
16
17
  def verify
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtCoderFriends
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: at_coder_friends
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nejiko96
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-16 00:00:00.000000000 Z
11
+ date: 2019-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: launchy
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '12.3'
61
+ version: '13.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '12.3'
68
+ version: '13.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -141,6 +141,7 @@ files:
141
141
  - lib/at_coder_friends.rb
142
142
  - lib/at_coder_friends/cli.rb
143
143
  - lib/at_coder_friends/config_loader.rb
144
+ - lib/at_coder_friends/context.rb
144
145
  - lib/at_coder_friends/cxx_generator.rb
145
146
  - lib/at_coder_friends/emitter.rb
146
147
  - lib/at_coder_friends/errors.rb