drink-socially 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ require 'cgi'
2
+
3
+ module NRB
4
+ module Untappd
5
+ class API
6
+
7
+ private
8
+
9
+ class Credential
10
+
11
+ class IncompleteCredentialError < ArgumentError; end
12
+
13
+ def self.valid_attrs
14
+ [ :access_token, :client_id, :client_secret ]
15
+ end
16
+
17
+
18
+ def initialize(opts={})
19
+ opts.slice!(*self.class.valid_attrs)
20
+ @creds = {}
21
+ !! opts[:access_token] && @creds[:access_token] = CGI::escape(opts[:access_token])
22
+ !! opts[:client_id] && @creds[:client_id] = CGI::escape(opts[:client_id])
23
+ !! opts[:client_secret] && @creds[:client_secret] = CGI::escape(opts[:client_secret])
24
+ raise IncompleteCredentialError.new('Provide either API access token or API client id & secret') unless valid?
25
+ end
26
+
27
+ valid_attrs.each do |m|
28
+ define_method m do
29
+ @creds[m]
30
+ end
31
+ end
32
+
33
+
34
+ def is_client?
35
+ !! @creds[:client_id] && !! @creds[:client_secret]
36
+ end
37
+ alias_method :is_app?, :is_client?
38
+
39
+
40
+ def is_user?
41
+ !! @creds[:access_token]
42
+ end
43
+
44
+
45
+ def merge(hash)
46
+ @creds.merge(hash)
47
+ end
48
+
49
+
50
+ def to_h
51
+ @creds
52
+ end
53
+
54
+
55
+ def to_param
56
+ @creds.map { |k,v| "#{k}=#{v}" }.join("&")
57
+ end
58
+
59
+ private
60
+
61
+ def valid?
62
+ (is_client? || is_user?) && ! (is_client? && is_user?)
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,13 @@
1
+ module NRB
2
+ module Untappd
3
+ class API
4
+ class Notification
5
+
6
+ def initialize(response)
7
+
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,83 @@
1
+ module NRB
2
+ module Untappd
3
+ class API
4
+ class Object < HTTPService::Response
5
+
6
+ attr_reader :attributes, :error_message, :pagination, :results
7
+
8
+ def initialize(args)
9
+ super
10
+ parse_error_response
11
+ setup_pagination(args)
12
+ extract_results args[:results_path]
13
+ end
14
+
15
+ private
16
+
17
+ def define_attributes_from(hash)
18
+ return unless hash.respond_to?(:keys)
19
+ @attributes ||= []
20
+ perform_unless_respond_to(hash.keys) do |attr|
21
+ @attributes.push(attr.to_sym)
22
+ end
23
+ end
24
+
25
+
26
+ def define_methods_from(hash)
27
+ return unless hash.respond_to?(:keys)
28
+ perform_unless_respond_to(hash.keys) do |meth|
29
+ define_singleton_method meth, lambda { hash[meth] }
30
+ end
31
+ end
32
+
33
+
34
+ def extract_from_body(path)
35
+ return unless !! body
36
+ path.inject(body) do |node,method_name|
37
+ break unless node.respond_to? method_name
38
+ node.send method_name
39
+ end
40
+ end
41
+
42
+
43
+ def extract_results(path)
44
+ @results = extract_from_body path if !! path
45
+ define_attributes_from @results
46
+ define_methods_from @results
47
+ end
48
+
49
+
50
+ def parse_error_response
51
+ return unless errored?
52
+ @error_message = if body[:meta]
53
+ "[#{body[:meta][:error_type]}] #{body[:meta][:error_detail]}"
54
+
55
+ elsif body[:error]
56
+ body[:error]
57
+
58
+ else
59
+ "Could not parse error message from response body"
60
+ end
61
+ end
62
+
63
+
64
+ def perform_unless_respond_to(arr)
65
+ return unless arr.respond_to?(:each)
66
+ arr.each do |key|
67
+ unless respond_to?(key)
68
+ yield key
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ def setup_pagination(args)
75
+ @paginator = args[:paginator_class] || NRB::Untappd::API::Pagination
76
+ @pagination = @paginator.from_response self
77
+ end
78
+
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,73 @@
1
+ require 'uri'
2
+ module NRB
3
+ module Untappd
4
+ class API
5
+ class Pagination
6
+
7
+ def self.from_response(response)
8
+ # It's duck types (almost) all the way down
9
+ return unless response.respond_to?(:body) &&
10
+ response.body.respond_to?(:response) &&
11
+ response.body.response.respond_to?(:pagination) &&
12
+ !! response.body.response.pagination
13
+ new response.body.response.pagination
14
+ end
15
+
16
+
17
+ def initialize(pagination)
18
+ @api_max_id = pagination[:max_id].to_i
19
+ @uris = { api_next: URI.parse(pagination[:next_url]),
20
+ api_prev: URI.parse(pagination[:since_url]),
21
+ api_since: URI.parse(pagination[:since_url])
22
+ }
23
+ end
24
+
25
+
26
+ def next_id
27
+ @api_max_id + 1
28
+ end
29
+
30
+
31
+ # Warning: The API will return the most recent results if next_id is
32
+ # greater than the most recent result
33
+ def next_uri
34
+ uri_for query: "since=#{next_id}", uri: :next
35
+ end
36
+
37
+
38
+ def next_url
39
+ next_uri.to_s
40
+ end
41
+
42
+
43
+ def prev_id
44
+ @api_max_id - 1
45
+ end
46
+ alias_method :previous_id, :prev_id
47
+
48
+
49
+ def prev_uri
50
+ uri_for query: "max_id=#{prev_id}", uri: :prev
51
+ end
52
+ alias_method :previous_uri, :prev_uri
53
+
54
+
55
+ def prev_url
56
+ prev_uri.to_s
57
+ end
58
+ alias_method :previous_url, :prev_url
59
+
60
+ private
61
+
62
+ def uri_for(opts)
63
+ return nil unless !! opts[:uri]
64
+ return @uris[otps[:uri]] if @uris[opts[:uri]]
65
+ @uris[otps[:uri]] = @uris["api_#{opts[:uri]}".to_sym]
66
+ @uris[otps[:uri]].query = opts[:query] if opts[:query]
67
+ @uris[otps[:uri]]
68
+ end
69
+
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,17 @@
1
+ module NRB
2
+ module Untappd
3
+ class API
4
+ class RateLimit
5
+
6
+ attr_reader :limit, :remaining
7
+
8
+ def initialize(headers)
9
+ return unless headers.respond_to?(:[])
10
+ @limit = headers["x-ratelimit-limit"]
11
+ @remaining = headers["x-ratelimit-remaining"]
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,47 @@
1
+ require 'strscan'
2
+ module NRB
3
+ module Untappd
4
+ class API
5
+ class URLTokenizer
6
+ attr_reader :map, :string
7
+
8
+ def initialize(args)
9
+ @map = args[:map]
10
+ @string = args[:string]
11
+ raise ArgumentError unless @map && @string
12
+ end
13
+
14
+
15
+ def tr
16
+ return @string unless @string =~ /:[#{word_chars}]+:/
17
+ result = ""
18
+ s = StringScanner.new(@string)
19
+ until s.eos? do
20
+ word = s.scan(/:[#{word_chars}]+:|[#{url_chars}]+/)
21
+ raise RuntimeError.new("String Scanner failed. File a bug.") if word.nil?
22
+ result += tr_word(word)
23
+ end
24
+ result
25
+ end
26
+
27
+ private
28
+
29
+ def tr_word(word)
30
+ return word unless word =~ /:([#{word_chars}]+):/
31
+ return map[$1.to_sym].to_s
32
+ end
33
+
34
+
35
+ def url_chars
36
+ word_chars + Regexp.quote('/')
37
+ end
38
+
39
+
40
+ def word_chars
41
+ "0-9a-zA-Z" + Regexp.quote("$-_.+!*'(),")
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+
3
+ module NRB
4
+ module Untappd
5
+ class Config
6
+
7
+ class NoConfig < ArgumentError; end
8
+
9
+ extend Forwardable
10
+
11
+ attr_reader :data
12
+
13
+ def_delegators :@data, :[], :each, :keys
14
+
15
+ def initialize(args)
16
+ load_data(args)
17
+ define_accessors
18
+ end
19
+
20
+ private
21
+
22
+ def define_accessors
23
+ return unless @data
24
+ @data.keys.each do |name|
25
+ unless respond_to?(name)
26
+ define_singleton_method name, lambda { data[name] }
27
+ end
28
+ end
29
+ end
30
+
31
+
32
+ def find_stream(args)
33
+ if args[:filename]
34
+ File.open args[:filename]
35
+ end
36
+ end
37
+
38
+
39
+ def load_data(args)
40
+ args[:stream] ||= find_stream(args)
41
+ raise NoConfig.new("Please supply :filename or :stream to Config.new") unless !! args[:stream]
42
+ loader = args[:stream_loader] || YAML
43
+ @data = loader.load_stream(args[:stream])[0]
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ class Hash
2
+
3
+ def slice(*keys)
4
+ hash = self.class.new
5
+ keys.each { |k| hash[k] = self[k] if has_key?(k) }
6
+ hash
7
+ end
8
+
9
+
10
+ def slice!(*keys)
11
+ omit = slice(*self.keys - keys)
12
+ hash = slice(*keys)
13
+ replace(hash)
14
+ omit
15
+ end
16
+
17
+ end
@@ -0,0 +1,61 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware/parse_oj'
3
+
4
+ module NRB
5
+ class HTTPService
6
+
7
+ autoload :Response, 'drink-socially/http_service/response'
8
+
9
+ DEFAULT_MIDDLEWARE = Proc.new do |faraday|
10
+ faraday.adapter Faraday.default_adapter
11
+ faraday.request :url_encoded
12
+ faraday.response :oj
13
+ end
14
+
15
+ class << self
16
+ attr_accessor :faraday_middleware
17
+
18
+ def default_middleware; DEFAULT_MIDDLEWARE; end
19
+ def default_http_class; Faraday; end
20
+ def default_response_class; Response; end
21
+
22
+
23
+ def make_request(args={}, connection_opts={})
24
+ new(args,connection_opts).make_request
25
+ end
26
+
27
+ end
28
+
29
+ self.faraday_middleware = self.default_middleware
30
+
31
+
32
+ def initialize(args={}, connection_opts={})
33
+ @connection_opts = connection_opts
34
+ @response_class = args.delete(:response_class) || self.class.default_response_class
35
+ @verb = args.delete(:verb)
36
+ @url = args.delete(:url)
37
+ @params = process_args(args)
38
+ @args = args
39
+ end
40
+
41
+
42
+ def make_request
43
+ connection = self.class.default_http_class.new @url, @connection_opts, &self.class.faraday_middleware
44
+ response = connection.send @verb, @url, @params
45
+ args = @args.merge( { body: response.body, headers: response.headers, status: response.status.to_i } )
46
+ @response_class.new args
47
+ rescue Faraday::Error::ParsingError => e
48
+ self.class.default_response_class.new body: {error: e.message}, status: 500
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :params, :verb, :url
54
+
55
+ def process_args(args)
56
+ return args unless @verb == :post
57
+ args.inject("") { |str,pair| str += "#{pair.first}=#{pair.last}&" }.chop
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,27 @@
1
+ require 'hashie'
2
+
3
+ module NRB
4
+ class HTTPService
5
+ class Response
6
+
7
+ attr_reader :status, :body, :headers
8
+
9
+ def errored?
10
+ ! success?
11
+ end
12
+
13
+
14
+ def initialize(args)
15
+ @status = args[:status]
16
+ @body = Hashie::Mash.new args[:body]
17
+ @headers = Hashie::Mash.new args[:headers]
18
+ end
19
+
20
+
21
+ def success?
22
+ @status >= 200 && @status < 300
23
+ end
24
+
25
+ end
26
+ end
27
+ end