redsync 0.1.1 → 0.1.2
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/README.textile +20 -17
- data/lib/redsync.rb +50 -139
- data/lib/redsync/sync_stat.rb +147 -0
- metadata +6 -5
data/README.textile
CHANGED
|
@@ -4,54 +4,57 @@ h2. What's this?
|
|
|
4
4
|
|
|
5
5
|
Sync your Redmine's wiki contents to your local filesystem.
|
|
6
6
|
Edit them while offline, then upsync them to Redmine.
|
|
7
|
+
(Requires Ruby 1.9)
|
|
7
8
|
|
|
8
|
-
h2.
|
|
9
|
-
|
|
10
|
-
Copy and edit config.yml.dist -> config.yml
|
|
9
|
+
h2. Install
|
|
11
10
|
|
|
12
11
|
<pre>
|
|
13
|
-
|
|
14
|
-
:url: "http://your.redmine.url/"
|
|
15
|
-
:project_slug: "project_identifier"
|
|
16
|
-
:username: "username"
|
|
17
|
-
:password: "password"
|
|
18
|
-
:data_dir: "~/redsync"
|
|
12
|
+
gem install redsync
|
|
19
13
|
</pre>
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
|
|
16
|
+
h2. Usage
|
|
22
17
|
|
|
23
18
|
<pre>
|
|
19
|
+
$ redsync --help
|
|
24
20
|
Usage: redsync [options]
|
|
25
21
|
-v, --[no-]verbose Output verbose logs
|
|
26
|
-
-c, --config FILE Use specified config file instead of
|
|
22
|
+
-c, --config FILE Use specified config file instead of ~/redsync.yml
|
|
27
23
|
-u, --upsync-only Upsync only, don't downsync
|
|
28
24
|
-d, --downsync-only Downsync only, don't upsync
|
|
29
25
|
</pre>
|
|
30
26
|
|
|
31
|
-
h2. Dependencies
|
|
32
|
-
|
|
33
|
-
* Ruby 1.9
|
|
34
|
-
* Mechanize (@gem install mechanize@)
|
|
35
|
-
* ActiveSupport (@gem install activesupport@)
|
|
36
27
|
|
|
37
28
|
h2. How it works
|
|
38
29
|
|
|
39
30
|
Just some scraping...
|
|
40
31
|
|
|
32
|
+
|
|
41
33
|
h2. Warnings
|
|
42
34
|
|
|
43
35
|
This software has NOT BEEN TESTED :(
|
|
44
36
|
Use it at your own risk.
|
|
45
37
|
Please beware that it may not work on all versions of Redmine (it's working fine with my installation of 1.2-stable)
|
|
46
38
|
|
|
39
|
+
h3. Time zones
|
|
40
|
+
|
|
47
41
|
Redsync assumes that your local timezone is the same as your Redmine timezone.
|
|
48
42
|
Sign in and go to "My Account" to change it.
|
|
49
43
|
|
|
50
|
-
|
|
44
|
+
h3. Conflicts
|
|
45
|
+
|
|
46
|
+
Redsync does not deal with conflicts well.
|
|
51
47
|
In fact, it does not deal with conflics AT ALL.
|
|
52
48
|
A default run of redsync will downsync first, overwriting any local changes if pages are updated on Redmine.
|
|
53
49
|
Then it will upsync any remaining changes.
|
|
54
50
|
|
|
51
|
+
h3. Deletions
|
|
52
|
+
|
|
53
|
+
Redsy does not delete anything.
|
|
54
|
+
It doesn't delete local files when remote wiki pages are gone.
|
|
55
|
+
It doesn't delete remote wiki pages when local files are gone either.
|
|
56
|
+
|
|
57
|
+
|
|
55
58
|
h2. License
|
|
56
59
|
|
|
57
60
|
The MIT License
|
data/lib/redsync.rb
CHANGED
|
@@ -10,35 +10,38 @@ require 'mechanize'
|
|
|
10
10
|
require 'active_support/all'
|
|
11
11
|
|
|
12
12
|
require 'redsync/cli'
|
|
13
|
+
require 'redsync/sync_stat'
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class Redsync
|
|
16
17
|
|
|
17
|
-
class
|
|
18
|
+
class Result
|
|
18
19
|
DOWNLOADED = 1
|
|
19
20
|
SKIPPED_UNKNOWN = 2
|
|
20
21
|
SKIPPED_OLD = 4
|
|
21
22
|
UPLOADED = 8
|
|
22
23
|
CREATED = 16
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
ERROR_ON_UPLOAD = 32
|
|
25
|
+
ERROR_ON_CREATE = 64
|
|
25
26
|
end
|
|
26
27
|
|
|
28
|
+
|
|
27
29
|
def initialize(options)
|
|
28
30
|
@config = {}
|
|
29
31
|
@config.merge! options
|
|
30
32
|
@config[:data_dir] = File.expand_path(@config[:data_dir])
|
|
31
33
|
@config[:conflicts_dir] = File.join(@config[:data_dir], "__redsync_conflicts__")
|
|
32
|
-
@config[:pages_list_file] = File.join(@config[:data_dir], "__redsync_pages_list__.yml")
|
|
33
34
|
puts "Using data dir: #{@config[:data_dir]}"
|
|
34
35
|
|
|
35
|
-
@
|
|
36
|
-
@
|
|
36
|
+
@config[:url] = @config[:url].match(/(.*?)\/?$/)[1]
|
|
37
|
+
@config[:login_url] = @config[:url] + "/login"
|
|
38
|
+
@config[:wiki_base_url] = @config[:url] + "/projects/" + @config[:project_slug] + "/wiki"
|
|
37
39
|
|
|
38
40
|
initialize_system_files
|
|
39
|
-
recover_pages_list
|
|
40
41
|
|
|
41
42
|
@agent = Mechanize.new
|
|
43
|
+
@syncstat = SyncStat.new(@config, @agent)
|
|
44
|
+
|
|
42
45
|
@logged_in = false
|
|
43
46
|
end
|
|
44
47
|
|
|
@@ -48,19 +51,17 @@ class Redsync
|
|
|
48
51
|
puts "Creating data dir"
|
|
49
52
|
FileUtils.mkdir(@config[:data_dir])
|
|
50
53
|
end
|
|
51
|
-
FileUtils.mkdir(@config[:conflicts_dir]) unless File.exist? @config[:conflicts_dir]
|
|
52
|
-
FileUtils.touch(@config[:pages_list_file]) unless File.exist? @config[:pages_list_file]
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
|
|
56
57
|
def login
|
|
57
|
-
puts "Logging in as #{@config[:username]} to #{@login_url}..."
|
|
58
|
-
page = @agent.get(@login_url)
|
|
58
|
+
puts "Logging in as #{@config[:username]} to #{@config[:login_url]}..."
|
|
59
|
+
page = @agent.get(@config[:login_url])
|
|
59
60
|
login_form = page.form_with(:action => "/login")
|
|
60
61
|
login_form.field_with(:name => "username").value = @config[:username]
|
|
61
62
|
login_form.field_with(:name => "password").value = @config[:password]
|
|
62
63
|
result_page = login_form.submit
|
|
63
|
-
if result_page.
|
|
64
|
+
if result_page.search("a.logout").any?
|
|
64
65
|
puts "Logged in successfully."
|
|
65
66
|
return true
|
|
66
67
|
else
|
|
@@ -70,166 +71,76 @@ class Redsync
|
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
|
|
73
|
-
def pages
|
|
74
|
-
refresh_pages_list if (!@pages || @pages.empty?)
|
|
75
|
-
@pages
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
|
|
79
74
|
def downsync
|
|
75
|
+
@syncstat.refresh
|
|
80
76
|
puts "\nDownsync:"
|
|
81
77
|
statuses = {
|
|
82
|
-
|
|
83
|
-
Status::SKIPPED_OLD => 0,
|
|
84
|
-
Status::SKIPPED_UNKNOWN => 0,
|
|
78
|
+
Result::DOWNLOADED => 0,
|
|
85
79
|
}
|
|
86
|
-
|
|
87
|
-
statuses[
|
|
80
|
+
@syncstat.remote_updated_page_names.each do |pagename|
|
|
81
|
+
statuses[download(pagename)] += 1
|
|
88
82
|
end
|
|
89
|
-
puts "Downloaded #{statuses[
|
|
90
|
-
puts "Skipped #{statuses[Status::SKIPPED_OLD] + statuses[Status::SKIPPED_UNKNOWN]} pages."
|
|
83
|
+
puts "Downloaded #{statuses[Result::DOWNLOADED]} pages."
|
|
91
84
|
end
|
|
92
85
|
|
|
93
86
|
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
page_info = pages[pagename]
|
|
98
|
-
if !page_info
|
|
99
|
-
puts "--Skipping #{pagename}: unknown page"
|
|
100
|
-
return Status::SKIPPED_UNKNOWN
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
filename = File.join(@config[:data_dir], "#{pagename}.txt")
|
|
104
|
-
|
|
105
|
-
if File.exist? filename
|
|
106
|
-
local_updated_at = File.stat(filename).mtime.to_datetime
|
|
107
|
-
remote_updated_at = DateTime.parse(page_info[:updated_at_str] + "T00:00:00" + now.zone)
|
|
108
|
-
if remote_updated_at.year == now.year && remote_updated_at.month == now.month && remote_updated_at.day == now.day
|
|
109
|
-
remote_updated_at = page_history(pagename)[0][:timestamp]
|
|
110
|
-
end
|
|
111
|
-
if local_updated_at >= remote_updated_at
|
|
112
|
-
puts "--Skip #{pagename}: local (#{local_updated_at}) is newer than remote (#{remote_updated_at})" if @config[:verbose]
|
|
113
|
-
return Status::SKIPPED_OLD
|
|
114
|
-
end
|
|
115
|
-
end
|
|
87
|
+
def download(pagename)
|
|
88
|
+
stat = @syncstat.for(pagename)
|
|
116
89
|
|
|
117
90
|
puts "--Download #{pagename}"
|
|
118
|
-
page = @agent.get(
|
|
119
|
-
File.open(
|
|
120
|
-
@
|
|
121
|
-
write_pages_list
|
|
91
|
+
page = @agent.get(stat[:url] + "/edit")
|
|
92
|
+
File.open(stat[:local_file], "w+:UTF-8") { |f| f.write(page.search("textarea")[0].text) }
|
|
93
|
+
@syncstat.update(pagename, :downloaded_at => File.stat(stat[:local_file]).mtime.to_datetime)
|
|
122
94
|
|
|
123
|
-
return
|
|
95
|
+
return Result::DOWNLOADED
|
|
124
96
|
end
|
|
125
97
|
|
|
126
98
|
|
|
127
99
|
def upsync
|
|
128
100
|
puts "\nUpsync:"
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
Status::ERROR_ON_EDIT => 0
|
|
101
|
+
results = {
|
|
102
|
+
Result::UPLOADED => 0,
|
|
103
|
+
Result::CREATED => 0,
|
|
104
|
+
Result::ERROR_ON_UPLOAD => 0,
|
|
105
|
+
Result::ERROR_ON_CREATE => 0,
|
|
135
106
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
statuses[upsync_page(File.basename(file, ".txt"))] += 1
|
|
107
|
+
@syncstat.new_page_names.each do |pagename|
|
|
108
|
+
results[upload(pagename, true)] += 1
|
|
109
|
+
end
|
|
110
|
+
@syncstat.local_updated_page_names.each do |pagename|
|
|
111
|
+
results[upload(pagename)] += 1
|
|
142
112
|
end
|
|
143
|
-
print "Created #{
|
|
144
|
-
print " (#{
|
|
113
|
+
print "Created #{results[Result::CREATED]} pages."
|
|
114
|
+
print " (#{results[Result::ERROR_ON_CREATE]} errors)" if results[Result::ERROR_ON_CREATE] > 0
|
|
145
115
|
print "\n"
|
|
146
|
-
print "Uploaded #{
|
|
147
|
-
print " (#{
|
|
116
|
+
print "Uploaded #{results[Result::UPLOADED]} pages."
|
|
117
|
+
print " (#{results[Result::ERROR_ON_UPLOAD]} errors)" if results[Result::ERROR_ON_UPLOAD] > 0
|
|
148
118
|
print "\n"
|
|
149
|
-
puts "Skipped #{statuses[Status::SKIPPED_OLD]} pages."
|
|
150
119
|
end
|
|
151
120
|
|
|
152
121
|
|
|
153
|
-
def
|
|
122
|
+
def upload(pagename, create = false)
|
|
154
123
|
now = DateTime.now
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
local_updated_at = File.stat(filename).mtime.to_datetime
|
|
124
|
+
stat = @syncstat.for(pagename)
|
|
125
|
+
puts (create ? "--Create #{pagename}" : "--Upload #{pagename}")
|
|
158
126
|
|
|
159
|
-
|
|
160
|
-
puts "--Skip #{pagename}" if @config[:verbose]
|
|
161
|
-
return Status::SKIPPED_OLD
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
puts (page_info ? "--Upload #{pagename}" : "--Create #{pagename}")
|
|
165
|
-
page = @agent.get(@wiki_base_url + "/" + pagename + "/edit")
|
|
127
|
+
page = @agent.get(@config[:wiki_base_url] + "/" + pagename + "/edit")
|
|
166
128
|
form = page.form_with(:id=>"wiki_form")
|
|
167
|
-
form.field_with(:name=>"content[text]").value = File.open(
|
|
129
|
+
form.field_with(:name=>"content[text]").value = File.open(stat[:local_file], "r:UTF-8").read
|
|
168
130
|
result_page = form.submit
|
|
169
131
|
errors = result_page.search("#errorExplanation li").map{|li|li.text}
|
|
132
|
+
|
|
170
133
|
if errors.any?
|
|
171
134
|
print "--Error: #{pagename}: "
|
|
172
135
|
puts errors
|
|
173
|
-
return (
|
|
136
|
+
return (create ? Result::ERROR_ON_CREATE : Result::ERROR_ON_UPLOAD)
|
|
174
137
|
else
|
|
175
138
|
now = DateTime.now
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
write_pages_list
|
|
182
|
-
return (page_info ? Status::UPLOADED : Status::CREATED)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def refresh_pages_list
|
|
188
|
-
puts "Refreshing pages list"
|
|
189
|
-
@pages ||= {}
|
|
190
|
-
page = @agent.get(@wiki_base_url + "/date_index")
|
|
191
|
-
|
|
192
|
-
page.search("#content h3").each do |h3|
|
|
193
|
-
links = h3.next_element.search("a")
|
|
194
|
-
links.each do |link|
|
|
195
|
-
page_url = @config[:url] + link.attr("href")
|
|
196
|
-
pagename = URI.decode(page_url.match(/^#{@wiki_base_url}\/(.*)$/)[1]).force_encoding("UTF-8")
|
|
197
|
-
@pages[pagename] ||= {}
|
|
198
|
-
@pages[pagename][:name] = pagename
|
|
199
|
-
@pages[pagename][:url] = page_url
|
|
200
|
-
@pages[pagename][:updated_at_str] = h3.text
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
@pages
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def recover_pages_list
|
|
209
|
-
@pages = YAML.load_file(@config[:pages_list_file])
|
|
210
|
-
@pages ||= {}
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def write_pages_list
|
|
215
|
-
File.open(@config[:pages_list_file], "w+:UTF-8") do |f|
|
|
216
|
-
f.write(@pages.to_yaml)
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
def page_history(pagename)
|
|
222
|
-
puts "--Getting page history for #{pagename}" if @config[:verbose]
|
|
223
|
-
history = []
|
|
224
|
-
page = @agent.get(@wiki_base_url + "/" + URI.encode(pagename) + "/history")
|
|
225
|
-
page.search("table.wiki-page-versions tbody tr").each do |tr|
|
|
226
|
-
timestamp = DateTime.parse(tr.search("td")[3].text+"+0900")
|
|
227
|
-
author_name = tr.search("td")[4].text.strip
|
|
228
|
-
history << {
|
|
229
|
-
:timestamp => timestamp,
|
|
230
|
-
:author_name => author_name
|
|
231
|
-
}
|
|
139
|
+
@syncstat.update(pagename, {
|
|
140
|
+
:downloaded_at => now,
|
|
141
|
+
:remote_updated_at => now
|
|
142
|
+
})
|
|
143
|
+
return (create ? Result::CREATED : Result::UPLOADED)
|
|
232
144
|
end
|
|
233
|
-
history
|
|
234
145
|
end
|
|
235
146
|
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
class Redsync
|
|
5
|
+
class SyncStat
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
def initialize(config, agent)
|
|
9
|
+
@config = config
|
|
10
|
+
@agent = agent
|
|
11
|
+
|
|
12
|
+
@file = File.join(@config[:data_dir], "__redsync_stat.yml")
|
|
13
|
+
FileUtils.touch @file unless File.exist? @file
|
|
14
|
+
@stat = YAML.load_file(@file) || {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def refresh
|
|
19
|
+
puts "Refreshing pages list"
|
|
20
|
+
page = @agent.get(@config[:wiki_base_url] + "/date_index")
|
|
21
|
+
now = DateTime.now
|
|
22
|
+
|
|
23
|
+
# Get remote and local update times using remote list
|
|
24
|
+
page.search("#content h3").each do |h3|
|
|
25
|
+
links = h3.next_element.search("a")
|
|
26
|
+
links.each do |link|
|
|
27
|
+
url = @config[:url] + link.attr("href")
|
|
28
|
+
name = URI.decode(url.match(/^#{@config[:wiki_base_url]}\/(.*)$/)[1]).force_encoding("UTF-8")
|
|
29
|
+
local_file = File.join(@config[:data_dir], "#{name}.txt")
|
|
30
|
+
|
|
31
|
+
remote_updated_at = DateTime.parse(h3.text + "T00:00:00" + now.zone)
|
|
32
|
+
|
|
33
|
+
if File.exist? local_file
|
|
34
|
+
local_updated_at = File.stat(local_file).mtime.to_datetime
|
|
35
|
+
if remote_updated_at.year == now.year && remote_updated_at.month == now.month && remote_updated_at.day == now.day
|
|
36
|
+
remote_updated_at = history(name)[0][:timestamp]
|
|
37
|
+
end
|
|
38
|
+
else
|
|
39
|
+
local_updated_at = DateTime.civil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
update(name, {
|
|
43
|
+
:name => name,
|
|
44
|
+
:url => url,
|
|
45
|
+
:local_file => local_file,
|
|
46
|
+
:remote_updated_at => remote_updated_at,
|
|
47
|
+
:local_updated_at => local_updated_at,
|
|
48
|
+
}, true)
|
|
49
|
+
|
|
50
|
+
update(name, {
|
|
51
|
+
:downloaded_at => local_updated_at
|
|
52
|
+
}, true) unless File.exist? local_file
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Look for new page files at local
|
|
57
|
+
Dir.entries(@config[:data_dir]).each do |file|
|
|
58
|
+
fullpath = File.join(@config[:data_dir], file)
|
|
59
|
+
next if File.directory?(fullpath)
|
|
60
|
+
next if file =~ /^__redsync_/
|
|
61
|
+
name = Iconv.iconv("UTF-8", "UTF-8-MAC", file) if RUBY_PLATFORM =~ /darwin/
|
|
62
|
+
name = name.first.match(/(.*)\.txt$/)[1]
|
|
63
|
+
next if @stat[name]
|
|
64
|
+
|
|
65
|
+
local_file = File.join(@config[:data_dir], "#{name}.txt")
|
|
66
|
+
local_updated_at = File.stat(local_file).mtime.to_datetime
|
|
67
|
+
update(name, {
|
|
68
|
+
:name => name,
|
|
69
|
+
:url => @config[:wiki_base_url] + "/#{name}",
|
|
70
|
+
:local_file => local_file,
|
|
71
|
+
:remote_updated_at => nil,
|
|
72
|
+
:local_updated_at => local_updated_at,
|
|
73
|
+
:downloaded_at => local_updated_at
|
|
74
|
+
}, true)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
write
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def history(name)
|
|
82
|
+
puts "--Getting page history for #{name}" if @config[:verbose]
|
|
83
|
+
now = DateTime.now
|
|
84
|
+
history = []
|
|
85
|
+
page = @agent.get(@config[:wiki_base_url] + "/" + URI.encode(name) + "/history")
|
|
86
|
+
page.search("table.wiki-page-versions tbody tr").each do |tr|
|
|
87
|
+
timestamp = DateTime.parse(tr.search("td")[3].text + now.zone)
|
|
88
|
+
author_name = tr.search("td")[4].text.strip
|
|
89
|
+
history << {
|
|
90
|
+
:timestamp => timestamp,
|
|
91
|
+
:author_name => author_name
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
history
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def each
|
|
99
|
+
@stat.each {|k,v| yield v}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def for(name)
|
|
104
|
+
@stat[name]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def update(name, hash, suspend_write = false)
|
|
109
|
+
@stat[name] ||= {}
|
|
110
|
+
@stat[name].merge! hash
|
|
111
|
+
write unless suspend_write
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def write
|
|
116
|
+
File.open(@file, "w+:UTF-8") do |f|
|
|
117
|
+
f.write("# DO NOT EDIT THIS FILE!\n")
|
|
118
|
+
f.write(@stat.to_yaml)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def new_page_names
|
|
124
|
+
self.inject([]) do |sum, page|
|
|
125
|
+
sum << page[:name] if !page[:remote_updated_at]
|
|
126
|
+
sum
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def remote_updated_page_names
|
|
132
|
+
self.inject([]) do |sum, page|
|
|
133
|
+
sum << page[:name] if page[:remote_updated_at] && (page[:remote_updated_at] > page[:downloaded_at])
|
|
134
|
+
sum
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def local_updated_page_names
|
|
140
|
+
self.inject([]) do |sum, page|
|
|
141
|
+
sum << page[:name] if page[:downloaded_at] && (page[:local_updated_at] > page[:downloaded_at])
|
|
142
|
+
sum
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: redsync
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -13,7 +13,7 @@ date: 2011-12-07 00:00:00.000000000Z
|
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: mechanize
|
|
16
|
-
requirement: &
|
|
16
|
+
requirement: &2160416520 !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ~>
|
|
@@ -21,10 +21,10 @@ dependencies:
|
|
|
21
21
|
version: '2.0'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
|
-
version_requirements: *
|
|
24
|
+
version_requirements: *2160416520
|
|
25
25
|
- !ruby/object:Gem::Dependency
|
|
26
26
|
name: activesupport
|
|
27
|
-
requirement: &
|
|
27
|
+
requirement: &2160416120 !ruby/object:Gem::Requirement
|
|
28
28
|
none: false
|
|
29
29
|
requirements:
|
|
30
30
|
- - ! '>='
|
|
@@ -32,7 +32,7 @@ dependencies:
|
|
|
32
32
|
version: '0'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
|
-
version_requirements: *
|
|
35
|
+
version_requirements: *2160416120
|
|
36
36
|
description: Sync Redmine's wiki pages to your local filesystem. Edit as you like,
|
|
37
37
|
then upsync.
|
|
38
38
|
email: merikonjatta@gmail.com
|
|
@@ -42,6 +42,7 @@ extensions: []
|
|
|
42
42
|
extra_rdoc_files: []
|
|
43
43
|
files:
|
|
44
44
|
- lib/redsync/cli.rb
|
|
45
|
+
- lib/redsync/sync_stat.rb
|
|
45
46
|
- lib/redsync.rb
|
|
46
47
|
- README.textile
|
|
47
48
|
- config.yml.dist
|