hungry 0.0.1 → 0.1.0

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