cameraplus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +9 -0
  2. data/.rvmrc +55 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +22 -0
  5. data/README.md +227 -0
  6. data/Rakefile +2 -0
  7. data/cameraplus.gemspec +25 -0
  8. data/lib/cameraplus.rb +18 -0
  9. data/lib/cameraplus/api/page.rb +17 -0
  10. data/lib/cameraplus/api/request.rb +45 -0
  11. data/lib/cameraplus/api/search.rb +25 -0
  12. data/lib/cameraplus/api/user.rb +17 -0
  13. data/lib/cameraplus/comment.rb +14 -0
  14. data/lib/cameraplus/exceptions.rb +7 -0
  15. data/lib/cameraplus/page.rb +34 -0
  16. data/lib/cameraplus/page_metadata.rb +73 -0
  17. data/lib/cameraplus/photo.rb +49 -0
  18. data/lib/cameraplus/photo_exif.rb +13 -0
  19. data/lib/cameraplus/photo_recipe.rb +12 -0
  20. data/lib/cameraplus/search.rb +45 -0
  21. data/lib/cameraplus/user.rb +72 -0
  22. data/lib/cameraplus/version.rb +3 -0
  23. data/lib/core_ext/hash.rb +13 -0
  24. data/spec/cameraplus/api/page_spec.rb +17 -0
  25. data/spec/cameraplus/api/request_spec.rb +47 -0
  26. data/spec/cameraplus/api/search_spec.rb +37 -0
  27. data/spec/cameraplus/api/user_spec.rb +17 -0
  28. data/spec/cameraplus/comment_spec.rb +32 -0
  29. data/spec/cameraplus/page_metadata_spec.rb +117 -0
  30. data/spec/cameraplus/page_spec.rb +84 -0
  31. data/spec/cameraplus/photo_exif_spec.rb +23 -0
  32. data/spec/cameraplus/photo_recipe_spec.rb +19 -0
  33. data/spec/cameraplus/photo_spec.rb +82 -0
  34. data/spec/cameraplus/search_spec.rb +51 -0
  35. data/spec/cameraplus/user_spec.rb +82 -0
  36. data/spec/core_ext/hash_spec.rb +53 -0
  37. data/spec/spec_helper.rb +6 -0
  38. data/spec/vcr_cassettes/api_request_invalid.yml +56 -0
  39. data/spec/vcr_cassettes/api_request_valid.yml +78 -0
  40. data/spec/vcr_cassettes/more_results.yml +369 -0
  41. data/spec/vcr_cassettes/page.yml +58 -0
  42. data/spec/vcr_cassettes/search.yml +55 -0
  43. data/spec/vcr_cassettes/user.yml +78 -0
  44. data/spec/vcr_config.rb +7 -0
  45. metadata +177 -0
@@ -0,0 +1,9 @@
1
+ .DS_Store
2
+ .rspec
3
+ .config
4
+ .bundle
5
+ *.gem
6
+ Gemfile.lock
7
+ doc/
8
+ pkg/*
9
+ coverage
data/.rvmrc ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
+ environment_id="ruby-1.9.3-p0@cameraplus"
8
+
9
+ #
10
+ # Uncomment following line if you want options to be set only for given project.
11
+ #
12
+ # PROJECT_JRUBY_OPTS=( --1.9 )
13
+
14
+ #
15
+ # First we attempt to load the desired environment directly from the environment
16
+ # file. This is very fast and efficient compared to running through the entire
17
+ # CLI and selector. If you want feedback on which environment was used then
18
+ # insert the word 'use' after --create as this triggers verbose mode.
19
+ #
20
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
21
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
22
+ then
23
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
24
+
25
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
26
+ then
27
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
28
+ fi
29
+ else
30
+ # If the environment file has not yet been created, use the RVM CLI to select.
31
+ if ! rvm --create "$environment_id"
32
+ then
33
+ echo "Failed to create RVM environment '${environment_id}'."
34
+ return 1
35
+ fi
36
+ fi
37
+
38
+ #
39
+ # If you use an RVM gemset file to install a list of gems (*.gems), you can have
40
+ # it be automatically loaded. Uncomment the following and adjust the filename if
41
+ # necessary.
42
+ #
43
+ # filename=".gems"
44
+ # if [[ -s "$filename" ]]
45
+ # then
46
+ # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
47
+ # fi
48
+
49
+ # If you use bundler, this might be useful to you:
50
+ # if command -v bundle && [[ -s Gemfile ]]
51
+ # then
52
+ # bundle install
53
+ # fi
54
+
55
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Kevin Tuhumury
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.
@@ -0,0 +1,227 @@
1
+ # Cameraplus (Camera+)
2
+
3
+ This gem provides a Ruby wrapper around the Camera+ API, using HTTParty. [Camera+](http://camerapl.us) is an iPhone photo app made by [tap tap tap](http://taptaptap.com) with effects made by professional photographer Lisa Bettany. Cameraplus is based on the [Web Sharing API](http://api.campl.us/web-api).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cameraplus'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cameraplus
18
+
19
+ ## Cameraplus::API
20
+
21
+ `Cameraplus::API` is a very thin wrapper for the Camera+ API. It uses the bare minimum to get the diserable data from Camera+ and returns it as a Hash.
22
+
23
+ ### Cameraplus::API::User
24
+
25
+ Cameraplus::API::User.find("mostlylisa") # => Retrieves a hash with user data of Twitter user @mostlylisa (Twitter account of Lisa Bettany)
26
+
27
+ ### Cameraplus::API::Page
28
+
29
+ Cameraplus::API::Page.find("b72Z") # => Retrieves a hash with the metadata of the specified page
30
+
31
+ ## Cameraplus layer
32
+
33
+ The Cameraplus gem also provides a 'convenience' layer. This layer lies on top of `Cameraplus::API`, which only returns hashes. The `Cameraplus` layer has an easier interface which returns objects like `Cameraplus::User`, `Cameraplus::Page` and `Cameraplus::Photo`.
34
+
35
+ ### Retrieving a user with `Cameraplus::User`
36
+
37
+ A user can be retrieved with the following line of code:
38
+
39
+ @user = Cameraplus::User.find("mostlylisa")
40
+
41
+ The `@user` variable provides access to the following properties:
42
+
43
+ @user.id # => 6978642
44
+ @user.username # => mostlylisa (this is a Twitter username)
45
+ @user.name # => Lisa Bettany
46
+ @user.avatar # => http://a0.twimg.com/profile_images/1767370289/284161_10150711631895637_674215636_19658534_6246798_n_normal.jpeg
47
+ @user.page_count # => 122
48
+ @user.photo_count # => 876
49
+
50
+ #### Retrieving pages from a user
51
+
52
+ A list with pages is also included in the response of the above call:
53
+
54
+ @user.pages # => [ #<Cameraplus::Page:0x007fb5c2f91080>, #<Cameraplus::Page:0x007fb5c2f9e5f0>, ... ]
55
+
56
+ Yeah, it's that easy. The above returns an Array with `Cameraplus::Page` objects. Provided that you've only selected the first page with `@page = @user.pages.first`. The `@page` variable will now have the following properties:
57
+
58
+ @page.url # => http://campl.us/iao4
59
+ @page.created_at # => 2012-03-14 22:53:49
60
+ @page.location # => 45.43383498680353 12.34226474539595
61
+ @page.location_name # => Venice, Venice
62
+ @page.tweet_text # => Carnival in Venice, a travel photographer's dream shoot!
63
+ @page.tweet_id # => 180064216093437954 (the actual id on Twitter)
64
+ @page.view_count # => 2101
65
+ @page.comment_count # => 1
66
+
67
+ Assuming that you'll want to create a list of the pages of the specified user, the following can be done in Haml:
68
+
69
+ %ul
70
+ - @user.pages.each do |page|
71
+ %li
72
+ = "#{page.tweet_text} at #{page.location_name} on #{page.created_at.strftime("%d %b %Y")}"
73
+
74
+ This will generate a list of pages with a maximum of 50 (in case each page has a single photo). The number of pages depends on the number of photos a page has. In case a page has 50 photos, it will only return a single page.
75
+
76
+ #### Retrieving the photos from the pages
77
+
78
+ Just like the `Cameraplus::User` instance has access to it's pages, a `Cameraplus::Page` in turn has access to it's photos:
79
+
80
+ @page.photos # => [ #<Cameraplus::Photo:0x007fb5c2f90a90>, #<Cameraplus::Photo:0x007fb5c2f9b300>, ... ]
81
+
82
+ The above returns an Array with `Cameraplus::Photo` objects. Again, assuming that you've selected the first photo on the page with: `@photo = @page.photos.first`, it will have access to the below properties:
83
+
84
+ @photo.small # => http://pics.campl.us/t/0/0b497f01791c851db1a17f81e0621a5c.jpg
85
+ @photo.medium # => http://pics.campl.us/iphone/0/0b497f01791c851db1a17f81e0621a5c.jpg
86
+ @photo.large # => http://pics.campl.us/f/0/0b497f01791c851db1a17f81e0621a5c.jpg
87
+ @photo.width # => 800
88
+ @photo.height # => 590
89
+ @photo.location # => 45.43383498680353 12.34226474539595
90
+
91
+ In case you want to list all the large versions of the photos retrieved by the above call, you could do the following and loop through them afterwards:
92
+
93
+ @large_photos = @user.pages.map { |page| page.map(&:large) }.flatten
94
+
95
+ #### Retrieving the next set of pages
96
+
97
+ When a user has more pages than the results of `@user.pages`, the next set of pages can be retrieved with `@user.more_pages`. This doesn't only return the new set of pages though. It also retrieves the user data. The result is exactly the same as `@user` only with a new list of pages and calling `.more_pages` on the new object returns even more of them (if they exist).
98
+
99
+ ### PageMetadata
100
+
101
+ The Camera+ Web Sharing API doesn't return all the details of a page through the earlier mentioned API call, which is made by the `Cameraplus::API::User` class. The `Cameraplus::API::Page` class however does retrieve all of the desired data. The `Cameraplus::PageMetadata` class basically is a layer on top of that API class.
102
+
103
+ Retrieving metadata from the page is as easy as:
104
+
105
+ @metadata = Cameraplus::PageMetadata.find("b72Z")
106
+
107
+ The argument "b72Z" is the identifier of the page and is being used in the actual URL. The first page retrieved in 'Retrieving pages from a user' has the following url: 'http://campl.us/iao4', `iao4` in this url would be used as a parameter in the above call. The following properties can be accessed on a `Cameraplus::PageMetadata` instance:
108
+
109
+ @metadata.url # => http://campl.us/b72Z
110
+ @metadata.created_at # => 2011-07-01 21:50:40
111
+ @metadata.location # => nil
112
+ @metadata.location_name # => nil
113
+ @metadata.tweet_text # => Tiny Town of Kotor, Montenegro #mybestphoto
114
+ @metadata.tweet_id # => 86914646237384704 (the actual id on Twitter)
115
+ @metadata.view_count # => 925
116
+ @metadata.comment_count # => 6
117
+
118
+ As you can see, these properties are identical to the properties of a `Cameraplus::Page`, but it also adds a lot more.
119
+
120
+ #### Retrieving the user belonging to the page
121
+
122
+ The user who owns the page can be accessed with:
123
+
124
+ @user = @metadata.user
125
+
126
+ You might expect the same properties as you'll get from the previous user call, this isn't the case though. The response of this requested call doesn't include `#page_count` and `#photo_count`. This leaves you with the properties:
127
+
128
+ @user.id # => 14199276
129
+ @user.username # => lucyk
130
+ @user.name # => Oleg Lutsenko
131
+ @user.avatar # => http://a0.twimg.com/profile_images/1318866321/avatar1_sqr_normal.jpg
132
+
133
+ #### Retrieving the photos belonging to the page
134
+
135
+ All of the photos which are included on the specified page are presented to you by calling:
136
+
137
+ @photos = @metadata.photos
138
+
139
+ These photos have the same properties as mentioned previously with the addition of exif data and recipes. The basic `Cameraplus::Photo` object has these properties:
140
+
141
+ @photo.small # => http://pics.campl.us/t/f/f2cc65ab786741e7ab9fb994d06626a6.jpg
142
+ @photo.medium # => http://pics.campl.us/iphone/f/f2cc65ab786741e7ab9fb994d06626a6.jpg
143
+ @photo.large # => http://pics.campl.us/f/f/f2cc65ab786741e7ab9fb994d06626a6.jpg
144
+ @photo.width # => 800
145
+ @photo.height # => 597
146
+ @photo.location # => nil
147
+
148
+ The added functionality is described below.
149
+
150
+ ##### Retrieving the exif data from a photo
151
+
152
+ As mentioned in the previous part, a `Cameraplus::Photo` object has more than just the default properties. It also includes exif data of the actual photo. The data can be accessed as follows:
153
+
154
+ @exif_data = @photo.exif
155
+
156
+ The above will return an Array with `Cameraplus::PhotoExif` objects, selecting the first item of the Array (`@exif = @exif_data.first`) will return the following:
157
+
158
+ @exif.title # => Camera
159
+ @exif.value # => Apple iPhone
160
+ @exif.style # => main
161
+
162
+ Exif data can include options like: Camera (Apple iPhone 4s), Software (Camera+ 2.2.1), Exposure (1/580 sec), Aperture (f/2.4) and Focal length (3.9 mm).
163
+
164
+ ##### Retrieving the recipe from a photo
165
+
166
+ An instance of `Cameraplus::Photo` also has an Array called recipes with any of the effects, in the form of a `Cameraplus::PhotoRecipe` object, which were applied to it in the Camera+ iPhone app. A recipe has two properties:
167
+
168
+ @recipe.type # => fx
169
+ @recipe.value # => Miniaturize (80%)
170
+
171
+ The recipes of a photo can be accessed through: `@photo.recipes`.
172
+
173
+ #### Retrieving the comments belonging to the page
174
+
175
+ People can comment on the photo or photos you've taken. Those comments are made available through the API. Accessing them is done by calling:
176
+
177
+ @comments = @metadata.comments
178
+
179
+ The Array contains `Cameraplus::Comment` instances with these properties:
180
+
181
+ @comment.author # => Lisa Bettany
182
+ @comment.avatar # => https://fbcdn-profile-a.akamaihd.net/hprofile-ak-snc4/261022_674215636_4030749_q.jpg
183
+ @comment.url # => http://www.facebook.com/profile.php?id=674215636
184
+ @comment.text # => great shot!
185
+
186
+ Once again, the above comment was retrieved by accessing the first element of the Array.
187
+
188
+ ### Search
189
+
190
+ It's also possible to search for a user, pages or photos. There is a single call for this, which is:
191
+
192
+ @results = Cameraplus::Search.find username: "mostlylisa"
193
+
194
+ This will retrieve an Array with `Cameraplus::Search` objects. Each of these has three properties:
195
+
196
+ @result.page # => #<Cameraplus::Page:0x007fb5c2f91080>
197
+ @result.user # => #<Cameraplus::User:0x007fcfd54aec08>
198
+ @result.photos # => [ #<Cameraplus::Photo:0x007fb5c2f90a90>, #<Cameraplus::Photo:0x007fb5c2f9b300>, ... ]
199
+
200
+ Again, this is assuming that we've selected the first result with: `@result = @results.first`.
201
+
202
+ #### Possible arguments for a search
203
+
204
+ As seen in the previous call, a hash is required for a search. It needs at least one argument. The possible properties of the hash are: `username`, `tweettext`, `earliest`, `latest` and `locationname`. Below is list of these properties with it's types and descriptions:
205
+
206
+ username String Twitter username
207
+ tweettext String Tweet text partial search
208
+ earliest YYYY-MM-DD HH:MM Return only tweets on or after this date/time (UTC)
209
+ latest YYYY-MM-DD HH:MM Return only tweets on or before this date/time (UTC)
210
+ locationname String Location name string partial match. Note: location names are missing for pages created before March, 2011.
211
+
212
+ ## Contributing
213
+
214
+ You're very welcome to contribute to this gem. To do so, please follow these steps:
215
+
216
+ 1. Fork this project
217
+ 2. Clone your fork on your local machine
218
+ 3. Install the development dependencies with `bundle install`
219
+ 4. Create your feature branch with `git checkout -b my-new-feature`
220
+ 5. Run the specs with `rspec` and make sure everything is covered with RSpec
221
+ 6. Commit your changes `git commit -am 'Added new feature'`
222
+ 7. Push to your branch `git push origin my-new-feature`
223
+ 8. Create a new Pull Request
224
+
225
+ ## Copyright
226
+
227
+ Copyright 2012 Kevin Tuhumury. Released under the MIT License.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/cameraplus/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "cameraplus"
6
+ gem.version = Cameraplus::VERSION
7
+ gem.authors = ["Kevin Tuhumury"]
8
+ gem.email = ["kevin.tuhumury@gmail.com"]
9
+ gem.homepage = "http://github.com/kevintuhumury/cameraplus"
10
+ gem.summary = %q{A Ruby wrapper around the Camera+ (camerapl.us) API.}
11
+ gem.description = %q{This gem provides a Ruby wrapper around the Camera+ (camerapl.us) API for use in your own project.}
12
+
13
+ gem.files = `git ls-files`.split("\n")
14
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ gem.require_paths = ["lib"]
17
+
18
+ gem.add_runtime_dependency "httparty"
19
+
20
+ gem.add_development_dependency "vcr"
21
+ gem.add_development_dependency "rspec"
22
+ gem.add_development_dependency "webmock"
23
+ gem.add_development_dependency "simplecov"
24
+ gem.add_development_dependency "spec_coverage"
25
+ end
@@ -0,0 +1,18 @@
1
+ require "httparty"
2
+ require "core_ext/hash"
3
+
4
+ require "cameraplus/api/request"
5
+ require "cameraplus/api/user"
6
+ require "cameraplus/api/page"
7
+ require "cameraplus/api/search"
8
+
9
+ require "cameraplus/user"
10
+ require "cameraplus/page"
11
+ require "cameraplus/page_metadata"
12
+ require "cameraplus/photo"
13
+ require "cameraplus/photo_exif"
14
+ require "cameraplus/photo_recipe"
15
+ require "cameraplus/comment"
16
+ require "cameraplus/search"
17
+ require "cameraplus/exceptions"
18
+ require "cameraplus/version"
@@ -0,0 +1,17 @@
1
+ module Cameraplus
2
+ module API
3
+ class Page
4
+
5
+ def self.find(identifier, options = {})
6
+ Request.call uri(identifier), options
7
+ end
8
+
9
+ private
10
+
11
+ def self.uri(identifier)
12
+ "/#{identifier}:info"
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ module Cameraplus
2
+ module API
3
+ class Request
4
+ include HTTParty
5
+
6
+ base_uri "http://camerapl.us"
7
+
8
+ def self.call(url, options = {})
9
+ new(url, options).parsed_response
10
+ end
11
+
12
+ def initialize(url, options = {})
13
+ @url = url
14
+ @options = options
15
+ end
16
+
17
+ def parsed_response
18
+ if code == 200
19
+ @response.parsed_response
20
+ else
21
+ raise InvalidResponseError, message
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def request
28
+ @response ||= self.class.get @url, query: @options
29
+ end
30
+
31
+ def response
32
+ request.response
33
+ end
34
+
35
+ def code
36
+ response.code.to_i
37
+ end
38
+
39
+ def message
40
+ "#{@response.message} (#{@response.code}): #{@response.body}\n#{@response.inspect}"
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ module Cameraplus
2
+ module API
3
+ class Search
4
+
5
+ def self.find(filters)
6
+ if has_valid_filters filters
7
+ Request.call "/search", filters
8
+ else
9
+ raise InvalidArgumentError
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def self.valid_filters
16
+ %w(username tweettext earliest latest locationname)
17
+ end
18
+
19
+ def self.has_valid_filters(filters)
20
+ filters.keys.select { |filter| valid_filters.include?(filter.to_s) }.any?
21
+ end
22
+
23
+ end
24
+ end
25
+ end