rypple 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/README +1 -0
- data/Rakefile +1 -0
- data/bin/rippleInstall.rb +169 -0
- data/lib/ripple/base.rb +181 -0
- data/lib/ripple/version.rb +3 -0
- data/lib/ripple.rb +1 -0
- data/pkg/ripple-0.0.1.gem +0 -0
- data/ripple.gemspec +22 -0
- data/ripple.yaml +5 -0
- metadata +102 -0
data/Gemfile
ADDED
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
A tool for integrating some remote file store (e.g. Dropbox) with a static site generator (e.g. jekyll) with some tools to ease remote updating of the site.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,169 @@
|
|
1
|
+
#!/bin/ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'ripple'
|
4
|
+
|
5
|
+
baseCGI = <<'eos'
|
6
|
+
#!/usr/bin/ruby
|
7
|
+
gemPath = "%%GEM_PATH"
|
8
|
+
configPath = "%%CONFIG_PATH"
|
9
|
+
inDir = "%%IN_DIR"
|
10
|
+
outDir = "%%OUT_DIR"
|
11
|
+
|
12
|
+
if !gemPath.empty?
|
13
|
+
$:.push(gemPath)
|
14
|
+
ENV['GEM_PATH'] = gemPath
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'cgi'
|
18
|
+
require 'rubygems'
|
19
|
+
require 'ripple'
|
20
|
+
require 'jekyll'
|
21
|
+
|
22
|
+
cgi = CGI.new
|
23
|
+
|
24
|
+
puts cgi.header
|
25
|
+
|
26
|
+
puts "<h1>Rippling . . .</h1>"
|
27
|
+
if Ripple.sync(configPath)
|
28
|
+
puts "<h1>Generating Static Site</h1>"
|
29
|
+
puts `%%COMMAND #{inDir} #{outDir}`
|
30
|
+
end
|
31
|
+
eos
|
32
|
+
|
33
|
+
updateHTML = <<'eos'
|
34
|
+
<html>
|
35
|
+
<head>
|
36
|
+
<title>Update Site with Ripple!</title>
|
37
|
+
</head>
|
38
|
+
<body>
|
39
|
+
<h1> Throw a stone into the pond..</h1>
|
40
|
+
<form action="update.cgi" method="POST">
|
41
|
+
<input type="submit" value="Ripple">
|
42
|
+
</form>
|
43
|
+
</body>
|
44
|
+
</html>
|
45
|
+
eos
|
46
|
+
|
47
|
+
htaccess = <<'eos'
|
48
|
+
AuthName "Ripple Updater"
|
49
|
+
AuthType Basic
|
50
|
+
AuthUserFile %%AUTH_FILE
|
51
|
+
Require valid-user
|
52
|
+
eos
|
53
|
+
|
54
|
+
configBase = File.join(ENV["HOME"], '.ripple')
|
55
|
+
puts "Configuring the Ripple update script for web use."
|
56
|
+
puts "Please enter directory for Ripple Configuration. Default:", configBase
|
57
|
+
directory = gets.chomp!
|
58
|
+
|
59
|
+
if !directory.empty?
|
60
|
+
configBase = directory
|
61
|
+
end
|
62
|
+
|
63
|
+
conf = Ripple.loadConfiguration(configBase)
|
64
|
+
session, client, keys = Ripple.connectToDropbox(configBase)
|
65
|
+
|
66
|
+
if !conf.nil? and !keys.nil?
|
67
|
+
Ripple.cleanup(conf, keys, configBase)
|
68
|
+
end
|
69
|
+
|
70
|
+
baseCGI.gsub!(/%%CONFIG_PATH/, configBase)
|
71
|
+
|
72
|
+
choice = false
|
73
|
+
insert = true
|
74
|
+
while !choice do
|
75
|
+
puts "Is your web host set up to use ruby gems? (y/N)"
|
76
|
+
answer = gets.chomp!.downcase!
|
77
|
+
if answer.nil? or answer.empty?
|
78
|
+
choice = true
|
79
|
+
elsif answer == "n" or answer == "y"
|
80
|
+
insert = (answer == "n")
|
81
|
+
choice = true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if insert
|
86
|
+
gemPath = ''
|
87
|
+
if ENV.has_key?('GEM_HOME')
|
88
|
+
gemPath = ENV['GEM_HOME']
|
89
|
+
else
|
90
|
+
puts "Please enter search path for the ruby gems."
|
91
|
+
gemPath = gets.chomp!
|
92
|
+
end
|
93
|
+
|
94
|
+
baseCGI.gsub!(/%%GEM_PATH/, gemPath)
|
95
|
+
end
|
96
|
+
|
97
|
+
puts "Where should static site files be store?"
|
98
|
+
inDir = File.expand_path(gets.chomp!)
|
99
|
+
|
100
|
+
if !File.exists?(inDir)
|
101
|
+
begin
|
102
|
+
Dir.mkdir(inDir)
|
103
|
+
rescue SystemCallError
|
104
|
+
puts "Cannot create", inDir, ", create this directory and run this script again."
|
105
|
+
exit
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
baseCGI.gsub!(/%%IN_DIR/, inDir)
|
110
|
+
|
111
|
+
puts "Where should the static site generator output files?"
|
112
|
+
outDir = File.expand_path(gets.chomp!)
|
113
|
+
|
114
|
+
if !File.exists?(inDir)
|
115
|
+
begin
|
116
|
+
Dir.mkdir(outDir)
|
117
|
+
rescue SystemCallError
|
118
|
+
puts "Cannot create output directory", outDir, ", fix this and run this script again."
|
119
|
+
exit
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
baseCGI.gsub!(/%%OUT_DIR/, outDir)
|
124
|
+
|
125
|
+
command = File.join("#{ENV["GEM_HOME"]}", 'bin', 'jekyll')
|
126
|
+
if File.exists?(command)
|
127
|
+
puts "Please enter any arguments to pass to jekyll"
|
128
|
+
args = gets.chomp!
|
129
|
+
baseCGI.gsub!(/%%COMMAND/, command + ' ' + args)
|
130
|
+
end
|
131
|
+
|
132
|
+
rippleDir = File.join(inDir, 'ripple')
|
133
|
+
|
134
|
+
if !File.exists?(rippleDir)
|
135
|
+
begin
|
136
|
+
Dir.mkdir(rippleDir)
|
137
|
+
rescue SystemCallError
|
138
|
+
"Cannot create ripple directory."
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
File.open(File.join(rippleDir, 'update.html'), 'w', 0644) { |f| f.puts updateHTML }
|
143
|
+
|
144
|
+
out = File.join(rippleDir, 'update.cgi')
|
145
|
+
File.open(out, 'w', 0755) { |f| f.puts baseCGI }
|
146
|
+
|
147
|
+
puts "Should I enable basic user authentication for the update script? (Y/n):"
|
148
|
+
|
149
|
+
answer = gets.chomp!
|
150
|
+
if answer.nil? or answer.empty? or answer.downcase! == 'y'
|
151
|
+
print "Enter user name for authentication:"
|
152
|
+
user = gets.chomp!
|
153
|
+
print "Enter password for authentication:"
|
154
|
+
chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
|
155
|
+
salt = chars[rand(chars.size - 1)] + chars[rand(chars.size - 1)]
|
156
|
+
pass = gets.chomp!.crypt(salt)
|
157
|
+
authFile = File.join(configBase, '.htpasswd')
|
158
|
+
File.open(authFile, 'w') { |f| f.puts "#{user}:#{pass}" }
|
159
|
+
htaccess.gsub!(/%%AUTH_FILE/, authFile)
|
160
|
+
File.open(File.join(rippleDir, '.htaccess'), 'w') { |f| f.puts htaccess }
|
161
|
+
end
|
162
|
+
|
163
|
+
puts "Attempting first update"
|
164
|
+
|
165
|
+
if Ripple.sync(configBase)
|
166
|
+
puts `jekyll #{inDir} #{outDir}`
|
167
|
+
else
|
168
|
+
puts "Ripple sync failed."
|
169
|
+
end
|
data/lib/ripple/base.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
module Ripple
|
3
|
+
require 'fileutils'
|
4
|
+
require 'yaml'
|
5
|
+
require "rubygems"
|
6
|
+
require 'dropbox_sdk'
|
7
|
+
require 'pathname'
|
8
|
+
|
9
|
+
ACCESS_TYPE = :app_folder
|
10
|
+
DefaultConfiguration = {
|
11
|
+
:destinationDir => './test',
|
12
|
+
:dropbox => {
|
13
|
+
:sync => ['**'],
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
DropboxKeyFile = "dropbox_session.yaml"
|
18
|
+
RippleConfigFile = "ripple.yaml"
|
19
|
+
|
20
|
+
def Ripple.connectToDropbox(path)
|
21
|
+
dropConf = File.join(path, DropboxKeyFile)
|
22
|
+
#Load Dropbox API dropboxKeys from file, if applicable.
|
23
|
+
if File.exists?(dropConf)
|
24
|
+
dropboxKeys = YAML::load(File.read(dropConf))
|
25
|
+
else
|
26
|
+
puts "A Dropbox API key/secret is required for accessing your sync files."
|
27
|
+
puts "You can visit https://www.dropbox.com/developers/apps to generate these."
|
28
|
+
puts "Please enter your Dropbox API key"
|
29
|
+
dropboxKeys['key'] = gets.chomp!
|
30
|
+
puts "Please enter your Dropbox API secret"
|
31
|
+
dropboxKeys['secret'] = gets.chomp!
|
32
|
+
end
|
33
|
+
|
34
|
+
session = nil
|
35
|
+
|
36
|
+
if dropboxKeys.has_key?(:session)
|
37
|
+
session = DropboxSession.deserialize(dropboxKeys[:session])
|
38
|
+
else
|
39
|
+
session = DropboxSession.new(dropboxKeys[:key], dropboxKeys[:secret])
|
40
|
+
session.get_request_token
|
41
|
+
authorize_url = session.get_authorize_url
|
42
|
+
puts "Visit #{authorize_url} to log in to Dropbox. Hit enter when you have done this."
|
43
|
+
gets
|
44
|
+
session.get_access_token
|
45
|
+
end
|
46
|
+
|
47
|
+
if session.nil?
|
48
|
+
return nil, nil, nil
|
49
|
+
end
|
50
|
+
|
51
|
+
if !dropboxKeys.has_key?(:session)
|
52
|
+
dropboxKeys[:session] = session.serialize()
|
53
|
+
end
|
54
|
+
|
55
|
+
client = DropboxClient.new(session, ACCESS_TYPE)
|
56
|
+
|
57
|
+
if client.nil?
|
58
|
+
return nil, nil, nil
|
59
|
+
else
|
60
|
+
return session, client, dropboxKeys
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def Ripple.loadConfiguration(path)
|
65
|
+
conf = Ripple::DefaultConfiguration.dup
|
66
|
+
|
67
|
+
ripConf = File.join(path, RippleConfigFile)
|
68
|
+
# Load configuration and override any values that differ from the default.
|
69
|
+
if File.exists?(ripConf)
|
70
|
+
loadedConf = YAML::load(File.read(ripConf))
|
71
|
+
conf.merge!(loadedConf)
|
72
|
+
end
|
73
|
+
|
74
|
+
conf[:destinationDir] = File.expand_path(conf[:destinationDir])
|
75
|
+
if !File.directory?(conf[:destinationDir])
|
76
|
+
begin
|
77
|
+
Dir.mkdir(conf[:destinationDir])
|
78
|
+
rescue SystemCallError
|
79
|
+
raise RuntimeError, "Destination doesn't exist and cannot be created."
|
80
|
+
end
|
81
|
+
end
|
82
|
+
return conf
|
83
|
+
end
|
84
|
+
|
85
|
+
def Ripple.cleanup(conf, keys, path)
|
86
|
+
dropConfig = File.join(path, DropboxKeyFile)
|
87
|
+
File.open(dropConfig, 'w') do|file|
|
88
|
+
file.puts keys.to_yaml
|
89
|
+
end
|
90
|
+
|
91
|
+
rippleConf = File.join(path, RippleConfigFile)
|
92
|
+
File.open(rippleConf, 'w') do |file|
|
93
|
+
file.puts conf.to_yaml
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Iterates over dropbox directory, returing paths and state hash for each file
|
98
|
+
# oldFileState should be a hash of paths to state hashes, same as return values
|
99
|
+
def Ripple.walkDropbox(client, path, fileState, oldFileState)
|
100
|
+
#Here we need to actually sync newest files.
|
101
|
+
begin
|
102
|
+
useState = (!oldFileState.nil? and oldFileState.has_key?(path) and oldFileState[path]["path"] == path)
|
103
|
+
oldState = useState ? oldFileState[path]["hash"] : nil
|
104
|
+
states = client.metadata(path, 10000, true, oldState)
|
105
|
+
rescue DropboxNotModified
|
106
|
+
puts "Files have not changed."
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
|
110
|
+
files = { states["path"] => states }
|
111
|
+
#State represents a folder
|
112
|
+
if states["is_dir"] and states.has_key?("contents")
|
113
|
+
states["contents"].each{ |xx|
|
114
|
+
if !xx.nil?
|
115
|
+
files[xx["path"]] = xx
|
116
|
+
end
|
117
|
+
useState = (!oldFileState.nil? and oldFileState.has_key?(xx["path"]))
|
118
|
+
old = (useState ? oldFileState[xx["path"]]["hash"] : nil)
|
119
|
+
subs = Ripple.walkDropbox(client, xx["path"], fileState, old)
|
120
|
+
if !subs.nil?
|
121
|
+
files.merge!(subs)
|
122
|
+
end
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
return files
|
127
|
+
end
|
128
|
+
|
129
|
+
def Ripple.sync(path = "")
|
130
|
+
conf = Ripple.loadConfiguration(path)
|
131
|
+
begin
|
132
|
+
session, client, dropboxKeys = Ripple.connectToDropbox(path)
|
133
|
+
rescue DropboxAuthError
|
134
|
+
puts "Dropbox authorization failed."
|
135
|
+
Ripple.cleanup(conf, dropboxKeys, path)
|
136
|
+
return
|
137
|
+
rescue NameError
|
138
|
+
puts "Destination does not exist."
|
139
|
+
Ripple.cleanup(conf, dropboxKeys, path)
|
140
|
+
return
|
141
|
+
end
|
142
|
+
|
143
|
+
if session.nil?
|
144
|
+
puts "Could not connect to Dropbox."
|
145
|
+
Ripple.cleanup(conf, dropboxKeys, path)
|
146
|
+
return
|
147
|
+
end
|
148
|
+
|
149
|
+
destDir = conf[:destinationDir]
|
150
|
+
|
151
|
+
fileState = {}
|
152
|
+
oldFileState = dropboxKeys[:files]
|
153
|
+
files = Ripple.walkDropbox(client, '/', fileState, oldFileState)
|
154
|
+
|
155
|
+
if !files.nil?
|
156
|
+
files.keys.each { |x|
|
157
|
+
file = client.get_file(x)
|
158
|
+
File.open(File.join(destDir, x), 'w') {|f| f.puts file}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
conf[:dropbox][:sync].each { |ii|
|
163
|
+
Dir.glob(File.join(destDir, ii)).each { |oo|
|
164
|
+
if !File.directory?(oo)
|
165
|
+
upName = Pathname.new(oo).relative_path_from(Pathname.new(destDir)).to_s
|
166
|
+
upName = File.join("", upName)
|
167
|
+
if files.nil? or !files.has_key?(upName)
|
168
|
+
up = File.open(oo)
|
169
|
+
client.put_file(upName, up, true)
|
170
|
+
up.close()
|
171
|
+
end
|
172
|
+
end
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
dropboxKeys[:files] = Ripple.walkDropbox(client, '/', fileState, {})
|
177
|
+
Ripple.cleanup(conf, dropboxKeys, path)
|
178
|
+
|
179
|
+
return true
|
180
|
+
end
|
181
|
+
end
|
data/lib/ripple.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ripple/base'
|
Binary file
|
data/ripple.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "lib/ripple/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rypple"
|
7
|
+
s.version = Ripple::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jason Ziglar"]
|
10
|
+
s.email = ["jasedit@catexia.com"]
|
11
|
+
s.homepage = "https://github.com/jasedit/ripple"
|
12
|
+
s.summary = %q{Dropbox interface for jekyll.}
|
13
|
+
s.description = %q{A gem providing a Dropbox syncing interface for jekyll, along with a cgi file to update jekyll.}
|
14
|
+
|
15
|
+
s.add_runtime_dependency "jekyll"
|
16
|
+
s.add_runtime_dependency "dropbox-sdk"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
end
|
data/ripple.yaml
ADDED
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rypple
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jason Ziglar
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-27 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: jekyll
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: dropbox-sdk
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: A gem providing a Dropbox syncing interface for jekyll, along with a cgi file to update jekyll.
|
49
|
+
email:
|
50
|
+
- jasedit@catexia.com
|
51
|
+
executables:
|
52
|
+
- rippleInstall.rb
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- Gemfile
|
59
|
+
- README
|
60
|
+
- Rakefile
|
61
|
+
- bin/rippleInstall.rb
|
62
|
+
- lib/ripple.rb
|
63
|
+
- lib/ripple/base.rb
|
64
|
+
- lib/ripple/version.rb
|
65
|
+
- pkg/ripple-0.0.1.gem
|
66
|
+
- ripple.gemspec
|
67
|
+
- ripple.yaml
|
68
|
+
homepage: https://github.com/jasedit/ripple
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
hash: 3
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
requirements: []
|
95
|
+
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.8.17
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: Dropbox interface for jekyll.
|
101
|
+
test_files: []
|
102
|
+
|