dekiru 0.1.3 → 0.1.4

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: 8c0bb6ed019dee9b851e2f68be25d4bb213a96c7d482fe9a1025ff89534809f2
4
- data.tar.gz: 71ab7770a0b3d111f1fb62668bfff4a3bf3e23a548c1217a1d15da40edc74169
3
+ metadata.gz: fa88f3d2cf47df7ed8a071acc0dbb0b86fa20e42c5ad3e72b3f23fb8c6f593b5
4
+ data.tar.gz: ddabf00fc5c3dfcfb8a22f9103f7a8d90ea5e92b6c158bd2b4ea1936d48ebc81
5
5
  SHA512:
6
- metadata.gz: 8930b878569914b971c14d5521091060ecf2079deec914a96e5d9ef9461e067df7b44d2d877988dbb40df15e94114dc05dda361c81985062427d4d7366d688fd
7
- data.tar.gz: e30b62a917913f8abfaaa2aadf9b4d56c584d24e107401f4cd855e089aa0d440a5eca0bacdba7d812d3feb8a911c2b9cae8679607191af938deb1d53bc6a15f2
6
+ metadata.gz: 02ed8451df03137d882758e3adb2fb06f7d8aa3b9e240ec84cd0e59a3bf460945f18919717010c44f58f13abbd6f468d39a33d37a29c9ad809334385ecbd6adc
7
+ data.tar.gz: be1798f99193883c7bb7978f52b9e52e40bcd2e01e1b041159f110c1be6517922b7a6067978a423e3c9315bf9a870bceb465fae1234b2929a318d191d486066c
data/README.md CHANGED
@@ -109,6 +109,51 @@ Rails.application.load_tasks
109
109
  Rake::Task['db:migrate'].enhance(['db:migrate:check_conflict']) if Rails.env.development?
110
110
  ```
111
111
 
112
+ ## Mail Security Hook
113
+
114
+ 以下の設定をすると、宛先を指定しないメールを配信しようとした時に`Dekiru::MailSecurityInterceptor::NoToAdreessError`例外を発生させる。
115
+
116
+ ※ toに空文字や空配列を指定してメールを配信しようとすると、bcc内のアドレスがtoに転記されるといった問題がある。これを未然に防ぐことができる。
117
+
118
+ ```ruby
119
+ # config/initializer/dekiru.rb
120
+ Dekiru.configure do |config|
121
+ config.mail_security_hook = true # default: false
122
+ end
123
+ ```
124
+
125
+ ## Data Migration Operator
126
+
127
+ 実行しながら進捗を表示したり、処理の最後に実行の確認をしたりといった、データ移行作業をするときに必要な処理を以下のような script を作成することで、実現できるようになります。
128
+
129
+ ```ruby
130
+ # scripts/demo.rb
131
+ Dekiru::DataMigrationOperator.execute('Demo migration') do
132
+ targets = User.where("email LIKE '%sonicgarden%'")
133
+
134
+ log "all targets count: #{targets.count}"
135
+ find_each_with_progress(targets) do |user|
136
+ user.update(admin: true)
137
+ end
138
+
139
+ log "updated user count: #{User.where("email LIKE '%sonicgarden%'").where(admin: true).count}"
140
+ end
141
+ ```
142
+
143
+ ```
144
+ $ bin/rails r scripts/demo.rb
145
+ Start: Demo migration at 2019-05-24 18:29:57 +0900
146
+
147
+ all targets count: 30
148
+ Time: 00:00:00 |=================>>| 100% Progress
149
+ updated user count: 30
150
+
151
+ Are you sure to commit? (yes/no) > yes
152
+
153
+ Finished successfully: Demo migration
154
+ Total time: 6.35 sec
155
+ ```
156
+
112
157
  ## Contributing
113
158
 
114
159
  1. Fork it
@@ -17,6 +17,7 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.add_dependency 'http_accept_language', [">= 2.0.0"]
19
19
  gem.add_dependency 'rails'
20
+ gem.add_dependency 'ruby-progressbar'
20
21
  gem.add_development_dependency 'rake', [">= 0"]
21
22
  gem.add_development_dependency 'rspec'
22
23
  gem.add_development_dependency 'rubocop'
@@ -3,9 +3,28 @@ require 'dekiru/railtie' if defined?(::Rails)
3
3
  require 'dekiru/helper'
4
4
  require 'dekiru/controller_additions'
5
5
  require 'dekiru/validators/existence'
6
+ require 'dekiru/data_migration_operator'
7
+ require 'dekiru/mail_security_interceptor'
6
8
 
7
9
  require 'active_support'
8
10
  require 'active_support/all'
9
11
 
10
12
  module Dekiru
13
+ class << self
14
+ def configure
15
+ yield(configuration)
16
+ end
17
+
18
+ def configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+ end
22
+
23
+ class Configuration
24
+ attr_accessor :mail_security_hook
25
+
26
+ def initialize
27
+ @mail_security_hook = false # default
28
+ end
29
+ end
11
30
  end
@@ -0,0 +1,82 @@
1
+ require 'ruby-progressbar'
2
+
3
+ module Dekiru
4
+ class DataMigrationOperator
5
+ attr_reader :title, :stream, :result, :canceled, :started_at, :ended_at, :error
6
+
7
+ def self.execute(title, options = {}, &block)
8
+ self.new(title, options).execute(&block)
9
+ end
10
+
11
+ def initialize(title, options = {})
12
+ @title = title
13
+ @stream = options[:output] || $stdout
14
+ end
15
+
16
+ def log(message)
17
+ stream.puts(message)
18
+ end
19
+
20
+ def execute(&block)
21
+ @started_at = Time.current
22
+ log "Start: #{title} at #{started_at}\n\n"
23
+ @result = ActiveRecord::Base.transaction(requires_new: true) do
24
+ instance_eval(&block)
25
+ confirm?("\nAre you sure to commit?")
26
+ end
27
+ log "Finished successfully: #{title}" if @result == true
28
+ rescue => e
29
+ @error = e
30
+ @result = false
31
+ ensure
32
+ @ended_at = Time.current
33
+ log "Total time: #{self.duration.round(2)} sec"
34
+
35
+ raise error if error
36
+
37
+ return @result
38
+ end
39
+
40
+ def duration
41
+ ((self.ended_at || Time.current) - self.started_at)
42
+ end
43
+
44
+ def find_each_with_progress(target_scope, options = {})
45
+ opt = {
46
+ format: '%a |%b>>%i| %p%% %t',
47
+ }.merge(options).merge(
48
+ total: target_scope.count,
49
+ output: stream
50
+ )
51
+ pb = ::ProgressBar.create(opt)
52
+ target_scope.find_each do |target|
53
+ yield target
54
+ pb.increment
55
+ end
56
+ pb.finish
57
+ end
58
+
59
+ def confirm?(message = 'Are you sure?')
60
+ loop do
61
+ stream.print "#{message} (yes/no) > "
62
+ case STDIN.gets.strip
63
+ when 'yes'
64
+ newline
65
+ return true
66
+ when 'no'
67
+ newline
68
+ cancel!
69
+ end
70
+ end
71
+ end
72
+
73
+ def newline
74
+ log ''
75
+ end
76
+
77
+ def cancel!
78
+ log "Canceled: #{title}"
79
+ raise ActiveRecord::Rollback
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,11 @@
1
+ module Dekiru
2
+ class MailSecurityInterceptor
3
+
4
+ class Dekiru::MailSecurityInterceptor::NoToAdreessError < StandardError ; end
5
+ def delivering_email(mail)
6
+ if mail.to.blank?
7
+ raise Dekiru::MailSecurityInterceptor::NoToAdreessError
8
+ end
9
+ end
10
+ end
11
+ end
@@ -10,6 +10,14 @@ module Dekiru
10
10
  end
11
11
  end
12
12
 
13
+ config.after_initialize do
14
+ if Dekiru.configuration.mail_security_hook
15
+ Rails.logger.info '[dekiru] mail_security_hook enabled'
16
+ interceptor = Dekiru::MailSecurityInterceptor.new
17
+ ActionMailer::Base.register_interceptor(interceptor)
18
+ end
19
+ end
20
+
13
21
  rake_tasks do
14
22
  load 'dekiru/tasks/smtp_check.rake'
15
23
  load 'dekiru/tasks/db.rake'
@@ -1,3 +1,3 @@
1
1
  module Dekiru
2
- VERSION = '0.1.3'
2
+ VERSION = '0.1.4'
3
3
  end
@@ -0,0 +1,113 @@
1
+ require "spec_helper"
2
+
3
+ describe Dekiru::DataMigrationOperator do
4
+ let(:dummy_stream) do
5
+ class Dekiru::DummyStream
6
+ attr_reader :out
7
+
8
+ def initialize
9
+ @out = ''
10
+ end
11
+
12
+ def puts(text)
13
+ @out = out << "#{text}\n"
14
+ end
15
+
16
+ def print(text)
17
+ @out = out << text
18
+ end
19
+
20
+ def tty?
21
+ false
22
+ end
23
+
24
+ def flush
25
+ end
26
+ end
27
+ Dekiru::DummyStream.new
28
+ end
29
+ let(:operator) { Dekiru::DataMigrationOperator.new('dummy', output: dummy_stream) }
30
+
31
+ describe '#execute' do
32
+ it 'confirm で yes' do
33
+ allow(STDIN).to receive(:gets) do
34
+ "yes\n"
35
+ end
36
+
37
+ expect do
38
+ operator.execute { log 'processing'; sleep 1.0 }
39
+ end.not_to raise_error
40
+
41
+ expect(operator.result).to eq(true)
42
+ expect(operator.duration).to be_within(0.1).of(1.0)
43
+ expect(operator.error).to eq(nil)
44
+ expect(operator.stream.out).to include('Are you sure to commit?')
45
+ expect(operator.stream.out).to include('Finished successfully:')
46
+ expect(operator.stream.out).to include('Total time:')
47
+ end
48
+
49
+ it 'confirm で no' do
50
+ allow(STDIN).to receive(:gets) do
51
+ "no\n"
52
+ end
53
+
54
+ expect do
55
+ operator.execute { log 'processing'; sleep 1.0 }
56
+ end.to raise_error(ActiveRecord::Rollback)
57
+
58
+ expect(operator.result).to eq(false)
59
+ expect(operator.duration).to be_within(0.1).of(1.0)
60
+ expect(operator.error.class).to eq(ActiveRecord::Rollback)
61
+ expect(operator.stream.out).to include('Are you sure to commit?')
62
+ expect(operator.stream.out).to include('Canceled:')
63
+ expect(operator.stream.out).to include('Total time:')
64
+ end
65
+
66
+ it '処理中に例外' do
67
+ expect do
68
+ operator.execute { raise ArgumentError }
69
+ end.to raise_error(ArgumentError)
70
+
71
+ expect(operator.result).to eq(false)
72
+ expect(operator.error.class).to eq(ArgumentError)
73
+ expect(operator.stream.out).not_to include('Are you sure to commit?')
74
+ expect(operator.stream.out).not_to include('Canceled:')
75
+ expect(operator.stream.out).to include('Total time:')
76
+ end
77
+ end
78
+
79
+ describe '#find_each_with_progress' do
80
+ it '進捗が表示される' do
81
+ class Dekiru::DummyRecord
82
+ def self.count
83
+ 10
84
+ end
85
+
86
+ def self.find_each
87
+ (0...count).to_a.each do |num|
88
+ yield(num)
89
+ end
90
+ end
91
+ end
92
+
93
+ allow(STDIN).to receive(:gets) do
94
+ "yes\n"
95
+ end
96
+
97
+ sum = 0
98
+ operator.execute do
99
+ find_each_with_progress(Dekiru::DummyRecord, title: 'count up number') do |num|
100
+ sum += num
101
+ end
102
+ end
103
+
104
+ expect(sum).to eq(45)
105
+ expect(operator.result).to eq(true)
106
+ expect(operator.error).to eq(nil)
107
+ expect(operator.stream.out).to include('Are you sure to commit?')
108
+ expect(operator.stream.out).to include('count up number:')
109
+ expect(operator.stream.out).to include('Finished successfully:')
110
+ expect(operator.stream.out).to include('Total time:')
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ class TestMailer < ActionMailer::Base
4
+ default from: 'no-reply@dekiru.test'
5
+
6
+ def test(to)
7
+ mail to: to, subject: 'test', body: 'test'
8
+ end
9
+ end
10
+
11
+ describe Dekiru::MailSecurityInterceptor do
12
+ before do
13
+ interceptor = Dekiru::MailSecurityInterceptor.new
14
+ ActionMailer::Base.register_interceptor(interceptor)
15
+ end
16
+
17
+ context '宛先(to)が空配列の場合' do
18
+ let(:to_address) { [] }
19
+ it '例外が発生すること' do
20
+ expect { TestMailer.test(to_address).deliver_now }.to raise_error(Dekiru::MailSecurityInterceptor::NoToAdreessError)
21
+ end
22
+ end
23
+ context '宛先(to)が空文字の場合' do
24
+ let(:to_address) { '' }
25
+ it '例外が発生すること' do
26
+ expect { TestMailer.test(to_address).deliver_now }.to raise_error(Dekiru::MailSecurityInterceptor::NoToAdreessError)
27
+ end
28
+ end
29
+ context '宛先(to)がnilの場合' do
30
+ let(:to_address) { nil }
31
+ it '例外が発生すること' do
32
+ expect { TestMailer.test(to_address).deliver_now }.to raise_error(Dekiru::MailSecurityInterceptor::NoToAdreessError)
33
+ end
34
+ end
35
+ context '宛先(to)が文字列の場合' do
36
+ let(:to_address) { 'test@dekiru.test' }
37
+ it '例外が発生しないこと' do
38
+ expect { TestMailer.test(to_address).deliver_now }.not_to raise_error
39
+ end
40
+ end
41
+ context '宛先(to)が配列の場合' do
42
+ let(:to_address) { ['test@dekiru.test'] }
43
+ it '例外が発生しないこと' do
44
+ expect { TestMailer.test(to_address).deliver_now }.not_to raise_error
45
+ end
46
+ end
47
+ end
@@ -8,16 +8,14 @@ require 'active_support/core_ext/object'
8
8
  require "active_record"
9
9
  require "action_view"
10
10
  require "action_view/helpers"
11
+ require 'action_mailer'
11
12
 
12
13
  PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
13
14
 
14
15
  $LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
15
16
 
16
17
  require 'dekiru'
17
-
18
- Dir.glob(File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')).each do |file|
19
- require(file)
20
- end
18
+ Dir.glob(File.join(PROJECT_ROOT, 'spec/supports/**/*.rb')).each { |f| require f }
21
19
 
22
20
  RSpec.configure do |config|
23
21
  end
@@ -0,0 +1 @@
1
+ ActionMailer::Base.delivery_method = :test
@@ -0,0 +1,5 @@
1
+ class ActiveRecord::Base
2
+ def self.transaction(*args)
3
+ yield(*args)
4
+ end
5
+ end
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.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akihiro Matsumura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-29 00:00:00.000000000 Z
11
+ date: 2019-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http_accept_language
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-progressbar
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -113,7 +127,9 @@ files:
113
127
  - lib/dekiru/capybara/helpers.rb
114
128
  - lib/dekiru/capybara/matchers.rb
115
129
  - lib/dekiru/controller_additions.rb
130
+ - lib/dekiru/data_migration_operator.rb
116
131
  - lib/dekiru/helper.rb
132
+ - lib/dekiru/mail_security_interceptor.rb
117
133
  - lib/dekiru/railtie.rb
118
134
  - lib/dekiru/smtp_check_mailer.rb
119
135
  - lib/dekiru/task_with_logger.rb
@@ -121,9 +137,13 @@ files:
121
137
  - lib/dekiru/tasks/smtp_check.rake
122
138
  - lib/dekiru/validators/existence.rb
123
139
  - lib/dekiru/version.rb
140
+ - spec/dekiru/data_migration_operator_spec.rb
124
141
  - spec/dekiru/helper_spec.rb
142
+ - spec/dekiru/mail_security_interceptor_spec.rb
125
143
  - spec/dekiru/validators/existence_spec.rb
126
144
  - spec/spec_helper.rb
145
+ - spec/supports/action_mailer.rb
146
+ - spec/supports/mock_active_record.rb
127
147
  homepage: ''
128
148
  licenses: []
129
149
  metadata: {}
@@ -148,6 +168,10 @@ signing_key:
148
168
  specification_version: 4
149
169
  summary: Usefull helper methods for Ruby on Rails
150
170
  test_files:
171
+ - spec/dekiru/data_migration_operator_spec.rb
151
172
  - spec/dekiru/helper_spec.rb
173
+ - spec/dekiru/mail_security_interceptor_spec.rb
152
174
  - spec/dekiru/validators/existence_spec.rb
153
175
  - spec/spec_helper.rb
176
+ - spec/supports/action_mailer.rb
177
+ - spec/supports/mock_active_record.rb