filecluster 0.1.6 → 0.1.7
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.
- data/bin/fc-daemon +6 -6
- data/lib/daemon.rb +15 -7
- data/lib/daemon/global_daemon_thread.rb +5 -5
- data/lib/daemon/task_thread.rb +16 -6
- data/lib/fc/base.rb +4 -4
- data/lib/fc/db.rb +13 -0
- data/lib/fc/item.rb +3 -3
- data/lib/fc/version.rb +1 -1
- data/lib/manage/policies.rb +1 -1
- data/lib/manage/show.rb +6 -6
- data/lib/manage/storages.rb +1 -1
- data/test/base_test.rb +1 -1
- data/test/daemon_test.rb +4 -4
- data/test/db_test.rb +4 -4
- data/test/functional_test.rb +4 -4
- data/test/helper.rb +3 -3
- data/test/item_test.rb +3 -3
- data/test/policy_test.rb +2 -2
- metadata +4 -4
data/bin/fc-daemon
CHANGED
@@ -8,12 +8,12 @@ require 'filecluster'
|
|
8
8
|
require 'utils'
|
9
9
|
require 'daemon'
|
10
10
|
|
11
|
-
$storages
|
12
|
-
$tasks
|
13
|
-
$
|
14
|
-
$tasks_threads
|
15
|
-
$check_threads
|
16
|
-
$exit_signal
|
11
|
+
$storages = [] # storages on current host
|
12
|
+
$tasks = {} # tasks by storage name
|
13
|
+
$curr_tasks = [] # current tasks
|
14
|
+
$tasks_threads = {} # threads by storage name
|
15
|
+
$check_threads = {} # threads by storage name
|
16
|
+
$exit_signal = false
|
17
17
|
$global_daemon_thread = nil
|
18
18
|
|
19
19
|
default_db_config = File.expand_path(File.dirname(__FILE__))+'/db.yml'
|
data/lib/daemon.rb
CHANGED
@@ -17,12 +17,12 @@ end
|
|
17
17
|
|
18
18
|
def run_global_daemon(timeout)
|
19
19
|
$log.debug('Run global daemon check')
|
20
|
-
r = FC::DB.
|
20
|
+
r = FC::DB.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
|
21
21
|
if !r || r['curr_time'].to_i - r['time'].to_i > timeout
|
22
22
|
$log.debug('Set global daemon host to current')
|
23
|
-
FC::DB.
|
23
|
+
FC::DB.query("REPLACE #{FC::DB.prefix}vars SET val='#{FC::Storage.curr_host}', name='global_daemon_host'")
|
24
24
|
sleep 1
|
25
|
-
r = FC::DB.
|
25
|
+
r = FC::DB.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
|
26
26
|
end
|
27
27
|
if r['val'] == FC::Storage.curr_host
|
28
28
|
if !$global_daemon_thread || !$global_daemon_thread.alive?
|
@@ -66,9 +66,10 @@ def update_tasks
|
|
66
66
|
cond = "storage_name in (#{storages_names}) AND status='#{type.to_s}'"
|
67
67
|
ids = $tasks.map{|storage_name, storage_tasks| storage_tasks.select{|task| task[:action] == type}}.
|
68
68
|
flatten.map{|task| task[:item_storage].id}
|
69
|
-
$
|
69
|
+
ids += $curr_tasks.select{|task| task[:action] == type}
|
70
70
|
|
71
71
|
cond << "AND id not in (#{ids.join(',')})" if (ids.length > 0)
|
72
|
+
cond << "LIMIT 1000"
|
72
73
|
FC::ItemStorage.where(cond).each do |item_storage|
|
73
74
|
$tasks[item_storage.storage_name] = [] unless $tasks[item_storage.storage_name]
|
74
75
|
$tasks[item_storage.storage_name] << {:action => type, :item_storage => item_storage}
|
@@ -83,10 +84,17 @@ end
|
|
83
84
|
def run_tasks
|
84
85
|
$log.debug('Run tasks')
|
85
86
|
$storages.each do |storage|
|
86
|
-
|
87
|
-
|
87
|
+
$tasks_threads[storage.name] = [] unless $tasks_threads[storage.name]
|
88
|
+
$tasks_threads[storage.name].delete_if {|thread| !thread.alive?}
|
89
|
+
tasks_count = $tasks[storage.name] ? $tasks[storage.name].size : 0
|
90
|
+
threads_count = $tasks_threads[storage.name].count
|
91
|
+
# 10 tasks per thread, maximum 3 tasks
|
92
|
+
run_threads_count = (tasks_count/10.0).ceil - threads_count
|
93
|
+
run_threads_count = 3 if run_threads_count > 3
|
94
|
+
$log.debug("tasks_count: #{tasks_count}, threads_count: #{threads_count}, run_threads_count: #{run_threads_count}")
|
95
|
+
run_threads_count.times do
|
88
96
|
$log.debug("spawn TaskThread for #{storage.name}")
|
89
|
-
$tasks_threads[storage.name]
|
97
|
+
$tasks_threads[storage.name] << TaskThread.new(storage.name)
|
90
98
|
end
|
91
99
|
end
|
92
100
|
end
|
@@ -6,9 +6,9 @@ class GlobalDaemonThread < BaseThread
|
|
6
6
|
sleep timeout.to_f/2
|
7
7
|
exit if $exit_signal
|
8
8
|
|
9
|
-
r = FC::DB.
|
9
|
+
r = FC::DB.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
|
10
10
|
if r['val'] == FC::Storage.curr_host
|
11
|
-
FC::DB.
|
11
|
+
FC::DB.query("UPDATE #{FC::DB.prefix}vars SET val='#{FC::Storage.curr_host}' WHERE name='global_daemon_host'")
|
12
12
|
else
|
13
13
|
$log.info("Exit from GlobalDaemonThread: global daemon already running on #{r['val']}")
|
14
14
|
FC::DB.close
|
@@ -40,7 +40,7 @@ class GlobalDaemonThread < BaseThread
|
|
40
40
|
sql = "SELECT i.id as item_id, i.size, i.copies as item_copies, GROUP_CONCAT(ist.storage_name ORDER BY ist.id) as storages, p.id as policy_id, p.copies as policy_copies "+
|
41
41
|
"FROM #{FC::Item.table_name} as i, #{FC::Policy.table_name} as p, #{FC::ItemStorage.table_name} as ist WHERE "+
|
42
42
|
"i.policy_id = p.id AND ist.item_id = i.id AND i.copies > 0 AND i.copies < p.copies AND i.status = 'ready' AND ist.status <> 'delete' GROUP BY i.id LIMIT 1000"
|
43
|
-
r = FC::DB.
|
43
|
+
r = FC::DB.query(sql)
|
44
44
|
r.each do |row|
|
45
45
|
$log.info("GlobalDaemonThread: new item_storage for item #{row['item_id']}")
|
46
46
|
item_storages = row['storages'].split(',')
|
@@ -61,11 +61,11 @@ class GlobalDaemonThread < BaseThread
|
|
61
61
|
def delete_deleted_items
|
62
62
|
$log.debug("GlobalDaemonThread: delete_deleted_items")
|
63
63
|
|
64
|
-
r = FC::DB.
|
64
|
+
r = FC::DB.query("SELECT i.id FROM #{FC::Item.table_name} as i LEFT JOIN #{FC::ItemStorage.table_name} as ist ON i.id=ist.item_id WHERE i.status = 'delete' AND ist.id IS NULL")
|
65
65
|
ids = r.map{|row| row['id']}
|
66
66
|
if ids.count > 0
|
67
67
|
ids = ids.join(',')
|
68
|
-
FC::DB.
|
68
|
+
FC::DB.query("DELETE FROM #{FC::Item.table_name} WHERE id in (#{ids})")
|
69
69
|
$log.info("GlobalDaemonThread: delete items #{ids}")
|
70
70
|
end
|
71
71
|
end
|
data/lib/daemon/task_thread.rb
CHANGED
@@ -1,38 +1,48 @@
|
|
1
1
|
class TaskThread < BaseThread
|
2
2
|
def go(storage_name)
|
3
|
+
return unless $tasks[storage_name]
|
3
4
|
while task = $tasks[storage_name].shift do
|
4
|
-
$
|
5
|
+
$curr_tasks << task
|
5
6
|
$log.debug("TaskThread(#{storage_name}): run task type=#{task[:action]}, item_storage=#{task[:item_storage].id}")
|
6
7
|
if task[:action] == :delete
|
7
|
-
make_delete(task
|
8
|
+
make_delete(task)
|
8
9
|
elsif task[:action] == :copy
|
9
|
-
make_copy(task
|
10
|
+
make_copy(task)
|
10
11
|
else
|
11
12
|
error "Unknown task action: #{task[:action]}"
|
12
13
|
end
|
13
|
-
$
|
14
|
+
$curr_tasks.delete(task)
|
14
15
|
$log.debug("TaskThread(#{storage_name}): Finish task type=#{task[:action]}, item_storage=#{task[:item_storage].id}")
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
|
-
def make_delete(
|
19
|
+
def make_delete(task)
|
20
|
+
item_storage = task[:item_storage]
|
21
|
+
# TODO: не лазить в базу за item
|
19
22
|
storage = $storages.detect{|s| s.name == item_storage.storage_name}
|
20
23
|
item = FC::Item.find(item_storage.item_id)
|
21
24
|
storage.delete_file(item.name)
|
22
25
|
item_storage.delete
|
23
26
|
rescue Exception => e
|
24
27
|
error "Delete item_storage error: #{e.message}; #{e.backtrace.join(', ')}", :item_id => item_storage.item_id, :item_storage_id => item_storage.id
|
28
|
+
$curr_tasks.delete(task)
|
25
29
|
end
|
26
30
|
|
27
|
-
def make_copy(
|
31
|
+
def make_copy(task)
|
32
|
+
item_storage = task[:item_storage]
|
28
33
|
# TODO: не лазить в базу за item, item_storages - перенести на стадию подготовки task-а
|
29
34
|
storage = $storages.detect{|s| s.name == item_storage.storage_name}
|
30
35
|
item = FC::Item.find(item_storage.item_id)
|
31
36
|
src_item_storage = FC::ItemStorage.where("item_id = ? AND status = 'ready'", item.id).sample
|
37
|
+
unless src_item_storage
|
38
|
+
$log.info("Item ##{item.id} #{item.name} has no ready item_storage")
|
39
|
+
return nil
|
40
|
+
end
|
32
41
|
src_storage = $all_storages.detect{|s| s.name == src_item_storage.storage_name}
|
33
42
|
$log.debug("Copy from #{src_storage.name} to #{storage.name} #{storage.path}#{item.name}")
|
34
43
|
item.copy_item_storage(src_storage, storage, item_storage)
|
35
44
|
rescue Exception => e
|
36
45
|
error "Copy item_storage error: #{e.message}; #{e.backtrace.join(', ')}", :item_id => item_storage.item_id, :item_storage_id => item_storage.id
|
46
|
+
$curr_tasks.delete(task)
|
37
47
|
end
|
38
48
|
end
|
data/lib/fc/base.rb
CHANGED
@@ -34,7 +34,7 @@ module FC
|
|
34
34
|
|
35
35
|
# get element by id
|
36
36
|
def self.find(id)
|
37
|
-
r = FC::DB.
|
37
|
+
r = FC::DB.query("SELECT * FROM #{self.table_name} WHERE id=#{id.to_i}")
|
38
38
|
raise "Record not found (#{self.table_name}.id=#{id})" if r.count == 0
|
39
39
|
self.create_from_fiels(r.first)
|
40
40
|
end
|
@@ -43,7 +43,7 @@ module FC
|
|
43
43
|
def self.where(cond = "1", *params)
|
44
44
|
i = -1
|
45
45
|
sql = "SELECT * FROM #{self.table_name} WHERE #{cond.gsub('?'){i+=1; "'#{Mysql2::Client.escape(params[i].to_s)}'"}}"
|
46
|
-
r = FC::DB.
|
46
|
+
r = FC::DB.query(sql)
|
47
47
|
r.map{|data| self.create_from_fiels(data)}
|
48
48
|
end
|
49
49
|
|
@@ -63,7 +63,7 @@ module FC
|
|
63
63
|
if fields.length > 0
|
64
64
|
sql << fields.join(',')
|
65
65
|
sql << " WHERE id=#{@id.to_i}" if @id
|
66
|
-
FC::DB.
|
66
|
+
FC::DB.query(sql)
|
67
67
|
@id = FC::DB.connect.last_id unless @id
|
68
68
|
self.class.table_fields.each do |key|
|
69
69
|
@database_fields[key] = self.send(key)
|
@@ -81,7 +81,7 @@ module FC
|
|
81
81
|
|
82
82
|
# delete object from DB
|
83
83
|
def delete
|
84
|
-
FC::DB.
|
84
|
+
FC::DB.query("DELETE FROM #{self.class.table_name} WHERE id=#{@id.to_i}") if @id
|
85
85
|
end
|
86
86
|
|
87
87
|
end
|
data/lib/fc/db.rb
CHANGED
@@ -35,6 +35,19 @@ module FC
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
# connect.query with deadlock solution
|
39
|
+
def self.query(sql)
|
40
|
+
FC::DB.connect.query(sql)
|
41
|
+
rescue Mysql2::Error => e
|
42
|
+
if e.message.match('Deadlock found when trying to get lock')
|
43
|
+
puts "Deadlock"
|
44
|
+
sleep 0.1
|
45
|
+
self.query(sql)
|
46
|
+
else
|
47
|
+
raise e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
38
51
|
def DB.init_db
|
39
52
|
FC::DB.connect.query(%{
|
40
53
|
CREATE TABLE #{@prefix}items (
|
data/lib/fc/item.rb
CHANGED
@@ -34,7 +34,7 @@ module FC
|
|
34
34
|
if item
|
35
35
|
if options[:replace] || storage
|
36
36
|
# mark delete item_storages on replace
|
37
|
-
FC::DB.
|
37
|
+
FC::DB.query("UPDATE #{FC::ItemStorage.table_name} SET status='delete' WHERE item_id = #{item.id}") if options[:replace] && !storage
|
38
38
|
# replace all fields
|
39
39
|
item_params.each{|key, val| item.send("#{key}=", val)}
|
40
40
|
else
|
@@ -101,7 +101,7 @@ module FC
|
|
101
101
|
|
102
102
|
# mark items_storages for delete
|
103
103
|
def mark_deleted
|
104
|
-
FC::DB.
|
104
|
+
FC::DB.query("UPDATE #{FC::ItemStorage.table_name} SET status='delete' WHERE item_id = #{id}")
|
105
105
|
self.status = 'delete'
|
106
106
|
save
|
107
107
|
end
|
@@ -115,7 +115,7 @@ module FC
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def get_available_storages
|
118
|
-
r = FC::DB.
|
118
|
+
r = FC::DB.query("SELECT st.* FROM #{FC::Storage.table_name} as st, #{FC::ItemStorage.table_name} as ist WHERE
|
119
119
|
ist.item_id = #{id} AND ist.status='ready' AND ist.storage_name = st.name")
|
120
120
|
r.map{|data| FC::Storage.create_from_fiels(data)}.select {|storage| storage.up? }
|
121
121
|
end
|
data/lib/fc/version.rb
CHANGED
data/lib/manage/policies.rb
CHANGED
@@ -11,7 +11,7 @@ end
|
|
11
11
|
|
12
12
|
def policies_show
|
13
13
|
if policy = find_policy
|
14
|
-
count = FC::DB.
|
14
|
+
count = FC::DB.query("SELECT count(*) as cnt FROM #{FC::Item.table_name} WHERE policy_id = #{policy.id}").first['cnt']
|
15
15
|
puts %Q{Policy
|
16
16
|
ID: #{policy.id}
|
17
17
|
Name: #{policy.name}
|
data/lib/manage/show.rb
CHANGED
@@ -3,7 +3,7 @@ def show_current_host
|
|
3
3
|
end
|
4
4
|
|
5
5
|
def show_global_daemon
|
6
|
-
r = FC::DB.
|
6
|
+
r = FC::DB.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
|
7
7
|
if r
|
8
8
|
puts "Global daemon run on #{r['val']}\nLast run #{r['curr_time']-r['time']} seconds ago."
|
9
9
|
else
|
@@ -31,7 +31,7 @@ def show_host_info
|
|
31
31
|
else
|
32
32
|
puts "Info for host #{host}"
|
33
33
|
storages.each do |storage|
|
34
|
-
counts = FC::DB.
|
34
|
+
counts = FC::DB.query("SELECT status, count(*) as cnt FROM #{FC::ItemStorage.table_name} WHERE storage_name='#{Mysql2::Client.escape(storage.name)}' GROUP BY status")
|
35
35
|
str = "#{storage.name} #{size_to_human(storage.size)}/#{size_to_human(storage.size_limit)} "
|
36
36
|
str += "#{storage.up? ? colorize_string('UP', :green) : colorize_string('DOWN', :red)}"
|
37
37
|
str += " #{storage.check_time_delay} seconds ago" if storage.check_time
|
@@ -46,17 +46,17 @@ end
|
|
46
46
|
|
47
47
|
def show_items_info
|
48
48
|
puts "Items by status:"
|
49
|
-
counts = FC::DB.
|
49
|
+
counts = FC::DB.query("SELECT status, count(*) as cnt FROM #{FC::Item.table_name} WHERE 1 GROUP BY status")
|
50
50
|
counts.each do |r|
|
51
51
|
puts " #{r['status']}: #{r['cnt']}"
|
52
52
|
end
|
53
53
|
puts "Items storages by status:"
|
54
|
-
counts = FC::DB.
|
54
|
+
counts = FC::DB.query("SELECT status, count(*) as cnt FROM #{FC::ItemStorage.table_name} WHERE 1 GROUP BY status")
|
55
55
|
counts.each do |r|
|
56
56
|
puts " #{r['status']}: #{r['cnt']}"
|
57
57
|
end
|
58
|
-
count = FC::DB.
|
58
|
+
count = FC::DB.query("SELECT count(*) as cnt FROM #{FC::Item.table_name} as i, #{FC::Policy.table_name} as p WHERE i.policy_id = p.id AND i.copies > 0 AND i.copies < p.copies AND i.status = 'ready'").first['cnt']
|
59
59
|
puts "Items to copy: #{count}"
|
60
|
-
count = FC::DB.
|
60
|
+
count = FC::DB.query("SELECT count(*) as cnt FROM #{FC::Item.table_name} as i, #{FC::Policy.table_name} as p WHERE i.policy_id = p.id AND i.copies > p.copies AND i.status = 'ready'").first['cnt']
|
61
61
|
puts "Items to delete: #{count}"
|
62
62
|
end
|
data/lib/manage/storages.rb
CHANGED
@@ -14,7 +14,7 @@ end
|
|
14
14
|
|
15
15
|
def storages_show
|
16
16
|
if storage = find_storage
|
17
|
-
count = FC::DB.
|
17
|
+
count = FC::DB.query("SELECT count(*) as cnt FROM #{FC::ItemStorage.table_name} WHERE storage_name='#{Mysql2::Client.escape(storage.name)}'").first['cnt']
|
18
18
|
puts %Q{Storage
|
19
19
|
Name: #{storage.name}
|
20
20
|
Host: #{storage.host}
|
data/test/base_test.rb
CHANGED
data/test/daemon_test.rb
CHANGED
@@ -59,10 +59,10 @@ class DaemonTest < Test::Unit::TestCase
|
|
59
59
|
|
60
60
|
def shutdown
|
61
61
|
Process.kill("KILL", @@pid)
|
62
|
-
FC::DB.
|
63
|
-
FC::DB.
|
64
|
-
FC::DB.
|
65
|
-
FC::DB.
|
62
|
+
FC::DB.query("DELETE FROM items_storages")
|
63
|
+
FC::DB.query("DELETE FROM items")
|
64
|
+
FC::DB.query("DELETE FROM policies")
|
65
|
+
FC::DB.query("DELETE FROM storages")
|
66
66
|
`rm -rf /tmp/host*-sd*`
|
67
67
|
`rm -rf #{@@test_file_path}`
|
68
68
|
`rm -rf #{@@test_dir_path}`
|
data/test/db_test.rb
CHANGED
@@ -34,10 +34,10 @@ class DbTest < Test::Unit::TestCase
|
|
34
34
|
@@item_storages_ids = item_storages.map{|is| is.save; is.id }
|
35
35
|
end
|
36
36
|
def shutdown
|
37
|
-
FC::DB.
|
38
|
-
FC::DB.
|
39
|
-
FC::DB.
|
40
|
-
FC::DB.
|
37
|
+
FC::DB.query("DELETE FROM items_storages")
|
38
|
+
FC::DB.query("DELETE FROM items")
|
39
|
+
FC::DB.query("DELETE FROM policies")
|
40
|
+
FC::DB.query("DELETE FROM storages")
|
41
41
|
end
|
42
42
|
end
|
43
43
|
def setup
|
data/test/functional_test.rb
CHANGED
@@ -33,10 +33,10 @@ class FunctionalTest < Test::Unit::TestCase
|
|
33
33
|
@@policies.each { |policy| policy.save}
|
34
34
|
end
|
35
35
|
def shutdown
|
36
|
-
FC::DB.
|
37
|
-
FC::DB.
|
38
|
-
FC::DB.
|
39
|
-
FC::DB.
|
36
|
+
FC::DB.query("DELETE FROM items_storages")
|
37
|
+
FC::DB.query("DELETE FROM items")
|
38
|
+
FC::DB.query("DELETE FROM policies")
|
39
|
+
FC::DB.query("DELETE FROM storages")
|
40
40
|
`rm -rf /tmp/host*-sd*`
|
41
41
|
`rm -rf #{@@test_file_path}`
|
42
42
|
`rm -rf #{@@test_dir_path}`
|
data/test/helper.rb
CHANGED
@@ -10,8 +10,8 @@ TEST_USER = 'root'
|
|
10
10
|
TEST_PASSWORD = ''
|
11
11
|
|
12
12
|
FC::DB.connect_by_config(:username => TEST_USER, :password => TEST_PASSWORD)
|
13
|
-
FC::DB.
|
14
|
-
FC::DB.
|
15
|
-
FC::DB.
|
13
|
+
FC::DB.query("DROP DATABASE IF EXISTS #{TEST_DATABASE}")
|
14
|
+
FC::DB.query("CREATE DATABASE #{TEST_DATABASE}")
|
15
|
+
FC::DB.query("USE #{TEST_DATABASE}")
|
16
16
|
FC::DB.init_db
|
17
17
|
FC::DB.options[:database] = TEST_DATABASE
|
data/test/item_test.rb
CHANGED
@@ -17,9 +17,9 @@ class ItemTest < Test::Unit::TestCase
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
def shutdown
|
20
|
-
FC::DB.
|
21
|
-
FC::DB.
|
22
|
-
FC::DB.
|
20
|
+
FC::DB.query("DELETE FROM items_storages")
|
21
|
+
FC::DB.query("DELETE FROM items")
|
22
|
+
FC::DB.query("DELETE FROM storages")
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
data/test/policy_test.rb
CHANGED
@@ -14,8 +14,8 @@ class PolicyTest < Test::Unit::TestCase
|
|
14
14
|
@@policy.save
|
15
15
|
end
|
16
16
|
def shutdown
|
17
|
-
FC::DB.
|
18
|
-
FC::DB.
|
17
|
+
FC::DB.query("DELETE FROM policies")
|
18
|
+
FC::DB.query("DELETE FROM storages")
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filecluster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rb-readline
|
@@ -186,7 +186,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
186
|
version: '0'
|
187
187
|
segments:
|
188
188
|
- 0
|
189
|
-
hash:
|
189
|
+
hash: -778580397
|
190
190
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
191
|
none: false
|
192
192
|
requirements:
|
@@ -195,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
195
|
version: '0'
|
196
196
|
segments:
|
197
197
|
- 0
|
198
|
-
hash:
|
198
|
+
hash: -778580397
|
199
199
|
requirements: []
|
200
200
|
rubyforge_project:
|
201
201
|
rubygems_version: 1.8.24
|