dekiru 0.1.8 → 0.2.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
  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