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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ae4d2f88bf4f0db1e61e84464ea24df21523043c039f5b06fabf1a81d6a5f7d
4
- data.tar.gz: a329f3991c66e86f0f76d859770291ce9d0a4b32391e7554a48183fde335445c
3
+ metadata.gz: 1d8df1d364e772bfe0abc6b281c3ff4678f367d0257d798e952955f531dcbee5
4
+ data.tar.gz: 617e9cf3e96ad6ef9d0407862aa2b1808475a61887d5360463fb66295c69ac5d
5
5
  SHA512:
6
- metadata.gz: a5c8ff8add10ef86b6d165bb8ac9805cfd83bff5e3feca41b1d0237fcfefdcd81b9922b893d5f9f86f1408edb45c14903efafd3178b2fa8b66057d3bc8f2c057
7
- data.tar.gz: 1f3724e621a879d91f43787c8b0a08cfd33a3b97b00069574753074337aab4abc1e18fd72a687017c54f3659fe5e88461335e381230342177c66a373de9907b9
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
@@ -3,6 +3,7 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
+ .byebug_history
6
7
  Gemfile.lock
7
8
  InstalledFiles
8
9
  _yardoc
data/README.md CHANGED
@@ -36,16 +36,20 @@ end
36
36
  ### examples
37
37
 
38
38
  ```ruby
39
- # Ajax処理の終了待ち
40
- click_link 'Ajax link!'
41
- wait_for_ajax
42
- expect(page).to have_content 'created element!'
43
-
44
- # Bootstrap3 のモーダルの出現終了待ち(待たないとモーダル内のノードのクリックに失敗することがある)
45
- wait_for_event('shown.bs.modal') do
46
- click_link 'Open bootstrap3 modal'
47
- end
48
- click_on 'Button in modal'
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
@@ -24,4 +24,5 @@ Gem::Specification.new do |gem|
24
24
  gem.add_development_dependency 'rspec'
25
25
  gem.add_development_dependency 'rubocop'
26
26
  gem.add_development_dependency 'webmock', ['>= 1.19.0']
27
+ gem.add_development_dependency 'byebug'
27
28
  end
@@ -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
- 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
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
- @stream = options[:output] || $stdout
14
- end
15
-
16
- def log(message)
17
- stream.puts(message)
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
- @result = ActiveRecord::Base.transaction(requires_new: true, joinable: false) do
24
- instance_eval(&block)
25
- confirm?("\nAre you sure to commit?")
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
- __echo__ "[START] #{_task.name} #{_args.to_h} (#{Time.current})"
7
+ TaskWithLogger.echo("[START] #{_task.name} #{_args.to_h} (#{Time.current})")
8
8
  yield(_task, _args)
9
- __echo__ "[END] #{_task.name} #{_args.to_h} (#{Time.current})"
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
- def __echo__(str)
15
- Rails.logger.info(str)
16
- puts(str)
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
 
@@ -1,3 +1,3 @@
1
1
  module Dekiru
2
- VERSION = '0.1.8'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Create database maintenance script using Dekiru::DataMigrationOperator
3
+
4
+ Example:
5
+ rails generate maintenance_script UpdateUserName
6
+
7
+ This will create:
8
+ scripts/XXX_update_user_name.rb
@@ -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
@@ -0,0 +1,3 @@
1
+ Dekiru::DataMigrationOperator.execute('<%= @name %>') do
2
+ # write here
3
+ end
@@ -26,7 +26,8 @@ describe Dekiru::DataMigrationOperator do
26
26
  end
27
27
  Dekiru::DummyStream.new
28
28
  end
29
- let(:operator) { Dekiru::DataMigrationOperator.new('dummy', output: dummy_stream) }
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
@@ -9,6 +9,7 @@ require "active_record"
9
9
  require "action_view"
10
10
  require "action_view/helpers"
11
11
  require 'action_mailer'
12
+ require 'byebug'
12
13
 
13
14
  PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
14
15
 
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.1.8
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: 2020-11-05 00:00:00.000000000 Z
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
data/.travis.yml DELETED
@@ -1,12 +0,0 @@
1
- rvm:
2
- - 2.4.6
3
- - 2.5.5
4
- - 2.6.3
5
- - 2.7.1
6
-
7
- # NOTE: https://github.com/travis-ci/travis-ci/issues/8978
8
- before_install:
9
- - gem update --system --no-document
10
-
11
- sudo: false
12
- cache: bundler