simple_mailing_list 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 876826ef49ceb87b9cde436ec1e710e201235c6b
4
+ data.tar.gz: 11146f4b0668fd4a82d52ddcbddab47c44d319c0
5
+ SHA512:
6
+ metadata.gz: 681ba3200cde48683fe55e9f08cff82593cbe5bf0e065608e1c5e35e76322c7a3254960a978a94d2df4ba75211cfd17d213a2fdd5b028ef3a763f853d9822df6
7
+ data.tar.gz: f5ba4251379ffa68c4ce0c41db8a2e95649aba52e38d6cc2f3511bbd5b38ddce393811e730211c0fd3637ec284b06cdfed59b1f0aeb4354383e00f9914846563
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /.Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in simple_mailing_list.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,52 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ simple_mailing_list (0.1.0)
5
+ activerecord
6
+ daemons
7
+ liquid
8
+ mail
9
+ sqlite3
10
+ thor
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activemodel (5.1.5)
16
+ activesupport (= 5.1.5)
17
+ activerecord (5.1.5)
18
+ activemodel (= 5.1.5)
19
+ activesupport (= 5.1.5)
20
+ arel (~> 8.0)
21
+ activesupport (5.1.5)
22
+ concurrent-ruby (~> 1.0, >= 1.0.2)
23
+ i18n (~> 0.7)
24
+ minitest (~> 5.1)
25
+ tzinfo (~> 1.1)
26
+ arel (8.0.0)
27
+ concurrent-ruby (1.0.5)
28
+ daemons (1.2.6)
29
+ i18n (0.9.5)
30
+ concurrent-ruby (~> 1.0)
31
+ liquid (4.0.0)
32
+ mail (2.7.0)
33
+ mini_mime (>= 0.1.1)
34
+ mini_mime (1.0.0)
35
+ minitest (5.11.3)
36
+ rake (10.5.0)
37
+ sqlite3 (1.3.13-x86-mingw32)
38
+ thor (0.20.0)
39
+ thread_safe (0.3.6)
40
+ tzinfo (1.2.5)
41
+ thread_safe (~> 0.1)
42
+
43
+ PLATFORMS
44
+ x86-mingw32
45
+
46
+ DEPENDENCIES
47
+ bundler (~> 1.16)
48
+ rake (~> 10.0)
49
+ simple_mailing_list!
50
+
51
+ BUNDLED WITH
52
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 nodai2hITC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.ja.md ADDED
@@ -0,0 +1,78 @@
1
+ # SimpleMailingList
2
+
3
+ シンプルなメーリングリスト/メールマガジンシステムです。
4
+
5
+ 一般的なそうしたシステムと異なり、「最低限必要なもの」が非常に少ないのが特徴です。
6
+
7
+ ## 動作環境
8
+
9
+ ### 最低動作環境
10
+
11
+ - 送受信可能なメールアドレス1つ
12
+ - Ruby が動作するコンピュータ1台
13
+
14
+ ### 推奨動作環境
15
+
16
+ - 送信可能なメールアドレス1つ
17
+ - 受信可能なメールアドレス3つ以上
18
+ 一般的なそうしたシステムと異なり、「最低限必要なもの」が非常に少ないのが特徴です。
19
+
20
+ ## 動作環境
21
+
22
+ ### 最低動作環境
23
+
24
+ - 送受信可能なメールアドレス1つ
25
+ - Ruby が動作するコンピュータ1台
26
+
27
+ ### 推奨動作環境
28
+
29
+ - 送信可能なメールアドレス1つ
30
+ - 受信可能なメールアドレス3つ以上
31
+ - Linux など、プロセスを Daemon 化可能な環境のコンピュータ1台
32
+
33
+ ## インストール
34
+
35
+ Gemfile に以下を追加し、```$ bundle```
36
+
37
+ ```ruby
38
+ gem 'simple_mailing_list'
39
+ gem 'sqlite3'
40
+ # gem 'mysql2'
41
+ # gem 'pg'
42
+ ```
43
+
44
+ または、以下のようにしてインストール:
45
+
46
+ $ gem install simple_mailing_list
47
+
48
+ ## 使い方
49
+
50
+ YAML 形式のコンフィグファイルを用意し、
51
+
52
+ $ simple_mailing_list setup -c <config.yaml>
53
+
54
+ でデータベースやディレクトリを作成する。
55
+
56
+ $ simple_mailing_list <mode> -c <config.yaml>
57
+
58
+ で実行。
59
+
60
+ ```<mode>``` を省略すると、「メールの受信・処理」「古いデータの削除」といった一連の処理を行います。
61
+
62
+ ```-c``` を省略すると、「 config.yaml 」が使用されます。
63
+
64
+ コンフィグファイルの書き方については、[config_example.ja.yaml](https://github.com/nodai2hITC/simple_mailing_list/blob/master/example/config_example.ja.yaml) を参考に。
65
+
66
+ ### Daemon 化
67
+
68
+ $ daemons_simple_mailing_list start -- loop -c <config.yaml>
69
+
70
+ で、「メールの受信・処理」「古いデータの削除」といった一連の処理をずっと繰り返します。
71
+
72
+ ## Contributing
73
+
74
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nodai2hITC/simple_mailing_list.
75
+
76
+ ## License
77
+
78
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # SimpleMailingList
2
+
3
+ [日本語説明(こちらの方が詳細)](https://github.com/nodai2hITC/simple_mailing_list/blob/master/README.ja.md)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'simple_mailing_list'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install simple_mailing_list
20
+
21
+ ## Usage
22
+
23
+ $ simple_mailing_list help
24
+
25
+ ## Contributing
26
+
27
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nodai2hITC/simple_mailing_list.
28
+
29
+ ## License
30
+
31
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "simple_mailing_list"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,127 @@
1
+ log:
2
+ filename: "logs/%v.log" # ログファイル名。Time#strftime の書式を使用可。省略すると標準出力。
3
+ rotation: daily # ログローテーション。Ruby の Logger を参照。
4
+ level: debug # 記録するログのレベル。Ruby の Logger を参照。
5
+
6
+ lockfile: lock/lock.file # ロック用のファイル名。省略するとロックを行わない。
7
+
8
+ maillogs_dir: maillogs # メールログを記録するフォルダ名。
9
+ maillogs_period: -1 # メールログを記録する秒数。マイナスにすると永遠。
10
+
11
+ validity_time: 86400 # 登録/削除確認メールの有効時間。(デフォルト:86400s==一日)
12
+ max_check_times: 5 # 同じメールアドレスから登録/削除要求メールが何度も来た場合、
13
+ # 有効時間内にはこの数以上の確認メールを送らない。
14
+
15
+ sleep_time1: 0.1 # メールを送った後の待機時間。
16
+ sleep_time2: 1.5 # 同じドメインに連続してメールを送る際の待機時間。
17
+
18
+ # 許可された人だけがメール転送をできるようにする設定。
19
+ # 指定されたメールアドレスからのメールか、
20
+ # 件名に「チェックコード」を含むメール(あるいはその両方)のみ転送される。
21
+ # 省略した場合、誰でもメール転送ができる。
22
+ permitted_users:
23
+ - address: yamada@mail.example.com
24
+ - check_code: "forward!"
25
+ # ↑例) yamada@mail.example.com からのメール、または
26
+ # 件名に「forward!」を含むメールのみを転送。
27
+
28
+ # メール配信に登録されている人だけがメール転送をできるようにする。
29
+ registered_user_only: false
30
+
31
+ # 転送メールの差出人を、もとのメールの差出人にする。
32
+ use_address_camouflage: false
33
+
34
+ # HTMLメールを有効にする。
35
+ enable_html_mail: false
36
+
37
+ # 使用するデータベースの設定。
38
+ database:
39
+ adapter: sqlite3
40
+ database: sml_test
41
+
42
+ # メール受信の設定。
43
+ # https://www.school.ctc-g.co.jp/columns/masuidrive/masuidrive07.html
44
+ # https://github.com/mikel/mail 等を参照。
45
+ receive_server:
46
+ protocol: pop3
47
+ options:
48
+ address: mail.example.com
49
+ port: 110
50
+ user_name: simpleml
51
+ password: "simpleml_pass"
52
+ enable_ssl: false
53
+
54
+ # メール送信の設定。同上
55
+ deliver_server:
56
+ protocol: smtp
57
+ address: '"シンプルメーリングリスト" <simpleml@mail.example.com>'
58
+ charset: utf-8
59
+ options:
60
+ address: mail.example.com
61
+ port: 25
62
+ domain: mail.example.com
63
+ authentication: null
64
+ user_name: null
65
+ password: null
66
+ ssl: null
67
+ enable_starttls_auto: false
68
+ openssl_verify_mode: null
69
+
70
+ # 登録要求メールの設定。
71
+ # 指定されたメールアドレスへのメールか、
72
+ # 件名・本文に特定の「チェックコード」を含むメールを
73
+ # 登録要求メールと判断する。
74
+ register:
75
+ - address: register1@mail.example.com
76
+ options: { grade: 1 }
77
+ - address: register2@mail.example.com
78
+ options: { grade: 2 }
79
+ - address: register3@mail.example.com
80
+ options: { grade: 3 }
81
+ - address: register@mail.example.com
82
+ subject: !ruby/regexp /(1|1|一)年/
83
+ options: { grade: 1 }
84
+ # ↑例)
85
+ # 「register(数値)@mail.example.com」にメールを送ると、
86
+ # その数値のgrade(学年)として登録される。
87
+ # または、「register@mail.example.com」に、件名に「1年」等を
88
+ # 含むメールを送ると、1年として登録される。
89
+
90
+ # 登録確認メールの件名・本文
91
+ register_confirm_subject: "登録確認メール:{{checkcode}}"
92
+ register_confirm_body: |
93
+ 登録ありがとうございます。
94
+ 24時間以内に、このメールに返信するか、
95
+ 以下の認証コードを本文中にコピーしたメールを送ってください。
96
+ 認証コード:{{checkcode}}
97
+ # 登録完了メールの件名・本文
98
+ register_success_subject: "登録完了"
99
+ register_success_body: |
100
+ 登録が完了しました。
101
+ 登録を解除するには、delete@mail.example.com にメールを送ってください。
102
+
103
+ # 削除要求メールの設定。register 参照。
104
+ delete:
105
+ - address: delete@mail.example.com
106
+ delete_confirm_subject: "登録解除確認メール:{{checkcode}}"
107
+ delete_confirm_body: |
108
+ 登録を解除するには24時間以内に、このメールに返信するか、
109
+ 以下の認証コードを本文中にコピーしたメールを送ってください。
110
+ 認証コード:{{checkcode}}
111
+ delete_success_subject: "登録解除完了"
112
+ delete_success_body: |
113
+ 登録を解除しました。
114
+
115
+ # 転送メールの設定。
116
+ forward:
117
+ - address: forward@mail.example.com
118
+ - address: forward1@mail.example.com
119
+ options: { grade: 1 }
120
+ - address: forward2@mail.example.com
121
+ options: { grade: 2 }
122
+ - address: forward3@mail.example.com
123
+ options: { grade: 3 }
124
+ # ↑例)
125
+ # forward@mail.example.com に送ると登録者全員に、
126
+ # forward(数値)@mail.example.com に送ると
127
+ # 該当学年の登録者のみにメールを転送する。
@@ -0,0 +1,89 @@
1
+ log:
2
+ filename: "logs/%v.log"
3
+ rotation: daily
4
+ level: debug
5
+
6
+ lockfile: lock/lock.file
7
+
8
+ maillogs_dir: maillogs
9
+ maillogs_period: -1
10
+
11
+ validity_time: 86400
12
+ max_check_times: 5
13
+
14
+ sleep_time1: 0.1
15
+ sleep_time2: 1.5
16
+
17
+ permitted_users:
18
+ - address: yamada@mail.example.com
19
+ - check_code: "forward!"
20
+
21
+ registered_user_only: false
22
+
23
+ use_address_camouflage: false
24
+
25
+ enable_html_mail: false
26
+
27
+ database:
28
+ adapter: sqlite3
29
+ database: sml_test
30
+
31
+ receive_server:
32
+ protocol: pop3
33
+ options:
34
+ address: mail.example.com
35
+ port: 110
36
+ user_name: simpleml
37
+ password: "simpleml_pass"
38
+ enable_ssl: false
39
+
40
+ deliver_server:
41
+ protocol: smtp
42
+ address: '"SimpleMailingList" <simpleml@mail.example.com>'
43
+ charset: utf-8
44
+ options:
45
+ address: mail.example.com
46
+ port: 25
47
+ domain: mail.example.com
48
+ authentication: null
49
+ user_name: null
50
+ password: null
51
+ ssl: null
52
+ enable_starttls_auto: false
53
+ openssl_verify_mode: null
54
+
55
+ register:
56
+ - address: register1@mail.example.com
57
+ options: { grade: 1 }
58
+ - address: register2@mail.example.com
59
+ options: { grade: 2 }
60
+ - address: register3@mail.example.com
61
+ options: { grade: 3 }
62
+ - address: register@mail.example.com
63
+ subject: !ruby/regexp /1st|first/i
64
+ options: { grade: 1 }
65
+
66
+ register_confirm_subject: "Registration Confirmation: {{checkcode}}"
67
+ register_confirm_body: |
68
+ Authentication key ... {{checkcode}}
69
+ register_success_subject: "Registration Complete !"
70
+ register_success_body: |
71
+ Registration Complete !
72
+
73
+ delete:
74
+ - address: delete@mail.example.com
75
+ delete_confirm_subject: "Deletion Confirmation: {{checkcode}}"
76
+ delete_confirm_body: |
77
+ Authentication key ... {{checkcode}}
78
+ delete_success_subject: "Deletion Complete !"
79
+ delete_success_body: |
80
+ Registration Complete !
81
+
82
+ forward:
83
+ - address: forward@mail.example.com
84
+ - address: forward1@mail.example.com
85
+ options: { grade: 1 }
86
+ - address: forward2@mail.example.com
87
+ options: { grade: 2 }
88
+ - address: forward3@mail.example.com
89
+ options: { grade: 3 }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "simple_mailing_list"
4
+ require "daemons"
5
+
6
+ options = {
7
+ ARGV: ARGV,
8
+ dir_mode: :normal,
9
+ dir: Dir.pwd,
10
+ log_output: true
11
+ }
12
+ options[:ARGV] += ["--", "loop"] unless options[:ARGV].index("--")
13
+
14
+ Daemons.run("simple_mailing_list", options)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "simple_mailing_list"
4
+
5
+ SimpleMailingList::CLI.start(ARGV)
@@ -0,0 +1,87 @@
1
+ module SimpleMailingList
2
+ module System
3
+ DEFAULT_CONFIGFILE = "config.yaml"
4
+
5
+ private
6
+
7
+ def load_configfile(configfile)
8
+ return if @path
9
+
10
+ # basic
11
+ config = YAML.load(File.read(configfile, encoding: "utf-8"))
12
+ @path = Dir.getwd
13
+
14
+ # log
15
+ config["log"] ||= {}
16
+ file = config["log"]["filename"] ?
17
+ File.expand_path(Time.now.strftime(config["log"]["filename"]), @path) : STDOUT
18
+ @log = (config["log"]["rotation"].is_a? Integer) ?
19
+ Logger.new(file, config["log"]["rotation"], config["log"]["shift_size"] || 1048576) :
20
+ Logger.new(file, config["log"]["rotation"] || 0)
21
+ if config["log"]["level"]
22
+ @log.level = config["log"]["level"].is_a?(String) ?
23
+ %w[debug info warn error fatal].index(config["log"]["level"].downcase) :
24
+ config["log"]["level"]
25
+ end
26
+
27
+ # others
28
+ @lockfile = config["lockfile"]
29
+ @maillogs_dir = File.expand_path(config["maillogs_dir"] || "maillogs", @path)
30
+ @maillogs_period = config["maillogs_period"] || -1
31
+
32
+ @validity_time = config["validity_time"] || 86400
33
+ @max_check_times = config["max_check_times"] || 5
34
+
35
+ @sleep_time1 = config["sleep_time1"] || 0.1
36
+ @sleep_time2 = config["sleep_time2"] || 1.5
37
+ @permitted_users = config["permitted_users"]
38
+ @registered_user_only = !!config["registered_user_only"]
39
+ @use_address_camouflage = !!config["use_address_camouflage"]
40
+ @enable_html_mail = !!config["enable_html_mail"]
41
+
42
+ @receive_servers = config["receive_servers"] || []
43
+ @deliver_server = config["deliver_server"] ||
44
+ { "protocol" => "sendmail", "charset" => "utf-8", "options" => {} }
45
+
46
+ @register = config["register"] || []
47
+ @register_confirm_subject = config["register_confirm_subject"] || ""
48
+ @register_confirm_body = config["register_confirm_body"] || ""
49
+ @register_success_subject = config["register_success_subject"] || ""
50
+ @register_success_body = config["register_success_body"] || ""
51
+
52
+ @delete = config["delete"] || []
53
+ @delete_confirm_subject = config["delete_confirm_subject"] || ""
54
+ @delete_confirm_body = config["delete_confirm_body"] || ""
55
+ @delete_success_subject = config["delete_success_subject"] || ""
56
+ @delete_success_body = config["delete_success_body"] || ""
57
+
58
+ @forward = config["forward"] || []
59
+ @reply_to_address = config["reply_to_address"]
60
+
61
+ # database
62
+ if config["database_require"]
63
+ require config["database_require"]
64
+ else
65
+ case config["database"]["adapter"]
66
+ when "mysql2"
67
+ require "mysql2"
68
+ when "postgresql"
69
+ require "pg"
70
+ when "sqlite3"
71
+ require "sqlite3"
72
+ else
73
+ require config["database"]["adapter"]
74
+ end
75
+ end
76
+ ActiveRecord::Base.establish_connection(config["database"])
77
+
78
+ # mail server
79
+ @receive_servers = Array(config["receive_server"]) if config["receive_server"]
80
+ deliver_server = @deliver_server
81
+ Mail.defaults do
82
+ delivery_method(deliver_server["protocol"].to_sym,
83
+ deliver_server["options"].symbolize_keys)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,45 @@
1
+ class User < ActiveRecord::Base ; end
2
+ class Confirmation < ActiveRecord::Base ; end
3
+
4
+ module SimpleMailingList
5
+ module System
6
+ private
7
+
8
+ def _delete_failed_users(failed_count = 10, time = 5 * 24 * 60 * 60)
9
+ @log.debug "Delete failed users."
10
+ last_failed_at = Time.now - time
11
+ users = User.where("last_failed_at > ? AND failed_count > ?", last_failed_at, failed_count)
12
+ users.each do |user|
13
+ @log.info "user[#{user.mail_address}] was deleted."
14
+ end
15
+ users.destroy_all()
16
+ User.find_each do |user|
17
+ user.failed_count = 0
18
+ user.save
19
+ end
20
+ end
21
+
22
+ def _delete_old_confirmations()
23
+ @log.debug "Delete old confirmations."
24
+ time = Time.now - @validity_time
25
+ confirmations = Confirmation.where("created_at < ?", time)
26
+ num = confirmations.size
27
+ confirmations.destroy_all()
28
+ @log.info "#{num} confirmation#{num > 1 ? 's' : ''} #{num > 1 ? 'were' : 'was'} deleted." if num > 0
29
+ end
30
+
31
+ def _delete_old_maillogs()
32
+ return unless @maillogs_period >= 0
33
+
34
+ @log.debug "Delete old maillogs."
35
+ time = Time.now - @maillogs_period
36
+ num = 0
37
+ maillogs = Dir.glob(File.join(@maillogs_dir, "*", "*.eml")).select do |maillog|
38
+ File.mtime(maillog) < time
39
+ end
40
+ num = maillogs.size
41
+ maillogs.each { |maillog| File.delete(maillog) }
42
+ @log.info "#{num} maillog#{num > 1 ? 's' : ''} #{num > 1 ? 'were' : 'was'} deleted." if num > 0
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ module SimpleMailingList
2
+ module System
3
+ private
4
+
5
+ def lock()
6
+ if @lockfile
7
+ open(File.expand_path(@lockfile, @path), "w") do |lockfile|
8
+ if lockfile.flock(File::LOCK_EX | File::LOCK_NB)
9
+ @log.debug("Lock successed.")
10
+ yield
11
+ lockfile.flock(File::LOCK_UN)
12
+ @log.debug("Unlocked.")
13
+ else
14
+ @log.debug("Lock failed.")
15
+ end
16
+ end
17
+ else
18
+ yield
19
+ end
20
+ end
21
+ end
22
+ end