ubiquitously 0.0.1.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +224 -19
- data/Rakefile +12 -4
- data/bin/u.me +4 -0
- data/lib/ubiquitously.rb +56 -7
- data/lib/ubiquitously/commands/base.rb +118 -0
- data/lib/ubiquitously/commands/post.rb +108 -0
- data/lib/ubiquitously/commands/user.rb +111 -0
- data/lib/ubiquitously/extensions/callbacks.rb +62 -0
- data/lib/ubiquitously/extensions/core.rb +88 -0
- data/lib/ubiquitously/extensions/mechanize.rb +35 -0
- data/lib/ubiquitously/models/base.rb +18 -0
- data/lib/ubiquitously/models/page.rb +28 -0
- data/lib/ubiquitously/models/post.rb +66 -0
- data/lib/ubiquitously/models/service.rb +1 -0
- data/lib/ubiquitously/models/service/account.rb +57 -0
- data/lib/ubiquitously/models/service/account/authorizable.rb +58 -0
- data/lib/ubiquitously/models/service/account/loggable.rb +50 -0
- data/lib/ubiquitously/models/service/account/restful.rb +21 -0
- data/lib/ubiquitously/models/service/post.rb +59 -0
- data/lib/ubiquitously/models/service/post/loggable.rb +64 -0
- data/lib/ubiquitously/models/service/post/ownable.rb +31 -0
- data/lib/ubiquitously/models/service/post/postable.rb +73 -0
- data/lib/ubiquitously/models/service/post/restful.rb +52 -0
- data/lib/ubiquitously/models/user.rb +128 -0
- data/lib/ubiquitously/services/blogger_den.rb +68 -0
- data/lib/ubiquitously/services/chetzit.rb +37 -0
- data/lib/ubiquitously/services/code_project.rb +21 -0
- data/lib/ubiquitously/services/delicious.rb +54 -0
- data/lib/ubiquitously/services/design_bump.rb +41 -0
- data/lib/ubiquitously/services/designgizer.rb +21 -0
- data/lib/ubiquitously/services/designr_fix.rb +21 -0
- data/lib/ubiquitously/services/digg.rb +151 -0
- data/lib/ubiquitously/services/digzign.rb +25 -0
- data/lib/ubiquitously/services/diigo.rb +68 -0
- data/lib/ubiquitously/services/dzone.rb +132 -0
- data/lib/ubiquitously/services/dzone_snippets.rb +39 -0
- data/lib/ubiquitously/services/facebook.rb +42 -0
- data/lib/ubiquitously/services/faves.rb +81 -0
- data/lib/ubiquitously/services/favshare.rb +78 -0
- data/lib/ubiquitously/services/flikode.rb +38 -0
- data/lib/ubiquitously/services/friendfeed.rb +21 -0
- data/lib/ubiquitously/services/gist.rb +52 -0
- data/lib/ubiquitously/services/google_buzz.rb +15 -0
- data/lib/ubiquitously/services/identica.rb +15 -0
- data/lib/ubiquitously/services/jump_tags.rb +21 -0
- data/lib/ubiquitously/services/kailoon.rb +27 -0
- data/lib/ubiquitously/services/meta_filter.rb +26 -0
- data/lib/ubiquitously/services/mixx.rb +94 -0
- data/lib/ubiquitously/services/mmo.rb +22 -0
- data/lib/ubiquitously/services/mvc_forge.rb +37 -0
- data/lib/ubiquitously/services/my_space.rb +25 -0
- data/lib/ubiquitously/services/nestdev.rb +36 -0
- data/lib/ubiquitously/services/newsvine.rb +76 -0
- data/lib/ubiquitously/services/pixel_groovy.rb +22 -0
- data/lib/ubiquitously/services/posterous.rb +24 -0
- data/lib/ubiquitously/services/programmers_heaven.rb +21 -0
- data/lib/ubiquitously/{propeller.rb → services/propeller.rb} +17 -26
- data/lib/ubiquitously/services/queness.rb +38 -0
- data/lib/ubiquitously/services/reddit.rb +62 -0
- data/lib/ubiquitously/services/scribd.rb +21 -0
- data/lib/ubiquitously/services/script_and_style.rb +23 -0
- data/lib/ubiquitously/services/sharebrain.rb +19 -0
- data/lib/ubiquitously/services/shoutwire.rb +34 -0
- data/lib/ubiquitously/services/slideshare.rb +70 -0
- data/lib/ubiquitously/services/smipple.rb +21 -0
- data/lib/ubiquitously/services/snipplr.rb +67 -0
- data/lib/ubiquitously/services/snipt.rb +49 -0
- data/lib/ubiquitously/services/sphinn.rb +24 -0
- data/lib/ubiquitously/services/stumble_upon.rb +67 -0
- data/lib/ubiquitously/services/stumpedia.rb +13 -0
- data/lib/ubiquitously/services/tumblr.rb +28 -0
- data/lib/ubiquitously/services/tweako.rb +53 -0
- data/lib/ubiquitously/services/twitter.rb +60 -0
- data/lib/ubiquitously/services/web_blend.rb +25 -0
- data/lib/ubiquitously/services/webdev5.rb +32 -0
- data/lib/ubiquitously/services/who_freelance.rb +21 -0
- data/lib/ubiquitously/services/wordtaps.rb +36 -0
- data/lib/ubiquitously/services/yahoo_buzz.rb +64 -0
- data/lib/ubiquitously/services/youblr.rb +15 -0
- data/lib/ubiquitously/services/zabox.rb +22 -0
- data/lib/ubiquitously/support/active_record.rb +25 -0
- data/lib/ubiquitously/support/storage.rb +71 -0
- data/test/config/cookies.yml +111 -0
- data/test/config/credentials.yml +7 -0
- data/test/config/secrets.yml +33 -0
- data/test/config/tokens.yml +30 -0
- data/test/meta.html +19 -0
- data/test/test_delicious.rb +37 -0
- data/test/test_digg.rb +57 -0
- data/test/test_dzone.rb +23 -18
- data/test/test_facebook.rb +40 -0
- data/test/test_faves.rb +18 -20
- data/test/test_helper.rb +8 -1
- data/test/test_newsvine.rb +3 -3
- data/test/test_page.rb +21 -0
- data/test/test_post.rb +55 -0
- data/test/test_propeller.rb +2 -2
- data/test/test_reddit.rb +26 -4
- data/test/test_stumble_upon.rb +23 -3
- data/test/test_twitter.rb +33 -0
- data/test/test_user.rb +22 -0
- metadata +199 -24
- data/lib/ext.rb +0 -37
- data/lib/ubiquitously/base.rb +0 -56
- data/lib/ubiquitously/dzone.rb +0 -111
- data/lib/ubiquitously/faves.rb +0 -43
- data/lib/ubiquitously/mister_wong.rb +0 -11
- data/lib/ubiquitously/mixins/resourceful.rb +0 -50
- data/lib/ubiquitously/mixx.rb +0 -66
- data/lib/ubiquitously/newsvine.rb +0 -54
- data/lib/ubiquitously/reddit.rb +0 -46
- data/lib/ubiquitously/sphinn.rb +0 -11
- data/lib/ubiquitously/stumble_upon.rb +0 -61
- data/test/config.yml +0 -18
data/README.markdown
CHANGED
@@ -14,37 +14,242 @@ Fill out `test/config.yml` with your credentials for the different services, the
|
|
14
14
|
|
15
15
|
rake test
|
16
16
|
|
17
|
-
### Register for the services you haven't already
|
18
|
-
|
19
|
-
First edit the config file with a username, password, and other fields that all the systems might have. Then run this
|
20
|
-
|
21
|
-
rake ubiquitously:me
|
22
|
-
|
23
17
|
### Automatically Post to Services
|
24
18
|
|
25
19
|
require 'rubygems'
|
26
20
|
require 'ubiquitously'
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
22
|
+
user = Ubiquitously::User.new(
|
23
|
+
:username => "viatropos",
|
24
|
+
:name => "Lance Pollard",
|
25
|
+
:email => "lancejpollard@gmail.com",
|
26
|
+
:cookies_path => "_cookies.yml"
|
27
|
+
)
|
28
|
+
post = Ubiquitously::Post.new(
|
29
|
+
:url => "http://postable.com",
|
30
|
+
:title => "An Interesting Post!",
|
31
|
+
:description => "This is one of those rare things on the web...",
|
32
|
+
:tags => ["writing", "programming", "javascript", "html", "ruby on rails"],
|
33
|
+
:user => user
|
33
34
|
)
|
35
|
+
post.save("stumbleupon", "delicious", "dzone", "digg", "diigo", "reddit", "tumblr", "mixx")
|
36
|
+
|
37
|
+
## Command-line
|
38
|
+
|
39
|
+
You can post to any of these services (even via oauth) completely from the command line.
|
40
|
+
|
41
|
+
u.me post twitter "Check out xyz!"
|
42
|
+
|
43
|
+
The anatomy of a command is this:
|
44
|
+
|
45
|
+
u.me post [services] [title] [options]
|
46
|
+
|
47
|
+
The `[services]` are a list of services you want this command to post to, and the `[title]` is whatever follows (in single/double quotes). The following will post to Twitter, Tumblr, and Posterous:
|
48
|
+
|
49
|
+
u.me post twitter tumblr posterous "Managing multiple interfaces"
|
34
50
|
|
51
|
+
You can get more precise by specifying options:
|
52
|
+
|
53
|
+
- `-l`: label, aka the Post "title"
|
54
|
+
- `-d`: description
|
55
|
+
- `-u`: url
|
56
|
+
- `-t`: tags (`comma,separated,without,any-spaces`)
|
57
|
+
- `-U`: username for the service
|
58
|
+
- `-P`: password for the service
|
59
|
+
|
60
|
+
So you can write a command like this:
|
61
|
+
|
62
|
+
u.me post digg "Oauth on the Command line!?" -d "You can now use Oauth from the command line" -t "oauth, ruby, unix" -u "http://ubiquitously.me"
|
63
|
+
|
64
|
+
Ubiquitously will save your login data (cookies, secrets, tokens, and params) in `~/.u.me`. You can open those files and edit them (add passwords, app keys, etc.) with these commands:
|
65
|
+
|
66
|
+
u.me cookies
|
67
|
+
u.me secrets
|
68
|
+
u.me tokens
|
69
|
+
u.me params
|
70
|
+
|
71
|
+
## How it thinks
|
72
|
+
|
73
|
+
It treats everything as a `Post`. A post has these properties:
|
74
|
+
|
75
|
+
- `title`: Name of the post/file/content
|
76
|
+
- `url`: Url to the post
|
77
|
+
- `description`: Description/summary/comment for the post. Ranges from 50-1000 characters. Most services only allow plain, but some (code snippets, and tumblr) allow html or code.
|
78
|
+
- `tags`: Array of lowercased tags. Formats tags how services want them (e.g. comma separated vs. dashed). Applies tag limits if necessary. These are user-defined.
|
79
|
+
- `categories`: Category for the post. Most services allow 1 category, some up to 4. These are specific to the service.
|
80
|
+
- `vote`: Vote for the post, either "up", "down", or "none" (or 1, 0, -1). Only if applicable to the service.
|
81
|
+
- `rating`: Rating for the post, 1-5, if applicable.
|
82
|
+
- `privacy`: Whether the service is public or private (0 || "public", 1 || "private")
|
83
|
+
- `state`: Status of the post, either "published", "draft", "submitted", or "queued".
|
84
|
+
- `kind`: The type of post, either post, link, quote, comment, image, video, audio, or answer.
|
85
|
+
- `format`: The format of your post content, either "plain", "html", "markdown", "textile", or any code (ruby, c, php, etc.).
|
86
|
+
- `service`: This is generated from the class, e.g. "digg".
|
87
|
+
- `service_url`: The url the service provides to the post.
|
88
|
+
|
89
|
+
Each service requires different things, which are determined by the `validates_presence_of` declaration for each class. Conceptually, you can divide the services into these categories:
|
90
|
+
|
91
|
+
1. Microblogging (twitter, yahoo meme, google buzz, identica)
|
92
|
+
2. Tumblelogging (tumblr, posterous)
|
93
|
+
3. Blogging (large posts)
|
94
|
+
4. Bookmarking (delicious, diigo, mixx, newsvine, reddit, digg...)
|
95
|
+
5. Code Sharing (gist, snipplr, dzone snippets, snipt, flikode, smipple)
|
96
|
+
|
35
97
|
## How it works
|
36
98
|
|
37
|
-
Everything is built around [Mechanize](http://mechanize.rubyforge.org/mechanize/GUIDE_rdoc.html) and [Nokogiri](http://nokogiri.org/tutorials/parsing_an_html_xml_document.html), both led by [Aaron Patterson](http://tenderlovemaking.com/).
|
99
|
+
Everything is built around [Mechanize](http://mechanize.rubyforge.org/mechanize/GUIDE_rdoc.html) and [Nokogiri](http://nokogiri.org/tutorials/parsing_an_html_xml_document.html), both led by [Aaron Patterson](http://tenderlovemaking.com/) (watch [Yehuda's "How to do the Impossible video"](http://www.youtube.com/watch?v=mo-lMdQMsdw)).
|
38
100
|
|
39
101
|
Many social bookmarking services do not have API's in order to prevent spammers from ruining their system. But what about for those of us that actually create content several times a day and want automation? We're out of luck.
|
40
102
|
|
41
103
|
So Ubiquitously creates a simple, RESTful API around some services I need to publish to now, and hopefully it will grow as you guys need more. The goal is to be semi-low-level and not to provide a full featured api to a service, as some services already have very well-done API's in Ruby.
|
42
104
|
|
43
|
-
##
|
105
|
+
## Why
|
106
|
+
|
107
|
+
Currently there's plenty of services to post about yourself: ping.fm, onlywire, posterous... But what if you want to do the same for other people? You're left having to go to the page and click one of their social buttons, and filling out the form. No way I'm filling out more than one for a page. This solves that problem, making it so you can post someone else's site to a million places just like yours, helping you build a community.
|
108
|
+
|
109
|
+
## What you should be doing...
|
110
|
+
|
111
|
+
### Posting in Forums
|
112
|
+
|
113
|
+
- Comment in the forums
|
114
|
+
|
115
|
+
### Adding to Article Directories
|
116
|
+
|
117
|
+
- Delicious
|
118
|
+
|
119
|
+
### Guest Posting
|
120
|
+
|
121
|
+
- Envato
|
122
|
+
|
123
|
+
### Publishing Documents
|
124
|
+
|
125
|
+
- Scribd
|
126
|
+
|
127
|
+
### Contributing Code
|
128
|
+
|
129
|
+
- snippets, github
|
130
|
+
|
131
|
+
### Sharing and Tagging
|
132
|
+
|
133
|
+
- Share others' content to stumbleupon, delicious, digg, diigo, reddit, tumblr, twitter, mixx, and identica.
|
134
|
+
- Tag other's posts on stumbleupon, delicious, digg, diigo, and reddit.
|
135
|
+
- Stumble other peoples posts that link to you. You'll benefit them and yourself.
|
136
|
+
- Write 2-3 sentences to describe each thing you post. So if you can share 10 things a day, that's 20-30 sentences.
|
137
|
+
|
138
|
+
### Commenting on Blogs
|
139
|
+
|
140
|
+
- Comment on other people's blogs
|
141
|
+
|
142
|
+
## What you should _not_ be doing...
|
143
|
+
|
144
|
+
### Self Promoting
|
145
|
+
|
146
|
+
- Only posting your content, you will get banned from the sites.
|
147
|
+
|
148
|
+
### Spamming
|
149
|
+
|
150
|
+
- Posting just the default description to all the sites
|
151
|
+
- Posting too much
|
152
|
+
|
153
|
+
## Tips and Resources
|
154
|
+
|
155
|
+
- [http://www.highrevenue.com/free-website-traffic/critique-of-50-top-ways-to-drive-traffic-to-your-site](http://www.highrevenue.com/free-website-traffic/critique-of-50-top-ways-to-drive-traffic-to-your-site)
|
156
|
+
- [http://www.highrevenue.com/linkbuilding-techniques/make-link-building-the-cornerstone-of-you-daily-activities](http://www.highrevenue.com/linkbuilding-techniques/make-link-building-the-cornerstone-of-you-daily-activities)
|
157
|
+
- [http://hubpages.com/hub/viatropos](http://hubpages.com/hub/viatropos)
|
158
|
+
- [http://www.mybloglog.com/](http://www.mybloglog.com/)
|
159
|
+
- [http://www.doshdosh.com/a-comprehensive-guide-to-stumbleupon-how-to-build-massive-traffic-to-your-website-and-monetize-it/](http://www.doshdosh.com/a-comprehensive-guide-to-stumbleupon-how-to-build-massive-traffic-to-your-website-and-monetize-it/)
|
160
|
+
- [http://www.thedesigncubicle.com/2009/12/websites-to-submit-your-design-articles-that-produce-heavy-traffic/](http://www.thedesigncubicle.com/2009/12/websites-to-submit-your-design-articles-that-produce-heavy-traffic/)
|
161
|
+
- [http://www.warriorforum.com/](http://www.warriorforum.com/)
|
162
|
+
- [http://lifehacker.com/5509815/how-to-declutter-and-streamline-your-google-reader-inbox](http://lifehacker.com/5509815/how-to-declutter-and-streamline-your-google-reader-inbox)
|
163
|
+
- [http://www.netvibes.com/](http://www.netvibes.com/)
|
164
|
+
- [http://www.noodlesoft.com/hazel.php](http://www.noodlesoft.com/hazel.php)
|
165
|
+
- [http://www.webdesignerdepot.com/2010/07/30-places-to-submit-your-website-designs/](http://www.webdesignerdepot.com/2010/07/30-places-to-submit-your-website-designs/)
|
166
|
+
- [http://knowem.com/](http://knowem.com/)
|
167
|
+
- [hriscredendino.com/](hriscredendino.com/)
|
168
|
+
- [http://www.networkworld.com/news/2010/080210-social-media-sucks-up-23.html](http://www.networkworld.com/news/2010/080210-social-media-sucks-up-23.html)
|
169
|
+
- [http://opensource.newscloud.com](http://opensource.newscloud.com)
|
170
|
+
- [http://www.doshdosh.com/list-of-social-media-news-websites/](http://www.doshdosh.com/list-of-social-media-news-websites/)
|
171
|
+
- [http://popurls.com/](http://popurls.com/)
|
172
|
+
- [http://www.webdesignerdepot.com/2010/07/30-places-to-submit-your-website-designs/](http://www.webdesignerdepot.com/2010/07/30-places-to-submit-your-website-designs/)
|
173
|
+
- [http://www.shareaholic.com/](http://www.shareaholic.com/)
|
174
|
+
|
175
|
+
> Linkbuilding is not a sprint... it is a MARATHON!
|
176
|
+
|
177
|
+
1. Get a bunch of feeds
|
178
|
+
2. Read the feeds
|
179
|
+
3. Share the feeds
|
180
|
+
|
181
|
+
## Todo
|
182
|
+
|
183
|
+
- find most appropriate tags for each service
|
184
|
+
- slideshare
|
185
|
+
- if submission already exists (digg, dzone, reddit...), then make description a comment and digg it.
|
186
|
+
- validations for each service (description wordcount, num tags, categories, title)
|
187
|
+
- who can post (is it for just me or for other people)?
|
188
|
+
- programmers_heaven, meta_filter, propeller, sharebrain, shoutwire, sphinn, stumpedia, web_blend, who_freelance, youblr, zabox
|
189
|
+
- Find image on page for display
|
190
|
+
- logging at stages in the process
|
191
|
+
- addthis-like javascript bookmarking tool
|
192
|
+
- flow:
|
193
|
+
- content type: video, image, post, status
|
194
|
+
- edit
|
195
|
+
- sumbit
|
196
|
+
- each needs to check to see if post already exists
|
197
|
+
- handle categories in a generic way
|
198
|
+
- handle captchas
|
199
|
+
- if this is actually useful, maybe creating accounts programmatically
|
200
|
+
- optimize login, i could probably skip the entire "parse form" section and just do a raw post.
|
201
|
+
- I don't want to support _every_ service because most of them are spammy or are not used much lately. Let's keep it clean here. Some services are also invite only and are in very very specific niches outside my realm. If you would like to support them, feel free to fork and customize.
|
202
|
+
|
203
|
+
<cite>copyright @viatropos 2010</cite>
|
204
|
+
|
205
|
+
## Future additions
|
206
|
+
|
207
|
+
- http://www.noupe.com/
|
208
|
+
- mashable
|
209
|
+
- smashing-magazine
|
210
|
+
- tripwire
|
211
|
+
- http://www.instantshift.com/user-submitted-news/
|
212
|
+
- http://designm.ag/submit-news/
|
213
|
+
- http://speckyboy.com/designnews/design-news-submission-form/
|
214
|
+
- http://net.tutsplus.com/link-feed/#add
|
215
|
+
- http://qik.com/
|
216
|
+
- http://www.associatedcontent.com/
|
217
|
+
- http://inboundmarketing.com
|
218
|
+
- http://clipmarks.com
|
219
|
+
- http://www.addurl.nu/
|
220
|
+
- http://blog.sponsoredreviews.com/?p=47
|
221
|
+
- http://www.metamags.com/
|
222
|
+
- http://www.developersniche.com/
|
223
|
+
- http://add.io/
|
224
|
+
- http://www.arto.com/section/frontpage/
|
225
|
+
- https://www.google.com/bookmarks/l
|
226
|
+
- amazon wishlist
|
227
|
+
- netvibes
|
228
|
+
- evernote
|
229
|
+
- http://www.evri.com/
|
230
|
+
- http://pinboard.in
|
231
|
+
- http://weheartit.com/about
|
232
|
+
- http://www.soup.io/
|
233
|
+
- http://izeby.com/
|
234
|
+
- http://linkmarking.com/
|
235
|
+
- yahoo meme
|
236
|
+
- reddit, digg, diigo, and mixx still don't work
|
237
|
+
- diigo doesn't work because cookie expires. create mechanize to re-login if cookie doesn't work
|
238
|
+
- evernote
|
239
|
+
- http://www.rubyflow.com
|
240
|
+
|
241
|
+
## Lists
|
242
|
+
|
243
|
+
- http://www.digalist.com
|
244
|
+
- listiki.com
|
245
|
+
- tipbo.com
|
246
|
+
|
247
|
+
## Tips from out there
|
248
|
+
|
249
|
+
Ask yourself the following questions about the linkability of your content:
|
250
|
+
|
251
|
+
- Am I Digging this because I want others to see it, and pressing a single button is an easy way for me to say "this is cool?"
|
252
|
+
- Or am I Digging this because People need to know about this, because they can’t live without it or because without it they would be out of the loop.
|
253
|
+
- Is it possible that my information is so new and/or informative that I will actually become an authority on this information, or serve as a reference for others interested in writing about similar content?
|
44
254
|
|
45
|
-
|
46
|
-
- buzz.yahoo.com
|
47
|
-
- http://www.wikio.com/about-us
|
48
|
-
- http://designbump.com/
|
49
|
-
- http://scriptandstyle.com/submit
|
50
|
-
- http://www.stumpedia.com/submitlink.php
|
255
|
+
> It is only one step more to make it so we fully create all the user accounts if they don't exist on all the services
|
data/Rakefile
CHANGED
@@ -5,18 +5,26 @@ require 'rake/gempackagetask'
|
|
5
5
|
# http://docs.rubygems.org/read/chapter/20
|
6
6
|
spec = Gem::Specification.new do |s|
|
7
7
|
s.name = "ubiquitously"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.1.0"
|
9
9
|
s.author = "Lance Pollard"
|
10
|
-
s.summary = "Ubiquitously:
|
10
|
+
s.summary = "Ubiquitously: Social Publishing to everywhere from anywhere, even if there's no API"
|
11
11
|
s.homepage = "http://github.com/viatropos/ubiquitously"
|
12
12
|
s.email = "lancejpollard@gmail.com"
|
13
|
-
s.description = "
|
13
|
+
s.description = "Social Publishing Rubygem"
|
14
14
|
s.has_rdoc = true
|
15
15
|
s.rubyforge_project = "ubiquitously"
|
16
16
|
s.platform = Gem::Platform::RUBY
|
17
|
-
s.files = %w(README.markdown Rakefile init.rb MIT-LICENSE) + Dir["{lib,rails,test}/**/*"] - Dir["test/tmp"]
|
17
|
+
s.files = %w(README.markdown Rakefile init.rb MIT-LICENSE) + Dir["{lib,rails,test,bin}/**/*"] - Dir["test/tmp"]
|
18
18
|
s.require_path = "lib"
|
19
|
+
s.executables = ["u.me"]
|
20
|
+
s.add_dependency("json")
|
19
21
|
s.add_dependency("activesupport", ">= 2.3.5")
|
22
|
+
s.add_dependency("mechanize")
|
23
|
+
s.add_dependency("highline")
|
24
|
+
s.add_dependency("rack")
|
25
|
+
s.add_dependency("httparty")
|
26
|
+
s.add_dependency("activemodel")
|
27
|
+
s.add_dependency("passport")
|
20
28
|
end
|
21
29
|
|
22
30
|
Rake::GemPackageTask.new(spec) do |pkg|
|
data/bin/u.me
ADDED
data/lib/ubiquitously.rb
CHANGED
@@ -1,38 +1,70 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'open-uri'
|
3
3
|
require 'cgi'
|
4
|
+
require 'rack/utils'
|
4
5
|
require 'yaml'
|
5
6
|
require 'json'
|
6
|
-
require 'nokogiri'
|
7
7
|
require 'mechanize'
|
8
8
|
require 'highline/import'
|
9
9
|
require 'logger'
|
10
|
+
require 'httparty'
|
10
11
|
require 'active_support'
|
11
12
|
require 'active_model'
|
13
|
+
require 'passport'
|
12
14
|
|
13
15
|
this = File.dirname(__FILE__)
|
14
|
-
require "#{this}/ext"
|
15
|
-
Dir["#{this}/ubiquitously/mixins/*"].each { |c| require c }
|
16
|
-
require "#{this}/ubiquitously/base"
|
17
16
|
|
18
17
|
module Ubiquitously
|
19
18
|
class SettingsError < StandardError; end
|
20
19
|
class AuthenticationError < StandardError; end
|
21
20
|
class DuplicateError < StandardError; end
|
22
|
-
|
21
|
+
class RecordInvalid < StandardError; end
|
22
|
+
class CommandInvalid < StandardError; end
|
23
|
+
|
23
24
|
class << self
|
24
|
-
attr_accessor :config
|
25
|
+
attr_accessor :config, :logger
|
26
|
+
|
27
|
+
def run(args)
|
28
|
+
command = args.shift
|
29
|
+
if command =~ /(secrets|tokens|cookies|credentials)/
|
30
|
+
system("open", File.expand_path("~/.u.me/#{$1}.yml"))
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
unless command =~ /(?:post|user)/
|
34
|
+
message = "\nUbiquitously command must be for either 'post' or 'user', e.g.:\n"
|
35
|
+
message << "u.me post twitter 'Working with Rails today...'"
|
36
|
+
raise CommandInvalid.new(message)
|
37
|
+
end
|
38
|
+
"Ubiquitously::Command::#{command.camelize}".constantize.run(args)
|
39
|
+
end
|
25
40
|
|
26
41
|
def configure(value)
|
27
42
|
self.config = value.is_a?(String) ? YAML.load_file(value) : value
|
28
43
|
end
|
29
44
|
|
45
|
+
def logger
|
46
|
+
unless @logger
|
47
|
+
@logger = Logger.new(STDOUT)
|
48
|
+
@logger.level = Logger::INFO
|
49
|
+
end
|
50
|
+
|
51
|
+
@logger
|
52
|
+
end
|
53
|
+
|
54
|
+
def debug?
|
55
|
+
logger.debug?
|
56
|
+
end
|
57
|
+
|
30
58
|
def key(path)
|
31
59
|
result = self.config
|
32
60
|
path.to_s.split(".").each { |node| result = result[node.to_s] if result }
|
33
61
|
result.to_s
|
34
62
|
end
|
35
63
|
|
64
|
+
def include?(service)
|
65
|
+
services.include?(service)
|
66
|
+
end
|
67
|
+
|
36
68
|
def credentials(service)
|
37
69
|
result = key(service)
|
38
70
|
unless result && result.has_key?("key") && result.has_key?("secret")
|
@@ -40,7 +72,24 @@ module Ubiquitously
|
|
40
72
|
end
|
41
73
|
result
|
42
74
|
end
|
75
|
+
|
76
|
+
def services
|
77
|
+
Dir.entries(File.dirname(__FILE__) + '/ubiquitously/services')[2..-1].collect do |service|
|
78
|
+
service = File.basename(service).split(".").first
|
79
|
+
end
|
80
|
+
end
|
43
81
|
end
|
44
82
|
end
|
45
83
|
|
46
|
-
Dir["#{this}/ubiquitously/*"].each { |c| require c
|
84
|
+
Dir["#{this}/ubiquitously/extensions/*"].each { |c| require c }
|
85
|
+
Dir["#{this}/ubiquitously/models/*"].each { |c| require c unless File.directory?(c) }
|
86
|
+
Dir["#{this}/ubiquitously/services/*"].each { |c| require c unless File.directory?(c) }
|
87
|
+
Dir["#{this}/ubiquitously/support/*"].each { |c| require c unless File.directory?(c) }
|
88
|
+
Dir["#{this}/ubiquitously/commands/*"].each { |c| require c unless File.directory?(c) }
|
89
|
+
|
90
|
+
overrides = Dir["#{this}/ubiquitously/services/*"].map do |file|
|
91
|
+
name = File.basename(file).split(".").first.camelize
|
92
|
+
["Ubiquitously::#{name}::Account".constantize, "Ubiquitously::#{name}::Post".constantize]
|
93
|
+
end.flatten
|
94
|
+
|
95
|
+
SubclassableCallbacks.override(*overrides)
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Ubiquitously
|
2
|
+
module Command
|
3
|
+
class Base
|
4
|
+
class << self
|
5
|
+
def run(args)
|
6
|
+
new(args).run
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :services, :attributes
|
11
|
+
|
12
|
+
def write(to, content = "")
|
13
|
+
File.open(to, "w+") { |file| file.puts content }
|
14
|
+
end
|
15
|
+
|
16
|
+
def configure(folder)
|
17
|
+
Dir.mkdir(folder) unless File.exists?(folder)
|
18
|
+
secrets_path = File.join(folder, "secrets.yml")
|
19
|
+
tokens_path = File.join(folder, "tokens.yml")
|
20
|
+
|
21
|
+
# usernames and passwords for post services
|
22
|
+
unless File.exists?(secrets_path)
|
23
|
+
secrets = "# #{secrets_path}, in your home directory\n"
|
24
|
+
secrets << "# write your username and passwords for the different services\n"
|
25
|
+
secrets << "# as necessary.\n\n"
|
26
|
+
Ubiquitously.services.each do |service|
|
27
|
+
secrets << "#{service}:\n"
|
28
|
+
secrets << " key: your_username\n"
|
29
|
+
secrets << " secret: your_password\n"
|
30
|
+
end
|
31
|
+
write(secrets_path, secrets)
|
32
|
+
system("open", secrets_path)
|
33
|
+
puts "Please configure your service username and passwords and rerun your command."
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
|
37
|
+
Ubiquitously.configure(secrets_path)
|
38
|
+
|
39
|
+
# oauth keys and secrets from services
|
40
|
+
unless File.exists?(tokens_path)
|
41
|
+
tokens = "# #{tokens_path}, in your home directory\n"
|
42
|
+
tokens << "# Get app key/secrets from the oauth providers below\n"
|
43
|
+
tokens << "# and fill them out as desired.\n\n"
|
44
|
+
tokens << "services:\n"
|
45
|
+
Passport.services.each do |service|
|
46
|
+
tokens << " #{service}:\n"
|
47
|
+
tokens << " key: app_key\n"
|
48
|
+
tokens << " secrets: app_secret\n"
|
49
|
+
end
|
50
|
+
write(tokens_path, tokens)
|
51
|
+
system("open", tokens_path)
|
52
|
+
puts "Please configure your oauth keys and secrets and rerun your command."
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
|
56
|
+
Passport.configure(tokens_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def main_folder
|
60
|
+
File.expand_path("~/.u.me")
|
61
|
+
end
|
62
|
+
|
63
|
+
def show
|
64
|
+
tmp = File.join(main_folder, "post.yml")
|
65
|
+
if File.exists?(tmp)
|
66
|
+
post = IO.read(tmp)
|
67
|
+
else
|
68
|
+
post = ""
|
69
|
+
post << "services: \n"
|
70
|
+
post << "url: \n"
|
71
|
+
post << "title: \n"
|
72
|
+
post << "description: \n"
|
73
|
+
post << "tags: \n"
|
74
|
+
write(tmp, post)
|
75
|
+
end
|
76
|
+
system("open", tmp)
|
77
|
+
require 'timeout'
|
78
|
+
begin
|
79
|
+
Timeout::timeout(30) {
|
80
|
+
puts "Will timeout in 30 seconds. Press Enter when you're ready."
|
81
|
+
STDIN.gets.chomp
|
82
|
+
self.attributes = YAML.load_file(tmp).symbolize_keys
|
83
|
+
#File.delete(tmp) if File.exists?(tmp)
|
84
|
+
# Something that should be interrupted if it takes too much time...
|
85
|
+
}
|
86
|
+
rescue Exception => e
|
87
|
+
puts e.inspect
|
88
|
+
puts "Resubmit post with same command. Finished process executing to clean things up."
|
89
|
+
exit
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
def initialize(args)
|
95
|
+
configure(main_folder)
|
96
|
+
self.services = []
|
97
|
+
self.attributes = {}
|
98
|
+
show if args.blank?
|
99
|
+
self.services << args.shift while args.length > 0 && args.first !~ /^-/
|
100
|
+
self.services << attributes.delete(:services) if attributes.has_key?(:services)
|
101
|
+
self.attributes[:title] = self.services.pop unless Ubiquitously.include?(self.services.last)
|
102
|
+
self.attributes = parse_options(args, attributes)
|
103
|
+
|
104
|
+
self.attributes.each do |key, value|
|
105
|
+
self.send("#{key.to_s}=", value) if self.respond_to?(key)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_options(attributes)
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
def run
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|