dekiru 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +28 -0
- data/.gitignore +1 -0
- data/README.md +42 -10
- data/dekiru.gemspec +1 -0
- data/lib/dekiru/capybara/Legacy_helpers.rb +40 -0
- data/lib/dekiru/capybara/helpers/wait_for_position_stable.rb +53 -0
- data/lib/dekiru/capybara/helpers.rb +3 -34
- data/lib/dekiru/data_migration_operator.rb +57 -8
- data/lib/dekiru/task_with_logger.rb +7 -6
- data/lib/dekiru/validators/existence.rb +1 -1
- data/lib/dekiru/version.rb +1 -1
- data/lib/generators/maintenance_script/USAGE +8 -0
- data/lib/generators/maintenance_script/maintenance_script_generator.rb +15 -0
- data/lib/generators/maintenance_script/templates/maintenance_script.rb.erb +3 -0
- data/spec/dekiru/data_migration_operator_spec.rb +19 -1
- data/spec/spec_helper.rb +1 -0
- metadata +22 -3
- data/.travis.yml +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d8df1d364e772bfe0abc6b281c3ff4678f367d0257d798e952955f531dcbee5
|
4
|
+
data.tar.gz: 617e9cf3e96ad6ef9d0407862aa2b1808475a61887d5360463fb66295c69ac5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47a8042edabdd3a3c5135af8fdf4eca91a7f265c1fbe6a4659490fb2600409a046c156aa533f8988baedc5012ebb796939e93e9880459eec050a1dbd95f59490
|
7
|
+
data.tar.gz: 89bbb4ec613d4ee1412a55c6bfa7d3f60b30ffb6d6fa2f40b3752c1f4b907f39613031a27945cf7547a00a8561d7d605577687389eaaa12296f0f5154ed20971
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [master]
|
6
|
+
pull_request:
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
rspec:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
env:
|
12
|
+
BUNDLE_JOBS: 4
|
13
|
+
BUNDLE_RETRY: 3
|
14
|
+
strategy:
|
15
|
+
fail-fast: false
|
16
|
+
matrix:
|
17
|
+
ruby: ["2.6", "2.7", "3.0"]
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
|
21
|
+
- name: Set up Ruby
|
22
|
+
uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
bundler-cache: true
|
26
|
+
|
27
|
+
- name: Run rspec
|
28
|
+
run: bundle exec rspec
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -36,16 +36,20 @@ end
|
|
36
36
|
### examples
|
37
37
|
|
38
38
|
```ruby
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
39
|
+
# アニメーション終了待ちヘルパー(アニメーション中のクリックは失敗することがある)
|
40
|
+
|
41
|
+
# CSSセレクタで指定した要素の位置が動かなくなるまで待つ
|
42
|
+
wait_for_position_stable(:css, '[data-test-id="confirmation-modal"]')
|
43
|
+
click_button 'OK'
|
44
|
+
|
45
|
+
# チェックボックスの位置が0.5秒間停止し続けるまで待つ
|
46
|
+
# タイムアウトは5秒
|
47
|
+
wait_for_position_stable(:checkbox, 'Red', wait: 5, stable_wait: 0.5)
|
48
|
+
check 'Red'
|
49
|
+
|
50
|
+
# findした要素を指定してアニメーション終了待ち
|
51
|
+
element = find('[data-test-id="confirmation-modal"]')
|
52
|
+
wait_for_element_position_stable(element)
|
49
53
|
```
|
50
54
|
|
51
55
|
## Capybara Matchers
|
@@ -154,6 +158,34 @@ Finished successfully: Demo migration
|
|
154
158
|
Total time: 6.35 sec
|
155
159
|
```
|
156
160
|
|
161
|
+
また`warning_side_effects: true`オプションを付けて実行することで、データ移行作業で発生した副作用が表示されるようになります。
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
Dekiru::DataMigrationOperator.execute('Demo migration', warning_side_effects: true) do
|
165
|
+
# ...
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
```
|
170
|
+
$ bin/rails r scripts/demo.rb
|
171
|
+
Start: Demo migration at 2019-05-24 18:29:57 +0900
|
172
|
+
|
173
|
+
all targets count: 30
|
174
|
+
Time: 00:00:00 |=================>>| 100% Progress
|
175
|
+
updated user count: 30
|
176
|
+
|
177
|
+
Write Queries!!
|
178
|
+
30 call: Update "users" SET ...
|
179
|
+
|
180
|
+
Enqueued Jobs!!
|
181
|
+
10 call: NotifyJob
|
182
|
+
|
183
|
+
Deliverd Mailers!!
|
184
|
+
10 call: UserMailer
|
185
|
+
|
186
|
+
Are you sure to commit? (yes/no) > yes
|
187
|
+
```
|
188
|
+
|
157
189
|
## Refinements
|
158
190
|
|
159
191
|
### Dekiru::CamelizeHash
|
data/dekiru.gemspec
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Dekiru
|
2
|
+
module Capybara
|
3
|
+
module LegacyHelpers
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
def wait_for_event(event)
|
7
|
+
page.execute_script(<<~"EOS")
|
8
|
+
(function(){
|
9
|
+
var eventName = '#{event}';
|
10
|
+
window._dekiruCapybaraWaitEvents = window._dekiruCapybaraWaitEvents || {};
|
11
|
+
window._dekiruCapybaraWaitEvents[eventName] = 1;
|
12
|
+
jQuery(document).one(eventName, function(){window._dekiruCapybaraWaitEvents[eventName] = 0;});
|
13
|
+
})();
|
14
|
+
EOS
|
15
|
+
yield
|
16
|
+
|
17
|
+
script = <<~"EOS"
|
18
|
+
(function(){
|
19
|
+
var eventName = '#{event}';
|
20
|
+
return window._dekiruCapybaraWaitEvents && window._dekiruCapybaraWaitEvents[eventName];
|
21
|
+
})();
|
22
|
+
EOS
|
23
|
+
wait_until do
|
24
|
+
result = page.evaluate_script(script)
|
25
|
+
raise Error, 'wait_for_event: Missing context. probably moved to another page.' if result.nil?
|
26
|
+
result == 0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara
|
31
|
+
def wait_for_ajax
|
32
|
+
wait_until { finished_all_ajax_requests? }
|
33
|
+
end
|
34
|
+
|
35
|
+
def finished_all_ajax_requests?
|
36
|
+
page.evaluate_script('jQuery.active').zero?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Dekiru
|
2
|
+
module Capybara
|
3
|
+
module Helpers
|
4
|
+
module WaitForPositionStable
|
5
|
+
class StableTimer
|
6
|
+
def initialize(wait)
|
7
|
+
@wait = wait
|
8
|
+
@stable = false
|
9
|
+
@start_time = nil
|
10
|
+
@prev_obj = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def stable?(obj)
|
14
|
+
if @prev_obj && @prev_obj == obj
|
15
|
+
if @start_time.nil?
|
16
|
+
@start_time = current
|
17
|
+
elsif current - @start_time > @wait
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
else
|
21
|
+
@start_time = nil
|
22
|
+
end
|
23
|
+
@prev_obj = obj
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def current
|
30
|
+
::Capybara::Helpers.monotonic_time
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def wait_for_element_position_stable(element, wait: ::Capybara.default_max_wait_time, stable_wait: 0.5)
|
35
|
+
stable_timer = StableTimer.new(stable_wait)
|
36
|
+
timer = ::Capybara::Helpers.timer(expire_in: wait)
|
37
|
+
loop do
|
38
|
+
rect = element.rect
|
39
|
+
break if stable_timer.stable?(rect)
|
40
|
+
raise 'Timeout to wait animation finished' if timer.expired?
|
41
|
+
|
42
|
+
sleep 0.1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def wait_for_position_stable(selector, locator, **options)
|
47
|
+
element = find(selector, locator, **options.except(:stable_wait))
|
48
|
+
wait_for_element_position_stable(element, **options.slice(:wait, :stable_wait))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,40 +1,9 @@
|
|
1
|
+
require 'dekiru/capybara/helpers/wait_for_position_stable'
|
2
|
+
|
1
3
|
module Dekiru
|
2
4
|
module Capybara
|
3
5
|
module Helpers
|
4
|
-
|
5
|
-
|
6
|
-
def wait_for_event(event)
|
7
|
-
page.execute_script(<<~"EOS")
|
8
|
-
(function(){
|
9
|
-
var eventName = '#{event}';
|
10
|
-
window._dekiruCapybaraWaitEvents = window._dekiruCapybaraWaitEvents || {};
|
11
|
-
window._dekiruCapybaraWaitEvents[eventName] = 1;
|
12
|
-
jQuery(document).one(eventName, function(){window._dekiruCapybaraWaitEvents[eventName] = 0;});
|
13
|
-
})();
|
14
|
-
EOS
|
15
|
-
yield
|
16
|
-
|
17
|
-
script = <<~"EOS"
|
18
|
-
(function(){
|
19
|
-
var eventName = '#{event}';
|
20
|
-
return window._dekiruCapybaraWaitEvents && window._dekiruCapybaraWaitEvents[eventName];
|
21
|
-
})();
|
22
|
-
EOS
|
23
|
-
wait_until do
|
24
|
-
result = page.evaluate_script(script)
|
25
|
-
raise Error, 'wait_for_event: Missing context. probably moved to another page.' if result.nil?
|
26
|
-
result == 0
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara
|
31
|
-
def wait_for_ajax
|
32
|
-
wait_until { finished_all_ajax_requests? }
|
33
|
-
end
|
34
|
-
|
35
|
-
def finished_all_ajax_requests?
|
36
|
-
page.evaluate_script('jQuery.active').zero?
|
37
|
-
end
|
6
|
+
include WaitForPositionStable
|
38
7
|
|
39
8
|
def wait_until(timeout: ::Capybara.default_max_wait_time, interval: 0.2, **opts, &block)
|
40
9
|
if defined?(Selenium::WebDriver::Wait)
|
@@ -10,19 +10,25 @@ module Dekiru
|
|
10
10
|
|
11
11
|
def initialize(title, options = {})
|
12
12
|
@title = title
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
@options = options
|
14
|
+
@stream = @options[:output] || $stdout
|
15
|
+
@without_transaction = @options[:without_transaction] || false
|
16
|
+
@side_effects = Hash.new do |hash, key|
|
17
|
+
hash[key] = Hash.new(0)
|
18
|
+
end
|
18
19
|
end
|
19
20
|
|
20
21
|
def execute(&block)
|
21
22
|
@started_at = Time.current
|
22
23
|
log "Start: #{title} at #{started_at}\n\n"
|
23
|
-
@
|
24
|
-
|
25
|
-
|
24
|
+
if @without_transaction
|
25
|
+
run(&block)
|
26
|
+
@result = true
|
27
|
+
else
|
28
|
+
@result = ActiveRecord::Base.transaction(requires_new: true, joinable: false) do
|
29
|
+
run(&block)
|
30
|
+
confirm?("\nAre you sure to commit?")
|
31
|
+
end
|
26
32
|
end
|
27
33
|
log "Finished successfully: #{title}" if @result == true
|
28
34
|
rescue => e
|
@@ -56,6 +62,12 @@ module Dekiru
|
|
56
62
|
pb.finish
|
57
63
|
end
|
58
64
|
|
65
|
+
private
|
66
|
+
|
67
|
+
def log(message)
|
68
|
+
stream.puts(message)
|
69
|
+
end
|
70
|
+
|
59
71
|
def confirm?(message = 'Are you sure?')
|
60
72
|
loop do
|
61
73
|
stream.print "#{message} (yes/no) > "
|
@@ -78,5 +90,42 @@ module Dekiru
|
|
78
90
|
log "Canceled: #{title}"
|
79
91
|
raise ActiveRecord::Rollback
|
80
92
|
end
|
93
|
+
|
94
|
+
def handle_notification(*args)
|
95
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
96
|
+
|
97
|
+
increment_side_effects(:enqueued_jobs, event.payload[:job].class.name) if event.payload[:job]
|
98
|
+
increment_side_effects(:deliverd_mailers, event.payload[:mailer]) if event.payload[:mailer]
|
99
|
+
|
100
|
+
if event.payload[:sql] && /\A\s*(insert|update|delete)/i.match?(event.payload[:sql])
|
101
|
+
increment_side_effects(:write_queries, event.payload[:sql])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def increment_side_effects(type, value)
|
106
|
+
@side_effects[type][value] += 1
|
107
|
+
end
|
108
|
+
|
109
|
+
def warning_side_effects(&block)
|
110
|
+
ActiveSupport::Notifications.subscribed(method(:handle_notification), /^(sql|enqueue|deliver)/) do
|
111
|
+
instance_eval(&block)
|
112
|
+
end
|
113
|
+
|
114
|
+
@side_effects.each do |name, items|
|
115
|
+
newline
|
116
|
+
log "#{name.to_s.titlecase}!!"
|
117
|
+
items.sort_by { |v, c| c }.reverse.slice(0, 20).each do |value, count|
|
118
|
+
log "#{count} call: #{value}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def run(&block)
|
124
|
+
if @options[:warning_side_effects]
|
125
|
+
warning_side_effects(&block)
|
126
|
+
else
|
127
|
+
instance_eval(&block)
|
128
|
+
end
|
129
|
+
end
|
81
130
|
end
|
82
131
|
end
|
@@ -4,16 +4,17 @@ module TaskWithLogger
|
|
4
4
|
|
5
5
|
def task(*args, &block)
|
6
6
|
new_block = proc do |_task, _args|
|
7
|
-
|
7
|
+
TaskWithLogger.echo("[START] #{_task.name} #{_args.to_h} (#{Time.current})")
|
8
8
|
yield(_task, _args)
|
9
|
-
|
9
|
+
TaskWithLogger.echo("[END] #{_task.name} #{_args.to_h} (#{Time.current})")
|
10
10
|
end
|
11
11
|
super(*args, &new_block)
|
12
12
|
end
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
15
|
+
def echo(str)
|
16
|
+
Rails.logger.info(str)
|
17
|
+
puts(str)
|
18
18
|
end
|
19
|
+
module_function :echo
|
19
20
|
end
|
@@ -9,7 +9,7 @@ module ActiveModel
|
|
9
9
|
class ExistenceValidator < EachValidator
|
10
10
|
def validate_each(record, attribute, value)
|
11
11
|
unless exists?(record, value)
|
12
|
-
record.errors.add(attribute, :existence, options.except(:in).merge!(value: value))
|
12
|
+
record.errors.add(attribute, :existence, **options.except(:in).merge!(value: value))
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
data/lib/dekiru/version.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class MaintenanceScriptGenerator < Rails::Generators::NamedBase
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
5
|
+
|
6
|
+
def copy_maintenance_script_file
|
7
|
+
template 'maintenance_script.rb.erb', "scripts/#{filename_date}_#{file_name}.rb"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def filename_date
|
13
|
+
Time.current.strftime('%Y%m%d')
|
14
|
+
end
|
15
|
+
end
|
@@ -26,7 +26,8 @@ describe Dekiru::DataMigrationOperator do
|
|
26
26
|
end
|
27
27
|
Dekiru::DummyStream.new
|
28
28
|
end
|
29
|
-
let(:
|
29
|
+
let(:without_transaction) { false }
|
30
|
+
let(:operator) { Dekiru::DataMigrationOperator.new('dummy', output: dummy_stream, without_transaction: without_transaction) }
|
30
31
|
|
31
32
|
describe '#execute' do
|
32
33
|
it 'confirm で yes' do
|
@@ -74,6 +75,23 @@ describe Dekiru::DataMigrationOperator do
|
|
74
75
|
expect(operator.stream.out).not_to include('Canceled:')
|
75
76
|
expect(operator.stream.out).to include('Total time:')
|
76
77
|
end
|
78
|
+
|
79
|
+
context 'without_transaction: true のとき' do
|
80
|
+
let(:without_transaction) { true }
|
81
|
+
|
82
|
+
it 'トランザクションがかからないこと' do
|
83
|
+
expect do
|
84
|
+
operator.execute { log 'processing'; sleep 1.0 }
|
85
|
+
end.not_to raise_error
|
86
|
+
|
87
|
+
expect(operator.result).to eq(true)
|
88
|
+
expect(operator.duration).to be_within(0.1).of(1.0)
|
89
|
+
expect(operator.error).to eq(nil)
|
90
|
+
expect(operator.stream.out).not_to include('Are you sure to commit?')
|
91
|
+
expect(operator.stream.out).to include('Finished successfully:')
|
92
|
+
expect(operator.stream.out).to include('Total time:')
|
93
|
+
end
|
94
|
+
end
|
77
95
|
end
|
78
96
|
|
79
97
|
describe '#find_each_with_progress' do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dekiru
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Akihiro Matsumura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http_accept_language
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: 1.19.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: byebug
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
description: Usefull helper methods for Ruby on Rails
|
112
126
|
email:
|
113
127
|
- matsumura.aki@gmail.com
|
@@ -115,9 +129,9 @@ executables: []
|
|
115
129
|
extensions: []
|
116
130
|
extra_rdoc_files: []
|
117
131
|
files:
|
132
|
+
- ".github/workflows/rspec.yml"
|
118
133
|
- ".gitignore"
|
119
134
|
- ".ruby-version"
|
120
|
-
- ".travis.yml"
|
121
135
|
- Gemfile
|
122
136
|
- LICENSE
|
123
137
|
- README.md
|
@@ -125,7 +139,9 @@ files:
|
|
125
139
|
- dekiru.gemspec
|
126
140
|
- lib/dekiru.rb
|
127
141
|
- lib/dekiru/camelize_hash.rb
|
142
|
+
- lib/dekiru/capybara/Legacy_helpers.rb
|
128
143
|
- lib/dekiru/capybara/helpers.rb
|
144
|
+
- lib/dekiru/capybara/helpers/wait_for_position_stable.rb
|
129
145
|
- lib/dekiru/capybara/matchers.rb
|
130
146
|
- lib/dekiru/controller_additions.rb
|
131
147
|
- lib/dekiru/data_migration_operator.rb
|
@@ -138,6 +154,9 @@ files:
|
|
138
154
|
- lib/dekiru/tasks/smtp_check.rake
|
139
155
|
- lib/dekiru/validators/existence.rb
|
140
156
|
- lib/dekiru/version.rb
|
157
|
+
- lib/generators/maintenance_script/USAGE
|
158
|
+
- lib/generators/maintenance_script/maintenance_script_generator.rb
|
159
|
+
- lib/generators/maintenance_script/templates/maintenance_script.rb.erb
|
141
160
|
- spec/dekiru/camelize_hash_spec.rb
|
142
161
|
- spec/dekiru/data_migration_operator_spec.rb
|
143
162
|
- spec/dekiru/helper_spec.rb
|