hungry 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ module Hungry
2
+ class Menu
3
+ class Category < Resource
4
+
5
+ attr_accessor :id, :name, :dishes, :menu
6
+
7
+ def dishes
8
+ @dishes ||= []
9
+ end
10
+
11
+ def dishes=(new_dishes)
12
+ @dishes = new_dishes.map do |attributes|
13
+ dish = Menu::Dish.new(attributes)
14
+ dish.category = self
15
+ dish.data_source = data_source
16
+ dish
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Hungry
2
+ class Menu
3
+ class Dish < Resource
4
+
5
+ attr_accessor :name, :price, :description, :options, :photos, :category
6
+
7
+ def options
8
+ @options ||= []
9
+ end
10
+
11
+ def options=(new_options)
12
+ @options = new_options.map do |attributes|
13
+ option = Menu::Option.new(attributes)
14
+ option.dish = self
15
+ option.data_source = data_source
16
+ option
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ module Hungry
2
+ class Menu
3
+ class Option < Resource
4
+
5
+ attr_accessor :description, :price, :dish
6
+
7
+ end
8
+ end
9
+ end
@@ -1,32 +1,69 @@
1
- require 'httparty'
1
+ require 'httparty' unless defined? HTTParty
2
2
 
3
3
  module Hungry
4
4
  class Resource
5
5
  include HTTParty
6
6
 
7
- attr_accessor :attributes
7
+ attr_accessor :attributes, :resources, :data_source
8
8
 
9
9
  ### CLASS METHODS:
10
10
 
11
11
  def self.get(*args)
12
- puts "[Resource]: GET #{args.map(&:inspect).join(', ')}"
13
12
  self.base_uri Hungry.api_url
14
13
  super
15
14
  end
16
15
 
17
- def self.belongs_to(resource, klass = Resource)
16
+ def self.belongs_to(resource, klass = 'Resource')
18
17
  define_method resource do
19
- if url = resources[resource]
20
- attributes = self.class.get url
21
- klass.new(attributes)
18
+ @belongs_to ||= {}
19
+ @belongs_to[resource] ||= begin
20
+ klass = Kernel.const_get(klass) if klass.is_a?(String)
21
+
22
+ if attributes[resource].present?
23
+ resource = klass.new attributes[resource]
24
+ resource.data_source = data_source
25
+ resource
26
+ elsif url = resources[resource]
27
+ attributes = self.class.get url
28
+ resource = klass.new attributes
29
+ resource.data_source = url
30
+ resource
31
+ end
22
32
  end
23
33
  end
24
34
  end
25
35
 
26
- def self.has_many(resource, klass = Resource)
36
+ def self.has_many(resource, klass = 'Resource')
27
37
  define_method resource do
28
- if url = resources[resource]
29
- klass.collection.from_url(url)
38
+ @has_many ||= {}
39
+ @has_many[resource] ||= begin
40
+ klass = Kernel.const_get(klass) if klass.is_a?(String)
41
+
42
+ if url = resources[resource]
43
+ collection = klass.collection.from_url(url)
44
+
45
+ if attributes[resource].present?
46
+ collection.results = attributes[resource]
47
+ end
48
+
49
+ collection
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def self.lazy_load(*attributes)
56
+ attributes.each do |attribute|
57
+ alias_method "#{attribute}_without_lazy_load".to_sym, attribute
58
+ define_method attribute do
59
+ result = send "#{attribute}_without_lazy_load".to_sym
60
+
61
+ if !result.present? && (canonical_data_source != data_source && canonical_data_source.present?)
62
+ reload
63
+ send "#{attribute}_without_lazy_load".to_sym
64
+ else
65
+ result
66
+ end
30
67
  end
31
68
  end
32
69
  end
@@ -56,13 +93,23 @@ module Hungry
56
93
  def find(id)
57
94
  raise NoEndpointSpecified unless endpoint
58
95
 
59
- response = get "#{endpoint}/#{id}"
96
+ uri = "#{endpoint}/#{id}"
97
+
98
+ Util.log "GET: #{uri}"
99
+ response = get uri
60
100
 
61
101
  if response.code == 200
62
- new(response)
102
+ attributes = Util.parse_json(response.body)
103
+ resource = new(attributes)
104
+ resource.data_source = uri
105
+ resource
63
106
  end
64
107
  end
65
108
 
109
+ def first(n = 1)
110
+ collection.first(n)
111
+ end
112
+
66
113
  def all(criteria = {})
67
114
  collection.all(criteria)
68
115
  end
@@ -89,5 +136,26 @@ module Hungry
89
136
  end
90
137
  end
91
138
  end
139
+
140
+ def canonical_data_source
141
+ resources && resources[:self]
142
+ end
143
+
144
+ def reload
145
+ resource = self.class.find id
146
+
147
+ if resource
148
+ self.data_source = resource.data_source
149
+ initialize resource.attributes
150
+ else
151
+ self.data_source = nil
152
+ initialize Hash[*attributes.keys.map { |key| [key, nil] }]
153
+ end
154
+
155
+ @has_many = {}
156
+ @belongs_to = {}
157
+
158
+ self
159
+ end
92
160
  end
93
161
  end
@@ -0,0 +1,17 @@
1
+ module Hungry
2
+ class Response < Resource
3
+
4
+ ### RESOURCES:
5
+
6
+ belongs_to :review, 'Hungry::Review'
7
+
8
+ ### ATTRIBUTES:
9
+
10
+ ### Review:
11
+ attr_accessor :id, :body, :author,
12
+
13
+ ### Utility:
14
+ :created_at, :updated_at
15
+
16
+ end
17
+ end
@@ -1,8 +1,12 @@
1
- require 'httparty'
2
-
3
1
  module Hungry
4
2
  class Review < Resource
5
3
 
4
+ ### RESOURCES:
5
+
6
+ belongs_to :venue, 'Hungry::Venue'
7
+
8
+ has_many :responses, 'Hungry::Response'
9
+
6
10
  ### ATTRIBUTES:
7
11
 
8
12
  ### Review:
@@ -11,9 +15,5 @@ module Hungry
11
15
  ### Utility:
12
16
  :created_at, :updated_at
13
17
 
14
- ### RESOURCES:
15
-
16
- belongs_to :venue, Venue
17
-
18
18
  end
19
19
  end
@@ -3,19 +3,33 @@ module Hungry
3
3
 
4
4
  self.endpoint = '/sites'
5
5
 
6
+ ### FINDERS:
7
+
8
+ def self.with_hostname(hostname)
9
+ collection.all(hostname: hostname).first
10
+ end
11
+
12
+ def self.for_country(country)
13
+ collection.all(country: country.id).first
14
+ end
15
+
16
+ def self.default_site
17
+ collection.all(default: true).first
18
+ end
19
+
20
+
6
21
  ### ATTRIBUTES:
7
22
 
8
23
  ### Preview:
9
24
  attr_accessor :id, :name, :title, :subtitle, :identifier, :default, :locale,
10
- :url, :timezone, :country, :applications,
25
+ :url, :email, :support_email, :timezone, :country, :applications,
11
26
 
12
27
  ### Utility:
13
- :resources, :counters
28
+ :resources, :counters
14
29
 
15
- ### FINDERS:
16
-
17
- def self.with_hostname(hostname)
18
- collection.all(hostname: hostname).first
30
+ def hostname
31
+ uri = URI.parse(url) rescue nil
32
+ uri && uri.hostname
19
33
  end
20
34
 
21
35
  end
@@ -3,6 +3,12 @@ module Hungry
3
3
 
4
4
  self.endpoint = '/tags'
5
5
 
6
+ ### RESOURCES:
7
+
8
+ has_many :venues, 'Hungry::Venue'
9
+
10
+ has_many :tags, 'Hungry::Tag'
11
+
6
12
  ### ATTRIBUTES:
7
13
 
8
14
  ### Tag:
@@ -10,12 +16,5 @@ module Hungry
10
16
 
11
17
  ### Utility:
12
18
  :resources, :counters
13
-
14
- ### RESOURCES:
15
-
16
- has_many :venues, Venue
17
-
18
- has_many :tags, Tag
19
-
20
19
  end
21
20
  end
@@ -0,0 +1,77 @@
1
+ module Hungry
2
+ class User < Resource
3
+
4
+ self.endpoint = '/users'
5
+
6
+ ### ASSOCIATIONS:
7
+
8
+ has_many :reviews, 'Hungry::Review'
9
+
10
+ ### FINDERS:
11
+
12
+ def self.with_ip(ip_address)
13
+ collection.all(ip: ip_address)
14
+ end
15
+
16
+ ### CLASS METHODS:
17
+
18
+ def self.authenticate_with_persistence_token(persistence_token)
19
+ result = authenticate_via_api 'Cookie' => "persistence_token=#{persistence_token}"
20
+ return false unless result.present?
21
+
22
+ from_json(result)
23
+ end
24
+
25
+ def self.authenticate_with_perishable_token(perishable_token)
26
+ result = authenticate_via_api query: "perishable_token=#{perishable_token}"
27
+ return false unless result.present?
28
+
29
+ from_json(result)
30
+ end
31
+
32
+ def self.from_json(json)
33
+ new Util.parse_json(json)
34
+ end
35
+
36
+ def self.authenticate_via_api(options = {})
37
+ options = {
38
+ path: '/account.json',
39
+ query: nil
40
+ }.merge(options)
41
+
42
+ url = URI.parse(Hungry.api_url)
43
+ url.path = options.delete(:path)
44
+ url.query = options.delete(:query)
45
+
46
+ http = Net::HTTP.new(url.host, url.port)
47
+ if url.scheme == 'https'
48
+ http.use_ssl = true
49
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
50
+ end
51
+
52
+ request = Net::HTTP::Get.new(url.request_uri, options)
53
+ response = http.request request
54
+
55
+ if response.code.to_i == 200
56
+ response.body
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ ### ATTRIBUTES:
63
+
64
+ ### Preview:
65
+ attr_accessor :id, :name, :avatar, :slug, :profile_url, :score, :bio,
66
+ :location, :email,
67
+
68
+ ### FULL:
69
+ :gender, :birth_date, :website_url, :persistence_token,
70
+ :single_access_token, :maintainer_of, :connections,
71
+ :device_tokens, :admin,
72
+
73
+ ### Utility:
74
+ :resources, :counters, :created_at, :updated_at, :activated_at
75
+
76
+ end
77
+ end
@@ -1,13 +1,23 @@
1
- require 'cgi'
2
- require 'rack/utils'
1
+ require 'cgi' unless defined?(CGI)
3
2
 
4
3
  module Hungry
5
4
  module Util
6
5
  extend self
7
6
 
7
+ def log(message, type = :info)
8
+ if Hungry.logger
9
+ message = "[Hungry] #{message}"
10
+ Hungry.logger.send(type, message)
11
+ end
12
+ end
13
+
14
+ def parse_json(json)
15
+ Hungry.json_parser.call(json)
16
+ end
17
+
8
18
  def params_from_uri(uri)
9
19
  uri = URI.parse(uri) unless uri.is_a?(URI)
10
- Rack::Utils.parse_query(uri.query) if uri.query.present?
20
+ parse_query(uri.query) if uri.query.present?
11
21
  end
12
22
 
13
23
  def uri_with_params(uri, params = {})
@@ -20,7 +30,37 @@ module Hungry
20
30
  end
21
31
 
22
32
  def is_numeric?(value)
23
- value.to_s =~ /^[+-]?[\d]+(\.[\d]+){0,1}$/
33
+ !!(value.to_s =~ /^[+-]?[\d]+(\.[\d]+){0,1}$/)
34
+ end
35
+
36
+ private
37
+
38
+ # Copied from rack/utils:
39
+ def unescape(s, encoding = Encoding::UTF_8)
40
+ URI.decode_www_form_component(s, encoding)
41
+ end
42
+
43
+ def parse_query(qs, d = nil, &unescaper)
44
+ unescaper ||= method(:unescape)
45
+
46
+ params = {}
47
+
48
+ (qs || '').split(d ? /[#{d}] */ : /[&;] */n).each do |p|
49
+ next if p.empty?
50
+ k, v = p.split('=', 2).map(&unescaper)
51
+
52
+ if cur = params[k]
53
+ if cur.class == Array
54
+ params[k] << v
55
+ else
56
+ params[k] = [cur, v]
57
+ end
58
+ else
59
+ params[k] = v
60
+ end
61
+ end
62
+
63
+ params
24
64
  end
25
65
  end
26
66
  end
@@ -6,27 +6,13 @@ module Hungry
6
6
 
7
7
  ### RESOURCES:
8
8
 
9
- has_many :reviews, Review
9
+ has_many :reviews, 'Hungry::Review'
10
10
 
11
- belongs_to :country, Country
11
+ belongs_to :country, 'Hungry::Country'
12
12
 
13
- belongs_to :region, Region
13
+ belongs_to :region, 'Hungry::Region'
14
14
 
15
- belongs_to :city, City
16
-
17
- ### ATTRIBUTES:
18
-
19
- ### Preview:
20
- attr_accessor :id, :name, :category, :telephone, :fax, :website_url,
21
- :tagline, :rating, :url, :address, :geolocation, :relevance,
22
- :distance, :plan,
23
-
24
- ### Full:
25
- :reachability, :staff, :prices, :capacity, :description,
26
- :tags, :menus, :images, :maintainers, :awards, :opening_hours,
27
-
28
- ### Utility:
29
- :resources, :counters, :created_at, :updated_at
15
+ belongs_to :city, 'Hungry::City'
30
16
 
31
17
  ### FINDERS:
32
18
 
@@ -50,8 +36,43 @@ module Hungry
50
36
  collection.sort_by(sortable)
51
37
  end
52
38
 
39
+ def self.maintainable_by(user_or_id)
40
+ collection.maintainable_by(user_or_id)
41
+ end
42
+
53
43
  def self.paginate(page, options = {})
54
44
  collection.paginate(page, options)
55
45
  end
46
+
47
+ ### ATTRIBUTES:
48
+
49
+ ### Preview:
50
+ attr_accessor :id, :name, :category, :telephone, :fax, :website_url,
51
+ :tagline, :rating, :url, :address, :geolocation,
52
+ :currency_symbol, :relevance, :distance, :plan,
53
+
54
+ ### Full:
55
+ :reachability, :staff, :prices, :capacity, :description,
56
+ :tags, :menus, :images, :maintainers, :awards, :opening_hours,
57
+ :holidays,
58
+
59
+ ### Utility:
60
+ :counters, :created_at, :updated_at, :status
61
+
62
+ lazy_load :tags, :menus, :maintainers, :opening_hours, :holidays
63
+
64
+ def geolocation=(new_coordinates)
65
+ @geolocation = Geolocation.parse(new_coordinates).tap do |geo|
66
+ attributes[:geolocation] = geo
67
+ end
68
+ end
69
+
70
+ def menus=(new_menus)
71
+ @menus = new_menus.map do |attributes|
72
+ menu = Menu.new(attributes)
73
+ menu.data_source = data_source
74
+ menu
75
+ end
76
+ end
56
77
  end
57
78
  end