txcatcher 0.1.88 → 0.1.89
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 +4 -4
- data/VERSION +1 -1
- data/bin/txcatcher-monitor +41 -30
- data/lib/tasks/db.rake +1 -0
- data/lib/txcatcher/catcher.rb +8 -8
- data/lib/txcatcher/cleaner.rb +5 -5
- data/lib/txcatcher/initializer.rb +2 -1
- data/lib/txcatcher/logger.rb +41 -45
- data/lib/txcatcher/models/transaction.rb +2 -2
- data/lib/txcatcher.rb +1 -1
- data/spec/logger_spec.rb +15 -15
- data/txcatcher.gemspec +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 64f1365dc50e4121cc7d07fe99974bf9ac7f0c6a
|
|
4
|
+
data.tar.gz: 8c273474bb1badd095448d2b2940b55fa0736166
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 82fb6d1ea5f063d7d7c9db04a9603bdd6ec61e8e4f712ec6f32146b353c38c607cec8cbaf76ec0a7153be8033c1a1e36737ff82b55c7e18aeab9ffa15186d397
|
|
7
|
+
data.tar.gz: 3dfb0e9e00e5ec5b8873f9d4394b124b5ed6b2f10935944f2412b5212cf9f0675bfe0d3dc87148221f997c35992593a7b98f4a3a142a12935f44872d348efb99
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.89
|
data/bin/txcatcher-monitor
CHANGED
|
@@ -99,49 +99,60 @@ def latest_output_addr
|
|
|
99
99
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
url = TxCatcher::Config["monitor"]["txcatcher_url"] + "/addr/#{addr}/utxo"
|
|
105
|
-
|
|
102
|
+
def fetch_txcatcher_response(addr, sleep_time: 3)
|
|
106
103
|
response = {}
|
|
107
104
|
3.times do
|
|
108
105
|
sleep sleep_time # let's wait, perhaps txcatcher hasn't caught up
|
|
109
106
|
response = fetch_txcatcher_for_addr(addr)
|
|
110
107
|
break unless response.empty?
|
|
111
108
|
end
|
|
112
|
-
|
|
113
|
-
if response.empty?
|
|
114
|
-
send_alert(
|
|
115
|
-
"TxCatcher response empty",
|
|
116
|
-
"TxCatcher returned an empty {} for #{url}"
|
|
117
|
-
)
|
|
118
|
-
TxCatcher::Logger.report("Checked #{url}, got empty response", :error)
|
|
119
|
-
else
|
|
120
|
-
TxCatcher::Logger.report("Checked #{url}, got #{response.size} utxos", :info)
|
|
121
|
-
end
|
|
122
|
-
|
|
109
|
+
return response
|
|
123
110
|
end
|
|
124
111
|
|
|
125
112
|
def fetch_txcatcher_for_addr(addr)
|
|
126
113
|
response = Faraday.get do |req|
|
|
127
|
-
req.url TxCatcher::Config["monitor"]["txcatcher_url"] + "/addr/#{addr}/utxo"
|
|
114
|
+
req.url TxCatcher::Config["monitor"]["txcatcher_url"] + "/addr/#{addr}1/utxo"
|
|
128
115
|
req.options.timeout = 5
|
|
129
116
|
req.options.open_timeout = 5
|
|
130
117
|
end
|
|
131
118
|
JSON.parse(response.body)
|
|
132
119
|
end
|
|
133
120
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
121
|
+
LOGGER = TxCatcher::Logger.new(log_file: "txcatcher_monitor.log", error_file: "txcatcher_monitor_errors.log", error_log_delimiter: "")
|
|
122
|
+
include TxCatcher::Initializer
|
|
123
|
+
ConfigFile.set!
|
|
124
|
+
read_config_file
|
|
125
|
+
|
|
126
|
+
response = nil
|
|
127
|
+
attempts = 0
|
|
128
|
+
while attempts < 3 && (response.nil? || response.empty?)
|
|
129
|
+
attempts += 1
|
|
130
|
+
begin
|
|
131
|
+
|
|
132
|
+
addr = latest_output_addr
|
|
133
|
+
response = fetch_txcatcher_response(addr)
|
|
134
|
+
url = TxCatcher::Config["monitor"]["txcatcher_url"] + "/addr/#{addr}/utxo"
|
|
135
|
+
|
|
136
|
+
if response.empty?
|
|
137
|
+
LOGGER.report("Checked #{url}, got empty response, attempt #{attempts}", :error)
|
|
138
|
+
if attempts >= 3
|
|
139
|
+
send_alert("TxCatcher response empty", "TxCatcher returned an empty {} for #{url}")
|
|
140
|
+
end
|
|
141
|
+
else
|
|
142
|
+
LOGGER.report("Checked #{url}, got #{response.size} utxo for addr #{addr}, attempt #{attempts}", :info)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
rescue StandardError => e
|
|
146
|
+
LOGGER.report(e, :error, data: { attempt: attempts })
|
|
147
|
+
if attempts >= 3
|
|
148
|
+
send_alert(
|
|
149
|
+
"TxCatcher monitor error",
|
|
150
|
+
"While checking #{TxCatcher::Config["monitor"]["txcatcher_url"]} response, got the following error\n\n" +
|
|
151
|
+
"#{e.to_s}\n\n#{e.backtrace.join("\n")}"
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
end # begin/rescue
|
|
155
|
+
end #while
|
|
156
|
+
|
|
157
|
+
# Only report if we actually fetched something. Otherwise another error was probably reported already.
|
|
158
|
+
|
data/lib/tasks/db.rake
CHANGED
data/lib/txcatcher/catcher.rb
CHANGED
|
@@ -9,7 +9,7 @@ module TxCatcher
|
|
|
9
9
|
@sockets = {}
|
|
10
10
|
|
|
11
11
|
{'rawtx' => "#{socket}#{name}.rawtx", 'hashblock' => "#{socket}#{name}.hashblock"}.each do |channel, address|
|
|
12
|
-
|
|
12
|
+
LOGGER.report "Start listening on #{name} #{channel}... (#{address})"
|
|
13
13
|
listen_to_zeromq_message(channel: channel, address: address)
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -32,16 +32,16 @@ module TxCatcher
|
|
|
32
32
|
# before we start listening to any messages from ZeroMQ.
|
|
33
33
|
queue_thread = Thread.new do
|
|
34
34
|
loop do
|
|
35
|
-
|
|
35
|
+
LOGGER.report "in #{channel} queue: #{@queue[channel].size}" if Config["logger"]["log_queue_info"]
|
|
36
36
|
if @queue[channel].empty?
|
|
37
37
|
sleep 1
|
|
38
38
|
else
|
|
39
39
|
begin
|
|
40
40
|
@queue[channel].pop.call
|
|
41
41
|
rescue Sequel::ValidationFailed => e
|
|
42
|
-
|
|
42
|
+
LOGGER.report e, :warn, timestamp: true
|
|
43
43
|
rescue Exception => e
|
|
44
|
-
|
|
44
|
+
LOGGER.report e, :error, timestamp: true
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -75,13 +75,13 @@ module TxCatcher
|
|
|
75
75
|
end # listen_to_zeromq_message
|
|
76
76
|
|
|
77
77
|
def handle_rawtx(txhex)
|
|
78
|
-
|
|
78
|
+
LOGGER.report "received tx hex: #{txhex[0..50]}..."
|
|
79
79
|
@queue["rawtx"] << ( Proc.new {
|
|
80
80
|
tx = TxCatcher::Transaction.new(hex: txhex)
|
|
81
81
|
tx.save
|
|
82
|
-
|
|
82
|
+
LOGGER.report "tx #{tx.txid} saved (id: #{tx.id}), deposits (outputs):"
|
|
83
83
|
tx.deposits.each do |d|
|
|
84
|
-
|
|
84
|
+
LOGGER.report " id: #{d.id}, addr: #{d.address.address}, amount: #{CryptoUnit.new(Config["currency"], d.amount, from_unit: :primary).to_standart}"
|
|
85
85
|
end
|
|
86
86
|
})
|
|
87
87
|
end
|
|
@@ -90,7 +90,7 @@ module TxCatcher
|
|
|
90
90
|
block_hash = TxCatcher.rpc_node.getblock(block_hex)
|
|
91
91
|
transactions = block_hash["tx"]
|
|
92
92
|
height = TxCatcher.current_block_height = block_hash["height"].to_i
|
|
93
|
-
|
|
93
|
+
LOGGER.report "*** Block #{height} mined, transactions received:\n #{transactions.join(" \n")}"
|
|
94
94
|
@queue["hashblock"] << ( Proc.new {
|
|
95
95
|
Transaction.where(txid: transactions).update(block_height: height)
|
|
96
96
|
})
|
data/lib/txcatcher/cleaner.rb
CHANGED
|
@@ -19,14 +19,14 @@ module TxCatcher
|
|
|
19
19
|
else
|
|
20
20
|
TxCatcher::Transaction.count
|
|
21
21
|
end
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
LOGGER.report "Cleaning transactions in DB..."
|
|
23
|
+
LOGGER.report "#{db_tx_count} now, needs to be below #{Config.max_db_transactions_stored}\n"
|
|
24
24
|
if db_tx_count > Config.max_db_transactions_stored
|
|
25
25
|
number_to_delete = (db_tx_count - Config.max_db_transactions_stored) + (Config.max_db_transactions_stored*0.1).round
|
|
26
26
|
clean_transactions(number_to_delete)
|
|
27
|
-
|
|
27
|
+
LOGGER.report "DB cleaned: #{@@cleaning_counter.to_s}"
|
|
28
28
|
else
|
|
29
|
-
|
|
29
|
+
LOGGER.report "Nothing to be cleaned from DB, size is below threshold."
|
|
30
30
|
end
|
|
31
31
|
if @stop || run_once
|
|
32
32
|
@@stopped = true
|
|
@@ -58,7 +58,7 @@ module TxCatcher
|
|
|
58
58
|
t.times do
|
|
59
59
|
limit = n <= 100 ? n : 100
|
|
60
60
|
transactions.limit(limit).each do |t|
|
|
61
|
-
|
|
61
|
+
LOGGER.report "- Removing tx #{t.txid}"
|
|
62
62
|
clean_deposits(t)
|
|
63
63
|
TxCatcher.db_connection[:transactions].filter(id: t.id).delete
|
|
64
64
|
@@cleaning_counter[:transactions] += 1
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module TxCatcher
|
|
2
2
|
|
|
3
3
|
VERSION = File.read(File.expand_path('../../', File.dirname(__FILE__)) + '/VERSION')
|
|
4
|
+
LOGGER = TxCatcher::Logger.new
|
|
4
5
|
|
|
5
6
|
class << self
|
|
6
7
|
attr_accessor :db_connection, :rpc_node, :current_block_height, :config_dir
|
|
@@ -156,7 +157,7 @@ module TxCatcher
|
|
|
156
157
|
Raven.configure do |config|
|
|
157
158
|
config.dsn = Config["logger"]["sentry_dsn"]
|
|
158
159
|
config.current_environment = Config["environment"]
|
|
159
|
-
end if Config["logger"] && Config["logger"]["sentry_dsn"]
|
|
160
|
+
end if Config["logger"] && Config["logger"]["sentry_dsn"] && Config["logger"]["sentry_dsn"] != "test"
|
|
160
161
|
end
|
|
161
162
|
|
|
162
163
|
end
|
data/lib/txcatcher/logger.rb
CHANGED
|
@@ -10,65 +10,61 @@ module TxCatcher
|
|
|
10
10
|
unknown: 5
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
def initialize(log_file: "txcatcher.log", error_file: "error.log", error_log_delimiter: "\n\n")
|
|
14
|
+
@log_file_name = log_file
|
|
15
|
+
@error_log_file_name = error_file
|
|
16
|
+
@error_log_delimiter = error_log_delimiter
|
|
17
|
+
end
|
|
17
18
|
|
|
18
|
-
def set_log_file_names!(log: nil, error: nil)
|
|
19
|
-
@@log_file_name = log if log
|
|
20
|
-
@@error_log_file_name = error if error
|
|
21
|
-
end
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
20
|
+
def report(message, log_level=:info, data: nil, timestamp: false)
|
|
21
|
+
[:logfile, :stdout, :sentry].each do |out|
|
|
22
|
+
if LOG_LEVELS[log_level] >= LOG_LEVELS[Config["logger"]["#{out}_level"].to_sym]
|
|
23
|
+
send("report_to_#{out}", message, log_level, data: data, timestamp: timestamp)
|
|
28
24
|
end
|
|
29
25
|
end
|
|
26
|
+
end
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
private
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
def report_to_stdout(message, log_level, data: nil, timestamp: timestamp)
|
|
31
|
+
$stdout.print prepare_message(message, timestamp: timestamp) + "\n"
|
|
32
|
+
$stdout.print " additional data: #{data.to_s}\n" if data
|
|
33
|
+
$stdout.print(@error_log_delimiter) if LOG_LEVELS[log_level] >= LOG_LEVELS[:error]
|
|
34
|
+
end
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
def report_to_logfile(message, log_level, data: nil, timestamp: true) # always gonna be forcing timestamp to be true here
|
|
37
|
+
fn = LOG_LEVELS[log_level] >= LOG_LEVELS[:error] ? @error_log_file_name : @log_file_name
|
|
38
|
+
fn = TxCatcher::Config.config_dir + "/#{fn}"
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
end
|
|
40
|
+
File.open(fn, "a") do |f|
|
|
41
|
+
f.print "#{prepare_message(message, timestamp: true)}\n"
|
|
42
|
+
f.print " additional data: #{data.to_s}\n" if data
|
|
43
|
+
f.print(@error_log_delimiter) if LOG_LEVELS[log_level] >= LOG_LEVELS[:error]
|
|
48
44
|
end
|
|
45
|
+
end
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
def report_to_sentry(e, log_level, data: nil, timestamp: timestamp)
|
|
48
|
+
return unless TxCatcher::Config["logger"]["sentry_dsn"]
|
|
49
|
+
data ||= {}
|
|
50
|
+
data.merge!(environment: Config["environment"], host: Config["host"], currency: Config["currency"])
|
|
51
|
+
Raven.tags_context data
|
|
52
|
+
e.kind_of?(Exception) ? Raven.capture_exception(e) : Raven.capture_message(e)
|
|
53
|
+
end
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
result
|
|
64
|
-
else
|
|
65
|
-
e
|
|
66
|
-
end
|
|
67
|
-
result = "[#{Time.now}] #{result}" if timestamp
|
|
55
|
+
def prepare_message(e, timestamp: false)
|
|
56
|
+
result = if e.kind_of?(Exception)
|
|
57
|
+
result = e.to_s + "\n"
|
|
58
|
+
result += e.message + "\n\n" if e.message != e.to_s
|
|
59
|
+
result += (e.backtrace&.join("\n") || "[no backtrace]")
|
|
68
60
|
result
|
|
61
|
+
else
|
|
62
|
+
e
|
|
69
63
|
end
|
|
64
|
+
result = "[#{Time.now}] #{result}" if timestamp
|
|
65
|
+
result
|
|
66
|
+
end
|
|
70
67
|
|
|
71
|
-
end
|
|
72
68
|
|
|
73
69
|
end
|
|
74
70
|
|
|
@@ -62,9 +62,9 @@ module TxCatcher
|
|
|
62
62
|
block_hash = TxCatcher.rpc_node.getblockhash(TxCatcher.current_block_height - i)
|
|
63
63
|
block = TxCatcher.rpc_node.getblock(block_hash)
|
|
64
64
|
i += 1
|
|
65
|
-
|
|
65
|
+
LOGGER.report "--- checking block #{TxCatcher.current_block_height - i} for tx #{self.txid}"
|
|
66
66
|
if block["tx"] && block["tx"].include?(self.txid)
|
|
67
|
-
|
|
67
|
+
LOGGER.report "tx #{self.txid} block height updated to #{block["height"]}"
|
|
68
68
|
self.update(block_height: block["height"])
|
|
69
69
|
return block["height"].to_i
|
|
70
70
|
end
|
data/lib/txcatcher.rb
CHANGED
|
@@ -9,12 +9,12 @@ Sequel.extension :migration
|
|
|
9
9
|
|
|
10
10
|
require_relative 'txcatcher/utils/crypto_unit'
|
|
11
11
|
require_relative 'txcatcher/utils/hash_string_to_sym_keys'
|
|
12
|
+
require_relative 'txcatcher/logger'
|
|
12
13
|
require_relative 'txcatcher/bitcoin_rpc'
|
|
13
14
|
require_relative 'txcatcher/config'
|
|
14
15
|
require_relative 'txcatcher/initializer'
|
|
15
16
|
require_relative 'txcatcher/catcher'
|
|
16
17
|
require_relative 'txcatcher/cleaner'
|
|
17
|
-
require_relative 'txcatcher/logger'
|
|
18
18
|
|
|
19
19
|
include TxCatcher::Initializer
|
|
20
20
|
prepare
|
data/spec/logger_spec.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
2
|
require_relative 'spec_helper'
|
|
3
3
|
|
|
4
|
-
RSpec.describe TxCatcher::
|
|
4
|
+
RSpec.describe TxCatcher::LOGGER do
|
|
5
5
|
|
|
6
6
|
describe "logging to logfiles" do
|
|
7
7
|
|
|
8
8
|
it "writes info messages to txcatcher.log" do
|
|
9
|
-
TxCatcher::
|
|
9
|
+
TxCatcher::LOGGER.report "example info message", :info
|
|
10
10
|
expect(File.read(LOGFILE)).to include("example info message")
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
it "writes errors into a separate error.log file" do
|
|
14
|
-
TxCatcher::
|
|
14
|
+
TxCatcher::LOGGER.report "example error message", :error
|
|
15
15
|
expect(File.read(ERRFILE)).to include("example error message")
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -21,44 +21,44 @@ RSpec.describe TxCatcher::Logger do
|
|
|
21
21
|
|
|
22
22
|
it "logs messages to Sentry" do
|
|
23
23
|
expect(Raven).to receive(:capture_message).once
|
|
24
|
-
TxCatcher::
|
|
25
|
-
TxCatcher::
|
|
24
|
+
TxCatcher::LOGGER.report "example info message", :info
|
|
25
|
+
TxCatcher::LOGGER.report "example error message", :error
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
it "logs errors to Sentry" do
|
|
29
29
|
expect(Raven).to receive(:capture_exception).once
|
|
30
|
-
TxCatcher::
|
|
30
|
+
TxCatcher::LOGGER.report StandardError.new, :error
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
it "logs to stdout" do
|
|
36
36
|
expect($stdout).to receive(:print).exactly(3).times
|
|
37
|
-
TxCatcher::
|
|
38
|
-
TxCatcher::
|
|
37
|
+
TxCatcher::LOGGER.report "example info message", :info
|
|
38
|
+
TxCatcher::LOGGER.report "example error message", :error
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
it "doesn't log an message if its loglevel is below current threshold" do
|
|
42
42
|
TxCatcher::Config["logger"]["stdout_level"] = "fatal"
|
|
43
43
|
expect($stdout).to receive(:print).exactly(2).times
|
|
44
|
-
TxCatcher::
|
|
45
|
-
TxCatcher::
|
|
46
|
-
TxCatcher::
|
|
47
|
-
TxCatcher::
|
|
44
|
+
TxCatcher::LOGGER.report "example info message", :info
|
|
45
|
+
TxCatcher::LOGGER.report "example warn message", :warn
|
|
46
|
+
TxCatcher::LOGGER.report "example error message", :error
|
|
47
|
+
TxCatcher::LOGGER.report "example fatal message", :fatal
|
|
48
48
|
TxCatcher::Config["logger"]["stdout_level"] = "info"
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
it "converts Exception into a text for logging" do
|
|
52
52
|
expect($stdout).to receive(:print).with("StandardError\n[no backtrace]\n")
|
|
53
53
|
expect($stdout).to receive(:print).with("\n\n")
|
|
54
|
-
TxCatcher::
|
|
54
|
+
TxCatcher::LOGGER.report StandardError.new, :error
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
it "prints additional data passed along" do
|
|
58
58
|
expect($stdout).to receive(:print).with("example error message\n")
|
|
59
|
-
expect($stdout).to receive(:print).with(' additional data: {:hello=>"world"}')
|
|
59
|
+
expect($stdout).to receive(:print).with(' additional data: {:hello=>"world"}' + "\n")
|
|
60
60
|
expect($stdout).to receive(:print).with("\n\n")
|
|
61
|
-
TxCatcher::
|
|
61
|
+
TxCatcher::LOGGER.report "example error message", :error, data: { hello: "world" }
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
end
|
data/txcatcher.gemspec
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
|
5
|
-
# stub: txcatcher 0.1.
|
|
5
|
+
# stub: txcatcher 0.1.89 ruby lib
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |s|
|
|
8
8
|
s.name = "txcatcher"
|
|
9
|
-
s.version = "0.1.
|
|
9
|
+
s.version = "0.1.89"
|
|
10
10
|
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
12
12
|
s.require_paths = ["lib"]
|