dribbble-bucket-sync 0.0.1
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/.gitignore +17 -0
- data/.rvmrc +12 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +54 -0
- data/Rakefile +2 -0
- data/bin/dribbble_bucket_sync +23 -0
- data/dribbble_bucket_sync.gemspec +21 -0
- data/lib/dribbble_bucket_sync.rb +65 -0
- data/lib/dribbble_bucket_sync/folder.rb +74 -0
- data/lib/dribbble_bucket_sync/version.rb +3 -0
- data/spec/dribbble_bucket_sync/folder_spec.rb +53 -0
- data/spec/dribbble_bucket_sync_spec.rb +16 -0
- data/spec/spec_helper.rb +2 -0
- metadata +111 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
if ! rvm list | grep -q ruby-1.9.3-p194 ; then
|
4
|
+
rvm install 1.9.3-p194
|
5
|
+
fi
|
6
|
+
|
7
|
+
rvm 1.9.3-p194@ryantownsend_dribbble_bucket_sync --create
|
8
|
+
|
9
|
+
if ! gem list | grep -q bundler ; then
|
10
|
+
gem install --no-ri --no-rdoc bundler
|
11
|
+
bundle install --without production
|
12
|
+
fi
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Ryan Townsend
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# DribbbleBucketSync
|
2
|
+
|
3
|
+
Dribbble doesn't offer a solution to backup or sync your buckets and their containing shots to your computer. This gem solves that.
|
4
|
+
|
5
|
+
You can sync to any local directory you wish, a folder will be created for each bucket, and the shots will be downloaded therein.
|
6
|
+
|
7
|
+
Upon subsequent syncing, the list of shots in a bucket will be downloaded, any missing shots will be downloaded, and any shots no longer in the bucket will be removed.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Note: requires Ruby 1.9
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem "dribbble-bucket-sync"
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install dribbble-bucket-sync
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
There is a command-line script to access the synchronisation:
|
28
|
+
|
29
|
+
$ dribbble_bucket_sync dribbble_username path/to/dir
|
30
|
+
|
31
|
+
Output should be like this:
|
32
|
+
|
33
|
+
$ dribbble_bucket_sync ryantownsend ~/dribbble
|
34
|
+
Starting synchronisation...
|
35
|
+
ryantownsend has 29 buckets.
|
36
|
+
Timelines has 2 shots.
|
37
|
+
Colour Palettes has 1 shots.
|
38
|
+
Synchronisation complete.
|
39
|
+
|
40
|
+
The syntax to run within your own app is fairly straight-forward:
|
41
|
+
|
42
|
+
require "dribbble_bucket_sync"
|
43
|
+
# create a location to store the files
|
44
|
+
directory = File.join(File.dirname(__FILE__), "data")
|
45
|
+
# start the synchronisation
|
46
|
+
DribbbleBucketSync.new("ryantownsend").sync_to(directory)
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
1. Fork it
|
51
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
52
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
53
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
54
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "dribbble_bucket_sync"
|
4
|
+
|
5
|
+
unless ARGV.size == 2
|
6
|
+
raise ArgumentError, "Must have 2 arguments: username and output directory"
|
7
|
+
end
|
8
|
+
|
9
|
+
username = ARGV[0]
|
10
|
+
directory = ARGV[1]
|
11
|
+
|
12
|
+
unless File.directory?(directory)
|
13
|
+
raise ArgumentError, "Directory does not exist: #{directory}"
|
14
|
+
end
|
15
|
+
|
16
|
+
puts "Starting synchronisation..."
|
17
|
+
|
18
|
+
# start the synchronisation
|
19
|
+
DribbbleBucketSync.new(username).sync_to(directory)
|
20
|
+
|
21
|
+
puts "Synchronisation complete."
|
22
|
+
|
23
|
+
system("open #{directory}")
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/dribbble_bucket_sync/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.authors = ["Ryan Townsend"]
|
6
|
+
s.email = ["ryan@ryantownsend.co.uk"]
|
7
|
+
s.description = %q{Synchronises your Dribbble buckets with a local folder}
|
8
|
+
s.summary = s.description
|
9
|
+
s.homepage = "https://github.com/ryantownsend/dribbble-bucket-sync"
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split($\)
|
12
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
14
|
+
s.name = "dribbble-bucket-sync"
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.version = DribbbleBucketSync::VERSION
|
17
|
+
|
18
|
+
s.add_dependency "dribbble-bucket-api"
|
19
|
+
s.add_development_dependency "rspec"
|
20
|
+
s.add_development_dependency "simplecov"
|
21
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "dribbble_bucket_api"
|
3
|
+
require "dribbble_bucket_sync/version"
|
4
|
+
require "dribbble_bucket_sync/folder"
|
5
|
+
|
6
|
+
class DribbbleBucketSync
|
7
|
+
attr_reader :username
|
8
|
+
|
9
|
+
def initialize(username)
|
10
|
+
@username = username
|
11
|
+
end
|
12
|
+
|
13
|
+
def sync_to(directory)
|
14
|
+
# ensure the directory exists
|
15
|
+
unless File.directory?(directory)
|
16
|
+
raise ArgumentError, "Sync destination must be a valid directory:\n#{directory}"
|
17
|
+
end
|
18
|
+
# get the first page
|
19
|
+
response = connection.buckets(page: 1)
|
20
|
+
# load the total pages
|
21
|
+
total_pages = response.total_pages
|
22
|
+
# return if total pages is none
|
23
|
+
return if total_pages < 1
|
24
|
+
# print how many buckets there are
|
25
|
+
puts "#{@username} has #{response.total_entries} buckets."
|
26
|
+
# loop through each page
|
27
|
+
1.upto(total_pages) do |page|
|
28
|
+
# load the buckets for this page
|
29
|
+
buckets = connection.buckets(page: page)
|
30
|
+
# sync each bucket
|
31
|
+
buckets.each do |bucket|
|
32
|
+
sync_bucket(bucket, directory)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def sync_bucket(bucket, directory)
|
39
|
+
# create the folder for the bucket
|
40
|
+
folder = Folder.new(directory, bucket.name)
|
41
|
+
# get the first page
|
42
|
+
response = bucket.shots(page: 1)
|
43
|
+
# load the total pages
|
44
|
+
total_pages = response.total_pages
|
45
|
+
# return if total pages is none
|
46
|
+
return if total_pages < 1
|
47
|
+
# print how many shots there are
|
48
|
+
puts "#{bucket.name} has #{response.total_entries} shots."
|
49
|
+
# loop through each page
|
50
|
+
1.upto(total_pages) do |page|
|
51
|
+
# load the buckets for this page
|
52
|
+
shots = bucket.shots(page: page)
|
53
|
+
# add each shot to the folder
|
54
|
+
shots.each do |shot|
|
55
|
+
folder.add_shot(shot)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
# remove old shots
|
59
|
+
folder.remove_old_shots
|
60
|
+
end
|
61
|
+
|
62
|
+
def connection
|
63
|
+
@connection = DribbbleBucketApi.connect(username: @username)
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "uri"
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
class DribbbleBucketSync
|
6
|
+
class Folder
|
7
|
+
attr_reader :path, :shots
|
8
|
+
|
9
|
+
def initialize(directory, name)
|
10
|
+
name = sanitize(name)
|
11
|
+
# store the path
|
12
|
+
@path = File.join(directory, name)
|
13
|
+
# ensure our folder exists
|
14
|
+
ensure_folder_exists
|
15
|
+
# create storage for the shots
|
16
|
+
@shots = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_shot(shot)
|
20
|
+
url = shot.image_url
|
21
|
+
# add the file to the shots list
|
22
|
+
filename = "#{shot.id}#{File.extname(url)}"
|
23
|
+
@shots << filename
|
24
|
+
# calculate the path
|
25
|
+
path = File.join(@path, filename)
|
26
|
+
# download if it doesn't exist
|
27
|
+
unless File.exist?(path)
|
28
|
+
download(url, path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove_old_shots
|
33
|
+
# loop through all shots in the folder
|
34
|
+
current_shot_files.each do |file|
|
35
|
+
# delete file if the shot isn't in the array
|
36
|
+
unless @shots.include?(File.basename(file))
|
37
|
+
FileUtils.rm(file)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# returns an array of existing shot files
|
43
|
+
def current_shot_files
|
44
|
+
Dir.glob(File.join(@path, "*.{jpg,jpeg,png,gif}"))
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
# downloads a URL to a local path
|
49
|
+
def download(url, destination)
|
50
|
+
# parse the URL
|
51
|
+
uri = URI.parse(url)
|
52
|
+
# connect to the site
|
53
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
54
|
+
# download the file
|
55
|
+
resp = http.get(uri.request_uri)
|
56
|
+
# open the local file
|
57
|
+
open(destination, "wb") do |file|
|
58
|
+
# write the data
|
59
|
+
file.write(resp.body)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# ensure the folder exists
|
65
|
+
def ensure_folder_exists
|
66
|
+
FileUtils.makedirs(@path)
|
67
|
+
end
|
68
|
+
|
69
|
+
# sanitizes the folder name
|
70
|
+
def sanitize(name)
|
71
|
+
name.downcase.gsub(/[^\w\-\s]/, "-").gsub(/[\-\_]+/, "-").gsub(/[\-\_]*$|^[\-\_]*/, "")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require_relative "../../lib/dribbble_bucket_sync/folder"
|
3
|
+
|
4
|
+
describe DribbbleBucketSync::Folder do
|
5
|
+
let(:directory) { File.join(File.dirname(__FILE__), "../tmp") }
|
6
|
+
let(:name) { "bucket-name" }
|
7
|
+
|
8
|
+
subject { DribbbleBucketSync::Folder.new(directory, name) }
|
9
|
+
|
10
|
+
after(:all) do
|
11
|
+
FileUtils.remove_dir(directory)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "::new" do
|
15
|
+
let(:name) { "_a;rand0m-bucKet-()$" }
|
16
|
+
|
17
|
+
it "should sanitize the name" do
|
18
|
+
folder = File.join(directory, "a-rand0m-bucket")
|
19
|
+
expect(subject.path).to eq(folder)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should create the folder" do
|
23
|
+
expect(File.directory?(subject.path)).to be_true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#add_shot" do
|
28
|
+
let(:url) { "http://example.com/file.jpg" }
|
29
|
+
let(:shot) { mock("shot", id: 123456, image_url: url) }
|
30
|
+
let(:destination) { File.join(subject.path, "#{shot.id}.jpg") }
|
31
|
+
|
32
|
+
it "should add the file to the shots" do
|
33
|
+
FileUtils.rm(destination) if File.exist?(destination)
|
34
|
+
subject.should_receive(:download).with(url, destination).and_return(true)
|
35
|
+
subject.add_shot(shot)
|
36
|
+
expect(subject.shots).to include("123456.jpg")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#remove_old_shots" do
|
41
|
+
let(:old_shot) { File.join(subject.path, "98765.png") }
|
42
|
+
|
43
|
+
before(:each) do
|
44
|
+
FileUtils.touch(old_shot)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should delete any shots no longer in the bucket" do
|
48
|
+
expect(File.exist?(old_shot)).to be_true
|
49
|
+
subject.remove_old_shots
|
50
|
+
expect(File.exist?(old_shot)).to be_false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fileutils"
|
3
|
+
require_relative "../lib/dribbble_bucket_sync"
|
4
|
+
|
5
|
+
describe DribbbleBucketSync do
|
6
|
+
subject { DribbbleBucketSync.new(username) }
|
7
|
+
|
8
|
+
let(:username) { "ryantownsend" }
|
9
|
+
|
10
|
+
describe "::new" do
|
11
|
+
it "should accept a username" do
|
12
|
+
expect(subject.username).to eq("ryantownsend")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dribbble-bucket-sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan Townsend
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: dribbble-bucket-api
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: simplecov
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Synchronises your Dribbble buckets with a local folder
|
63
|
+
email:
|
64
|
+
- ryan@ryantownsend.co.uk
|
65
|
+
executables:
|
66
|
+
- dribbble_bucket_sync
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- .rvmrc
|
72
|
+
- Gemfile
|
73
|
+
- LICENSE
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- bin/dribbble_bucket_sync
|
77
|
+
- dribbble_bucket_sync.gemspec
|
78
|
+
- lib/dribbble_bucket_sync.rb
|
79
|
+
- lib/dribbble_bucket_sync/folder.rb
|
80
|
+
- lib/dribbble_bucket_sync/version.rb
|
81
|
+
- spec/dribbble_bucket_sync/folder_spec.rb
|
82
|
+
- spec/dribbble_bucket_sync_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
homepage: https://github.com/ryantownsend/dribbble-bucket-sync
|
85
|
+
licenses: []
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.8.24
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: Synchronises your Dribbble buckets with a local folder
|
108
|
+
test_files:
|
109
|
+
- spec/dribbble_bucket_sync/folder_spec.rb
|
110
|
+
- spec/dribbble_bucket_sync_spec.rb
|
111
|
+
- spec/spec_helper.rb
|