cameraplus 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.
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