Thanks for up vote for my previous post. I'll show you more detail of usage and the sneak a peek my project. Let me introduce my project first. I write my blog post and deploy at heroku. And I want to write markdown with great UI of Day One.app. Day One can drop a post to Dropbox directly. So I decided to use them. This is quick dirty code but you can start blogging.
Did you ready to use Dropbox::API? If you not, please read previous post first.
I don't want to install Dropbox.app so in this case I use iOS version. Please turn off iCloud sync, and turn on Dropbox, and check your Dropbox folder. I recommend to try with no posts.
Note: Backup your previous posts, they are stored at ~/Library/Mobile Documents/***~com~dayoneapp~dayone/Documents/Journal_dayone/entries
Did you notice all posts are uploaded at (Your_Dropbox_Dir)/Apps/Day One/Journal.dayone/entries/*.doentry
? If you read previous my post, you can't access to DayOne journal. We need change settings first. Go to My Apps and remove previous settings (we can't toggle access type). And create new one and choose Access is Full Dropbox. Yes, now you can access whole Dropbox directory.
Note: Don't forget to run rake dropbox:authorize
for new app.
And you need change your code. If you have config/env.yaml
DROPBOX_APP_MODE: dropbox
Or directly,
Dropbox::API::Config.mode = 'dropbox'
sandbox
is only within your App folder, dropbox
is whole Dropbox directory.
I prepared dirty code from gists. Please download this on any directory, run rspec --color dropbox-api_spec.rb
First spec must fail because your account name didn't set. Change your name and run rspec again, so your specs are passed.
The important thing is all data will be receive Hash
. Did you try Dropbox::API::Client#download
? API is not GUI App. You need receive data and write them. I'll show you how after.
Want to know more client.account
data? Comment out line 23, you can get all account data.
#<Dropbox::API::Object country="JP" display_name="Seiichi Yonezawa" email="[email protected]" quota_info=#<Dropbox::API::Object normal=*** quota=*** shared=0> referral_link="https://www.dropbox.com/referrals/***" uid=***>
This is very cool RSpec tip. You don't need to put p
onto your code anymore!
This is very basic tutorial so I didn't use Sinatra in this lesson. But in this pro tip, we create simple file uploader/downloader with API.
API does not have way to access your directory like Dir.chdir
. Please comment line 28 and run spec.
Did you pass the spec? (or not?) If you have a one or more posts, you will pass. Did you notice clients.ls
returns an Array? It is like a simple database. If you didn't pass 2nd spec, add any file.
Now you can add real code. Please add this code to your dropbox-api_spec.rb
class MyApp do
def initialize
@client = Dropbox::API::Client.new(:token => $config["DROPBOX_TOKEN"],:secret => $config["DROPBOX_SECRET"])
@posts = @client.ls
end
def lists_all_posts
@posts.length
end
end
And comment out 3rd pending, run your spec.
Did you pass your spec? What's happen? Don't worry this is no problem. I'm just kidding. Your code at @posts = @client.ls
does look up your top of directory. Now you should change like @posts = @client.ls "Apps/Day One/Journal.dayone/entries"
So you can pass all your specs?
Did you notice what's difference 2nd and 3rd specs? 2nd one means directly run Dropbox::API::Client#ls
but 3rd one is real ruby code and it must same value with 2nd spec. This is writing Test with RSpec I guess. (But I don't know more details about RSpec.)
Yep this is the end of basics. Let's work more codes.
We can access all posts. But how can we get specify post? It's little harder. Because DayOne journal file is .plist
as XML. It contains markdown post, date format, which has star or not… We need another Gem to solve this problem.
$ gem install plist
Thanks for the great library Plist Gem. It is very simple way to parse/generate plist file.
Add your dropbox-api_spec.rb to require 'plist'
. I believe RSpec can check to work your new gem. Please create a new file and save to new example.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<integer>1</integer>
<string>two</string>
<dict>
<key>c</key>
<integer>13</integer>
</dict>
</array>
</plist>
Did you check 4th spec was passed? Now we try to read from day one journal.
We use Dropbox::API::Client#download this time. This method bring data as not file, so you don't worry to duplicated files when you run spec. Now let's try. Please comment out 5th spec.
Every files on the dropbox has data like,
client_mtime="Tue, 22 Jan 2013 05:35:55 +0000" # date when file updated
modified="Tue, 22 Jan 2013 05:35:55 +0000" # date when file modified
path="/Apps/Day One/Journal.dayone/entries/00CE9B175F204D52998085D6C7BA7D5E.doentry" # path
You can get more info REST API reference.
My first fault was client_mtime
is not same file created_time
. And we can't sort every posts by this value. The only important thing is "path"
. This data is just Hash
. We use this value and get just top of doentry from the list.
Did you pass 5th spec? I know you couldn't. This is my second misunderstood. But we can get .doentry
path. Why?
The reason why Dropbox::API::Client#download doesn't know "/" (root) symbol. So we need remove it to pass the spec. Change your spec to
client.download(post["Path"].sub("/", ''))
Did you know this mean? It's simple. Only Path begins with "/", sub
remove first "/" only. If you use gsub("/", "")
this removes all "/" symbols from String.
Did you pass this time? If you use non-ASCII characters like Café
? , (I use Japanese usually) You can't get correct data. Day One use UTF-8. So we can solve put force_encoding("utf-8")
with this problem.
Let's grow real ruby code up.
def get_first_post
post = @client.download(@posts.first["path"].sub("/", ""))
Plist::parse_xml(post.force_encoding("utf-8"))
end
Yeah, MyApp#get_first_post is just Hash
. Now you can comment out 6th spec. You can get your content with only Ruby and Dropbox::API. Here's quick tip for you.
require 'redcarpet'
post = MyApp.new.get_first_post
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
markdown.render(post["Entry Text"])
That's the all folks! Thanks for reading stuff.
Want to know more? I'll show you one more thing.
If you don't have DayOne.app, you couldn't enjoy this post. But here is the way to create new one. I need to convert my blog posts as DayOne. I will describe Dropbox::API::Client#upload
This is my gists. You need to install DayOne CLI Tools.
My blog post files are simple. I manage all posts from Yajl-JSON and database file. If you use Jekyll/Octopress(I love their projects but I don't think this is good way to deploy Heroku), You should read Cameron Diagle's Blog.
You need to tell ruby to an array which contains all posts as hash.
# my hash looks like this
# [{"datetime":"2012-12-19 14:03:47 +0900","filename":"posts/cb4fe97da53534f69a850f83a0e1150f6acd7034.md","permalink":"blogging-with-sinatra","tags":["ruby","sinatra"],"title":"Blogging with sinatra"}]
hash = Yajl::Parser.new.parse(File.read('database.json'))
hash.each do |h|
if FileTest.exist?(h['filename'].sub(/^posts\//, ''))
system "dayone -d='#{h['datetime']}' new < #{h['filename'].sub(/^posts\//, '')}"
end
end
It means $ dayone -d=2012-12-19 new < "blog contents"
as loop. So ready to migration. We can put these generated files puts directly via browser, Dropbox.app. But I tried to put with API.
In conclusion, this doesn't mean nothing so I comment them out. But this code is still good for practice.
dayone = Dir.glob('*.doentry').sort_by! do |p|
foo = Plist::parse_xml(File.read(p))
foo["Creation Date"]
end
This is local. We can directly parse plist. and sort by real created date. dayone
has simple array.
dayone.each do |d|
client.upload(
'Apps/Day One/Journal.dayone/entries/' + File.basename(d),
File.read(d)
)
p "#{d} has completed."
sleep 1
end
Dropbox::API::Client#upload
needs two values. First one is the Path which you want to save. Another one is content. It tools long time. I put status message every post finished uploading.
But dropbox's mtime didn't sorted what I want.
I couldn't show you all of methods, but these are very simple and easy to use. But every download needs few seconds. You should contrive what you use your plan.
If you want to list all posts like sinatra, I recommend to make database file with YAML, JSON, or XML whatever. You should stored its path.