rlocu 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e3487ae33152629fffe23b46033f4b39a33b3f6b
4
+ data.tar.gz: 91c4d5c8fa76b87538ca8ba49cae5d6ebc96c9de
5
+ SHA512:
6
+ metadata.gz: 30eb21a8d1bb3d2f8191b0fa3cc963ccf82fe5ce3b0a1bc5ff89bb2e14f7e20900e2e5b722df5c34ebaaf04de415770816a35cc91cf8c96ac0dce3b65469efac
7
+ data.tar.gz: a8f7f656d564ddcd620e8fd1e67e8cd151bbf1d0a4c5cfbd20b85e65f0ce337fb0efa2a22bbb557a02edffd144c93c48c66943dc8d5ebb98e40047a9fa4bc170
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --require spec_helper
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Rlocu
2
2
 
3
- A simple, unoffical, and currently incomplete wrapper for the Locu API (locu.com)
3
+ A simple, unoffical, and somewhat complete wrapper for the Locu Version 2 API (locu.com)
4
+
5
+ Currrently supports Venue Searches by
6
+ * lat long radius
7
+ * locu id
8
+ * in categories
9
+ * with menus
4
10
 
5
11
  ## Installation
6
12
 
@@ -10,46 +16,41 @@ Add this line to your application's Gemfile:
10
16
 
11
17
  And then execute:
12
18
 
13
- $ bundle
19
+ $ bundle install
14
20
 
15
- Or install it yourself as:
21
+ Or install it yourself:
16
22
 
17
23
  $ gem install rlocu
18
24
 
19
25
  ## Getting Started
20
26
 
21
- When you require Rlocu it runs config! on itself to set api_key and http_base. It reads these values from a YAML .config file in .lib/ exa:
27
+ Before executing any Rlocu queries you need to configure it.
22
28
 
23
- API_KEY: 'yourapikeyyourapikeyyourapikey'
24
- HTTP_BASE: 'https://api.locu.com/v1_0/'
29
+ Rlocu.configure do |conf|
30
+ conf.api_key = 'yourapikeyyourapikeyyourapikey'
31
+ end
25
32
 
26
33
  ## Usage
34
+ In general you search Locu with `RLocu::VenueSearch`.
27
35
 
28
- Right now the only functionality covered is Venue Search to search for venues, and Venue Details which gets you menus, etc.
29
- Everything is under the Rlocu namespace.
36
+ The class takes an optional parameter `return_fields` which is an array of venue fields* (properties) that should be returned. It defaults to:
30
37
 
31
- Venue Search
32
- Pass a hash of params to Rlocu::VenueSearch.query()
33
- Returns an array of venues (Rlocu::Venue)
34
- Or you can pass it a block to iterate through the venues:
38
+ %w{locu_id name location description menus}
35
39
 
36
- Rlocu::VenueSearch.query(postal_code: '90278', cuisine: 'Thai') do |v|
37
- puts v.name
38
- end
40
+ *See Locu Developer Docs for available fields.
39
41
 
40
- Venue Details
41
- You can pass an array of up to 5 venues to Rlocu::VenueDetails()
42
- Returns an array of Rlocu::Venues with the detail data (th venue search and detail search have overlapping attributes).
42
+ There are several instance methods that follow the typical usage pattern. Each adds a `QueryBuilder::KeyValueCondition` to the `@key_value_conditions` array and returns `self` so they can be chained together. This creates a restrictive && filter (ie search for venues that satisfy this condition and this condition.) Then you call `search` to perform the search with the query that has been built.
43
+
44
+ Example:
45
+
46
+ venues = Rlocu::VenueSearch.new.in_lat_long_radius(lat: my_latitude, long: my_longitude, radius: 2000).with_menus.search
47
+
48
+ ## The Objects
49
+
50
+ `Rlocu::Venue` has many `Rlocu::Menu`. Both objects map instance variables to (most of) the properties from the Locu API.
43
51
 
44
- Or you can pass it a block to iterate through the venues:
45
- Rlocu::VenueDetails.query(array_of_venues) do |v|
46
- puts v.twitter_id
47
- end
48
52
 
49
- Object Space
50
- The object space essentially follows the Locu API with two little utility objects:
51
- RLocu::Location
52
- Rlocu::Bounds
53
+ `RLocu::Menu#to_s` creates a rough ascii print of the menu, but should give a fairly decent example for displaying.
53
54
 
54
55
  ## Contributing
55
56
 
data/Rakefile CHANGED
@@ -1,12 +1,13 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
- require 'rake/testtask'
3
+ # tests run with minitest_helper.rb
4
+ # require 'rake/testtask'
4
5
 
5
- task default: [:test]
6
+ # task default: [:test]
6
7
 
7
- Rake::TestTask.new do |t|
8
- t.libs << 'test'
9
- t.test_files = FileList['test/test*.rb']
10
- #t.verbose = true
11
- end
8
+ # Rake::TestTask.new do |t|
9
+ # t.libs << 'test'
10
+ # t.test_files = FileList['test/test*.rb']
11
+ # #t.verbose = true
12
+ # end
12
13
 
@@ -1,8 +1,23 @@
1
- #require "rlocu/version"
2
- require_relative 'rlocu/config'
3
- require_relative 'rlocu/venue'
1
+
2
+ module Rlocu
3
+ RlocuError = Class.new(StandardError)
4
+ class << self
5
+ attr_accessor :api_key
6
+ def configure
7
+ yield self
8
+ end
9
+
10
+ def api_version
11
+ 'v2'
12
+ end
13
+ end
14
+ end
15
+
16
+ require_relative 'rlocu/version'
17
+ require_relative 'rlocu/utilities/lat_long_radius'
18
+ require_relative 'rlocu/geo_json'
4
19
  require_relative 'rlocu/menu'
20
+ require_relative 'rlocu/venue'
21
+ require_relative 'rlocu/query_builder'
22
+ require_relative 'rlocu/venue_query'
5
23
  require_relative 'rlocu/venue_search'
6
- require_relative 'rlocu/venue_details'
7
- require_relative 'utilities'
8
-
@@ -0,0 +1,22 @@
1
+ module Rlocu
2
+ class GeoJSON
3
+ def initialize(geo)
4
+ raise ArgumentError unless geo['type']
5
+ raise ArgumentError unless geo['coordinates']
6
+ @point = geo['type']
7
+ @coordinates = geo['coordinates']
8
+ end
9
+
10
+ def longitude
11
+ coordinates[0]
12
+ end
13
+
14
+ def latitude
15
+ coordinates[1]
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :coordinates
21
+ end
22
+ end
@@ -1,130 +1,144 @@
1
1
  module Rlocu
2
2
  class Menu
3
- attr_accessor :name
4
- attr_reader :sections
3
+ attr_reader :menu_name, :sections
5
4
 
6
- def initialize(meta_menu)
7
- @name = meta_menu['menu_name']
8
- self.sections = meta_menu['sections']
5
+ def initialize(menu_hash)
6
+ @menu_name = menu_hash['menu_name']
7
+ self.sections = menu_hash['sections']
9
8
  end
10
9
 
11
- def sections=(meta_sections)
10
+ def sections=(sections_list)
12
11
  @sections = []
13
- meta_sections.each {|h| @sections << Section.new(h)}
12
+ sections_list.each { |section| @sections << Section.new(section) } unless sections_list.nil?
14
13
  end
15
14
 
16
- end
17
-
18
- class Section
19
- attr_accessor :name
20
- attr_reader :subsections
21
-
22
- def initialize(meta_section)
23
- @name = meta_section['section_name']
24
- self.subsections = meta_section['subsections']
25
- end
26
-
27
- def subsections=(meta_subsections)
28
- @subsections = []
29
- meta_subsections.each {|h| @subsections << Subsection.new(h)}
15
+ def to_s
16
+ str = "Menu: #{menu_name}\n"
17
+ sections.each do |section|
18
+ str << "-----#{section.section_name}-----\n"
19
+ section.subsections.each do |subsection|
20
+ str << "---#{subsection.subsection_name}---\n"
21
+ subsection.contents.each do |content|
22
+ case content
23
+ when SectionText
24
+ str << "SECTION TEXT : #{content.to_s}\n"
25
+ when MenuItem
26
+ str << "MENU ITEM : #{content.name} #{content.description} #{content.price}\n"
27
+ content.option_groups.each do |opt_group|
28
+ str << "OPTION GROUP : #{opt_group.text}\n"
29
+ opt_group.options.each do |option|
30
+ str << "OPTION : #{option.to_s}\n"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ str
30
38
  end
31
39
 
32
- end
33
-
34
- class Subsection
35
- attr_accessor :name
36
- attr_reader :contents
37
-
38
- def initialize(meta_subsection)
39
- @name = meta_subsection['subsection_name']
40
- self.contents = meta_subsection['contents']
41
- end
40
+ class Section
41
+ attr_reader :section_name, :subsections
42
+ def initialize(section_hash)
43
+ @section_name = section_hash['section_name']
44
+ self.subsections = section_hash['subsections']
45
+ end
42
46
 
43
- def contents=(meta_contents)
44
- @contents = []
45
- meta_contents.each do |h|
46
- #TODO: raise error if type is different
47
- @contents << case h['type']
48
- when 'SECTION_TEXT'
49
- SectionText.new(h)
50
- when 'ITEM'
51
- MenuItem.new(h)
52
- end
47
+ def subsections=(subsections_list)
48
+ @subsections = []
49
+ subsections_list.each { |subsection| @subsections << Subsection.new(subsection) } unless subsections_list.nil?
53
50
  end
54
51
  end
55
52
 
56
- end
57
-
58
- class SectionText
59
- attr_accessor :type, :text
60
-
61
- def initialize(meta_section_text)
62
- @type = meta_section_text['type']
63
- @text = meta_section_text['text']
64
- end
53
+ class Subsection
54
+ attr_reader :subsection_name, :contents
55
+ def initialize(subsection_hash)
56
+ @subsection_name = subsection_hash['subsection_name']
57
+ self.contents = subsection_hash['contents']
58
+ end
65
59
 
66
- def to_s
67
- @text
60
+ def contents=(contents_list)
61
+ @contents = []
62
+ return if contents_list.nil?
63
+ contents_list.each do |content|
64
+ #TODO: raise error if type is different
65
+ @contents << case content['type']
66
+ when 'SECTION_TEXT'
67
+ SectionText.new(content)
68
+ when 'ITEM'
69
+ MenuItem.new(content)
70
+ end
71
+ end
72
+ end
68
73
  end
69
-
70
- end
71
74
 
72
- # create a to_s for each that can be over-ridden by the user
73
- class MenuItem
74
- attr_accessor :type, :name, :description, :price
75
- attr_reader :option_groups
76
-
77
- def initialize(meta_menu_item)
78
- @type = meta_menu_item['type']
79
- @name = meta_menu_item['name']
80
- @description = meta_menu_item['description']
81
- @price = meta_menu_item['price']
82
- self.option_groups = meta_menu_item['option_groups']
83
- end
75
+ class SectionText
76
+ attr_reader :type, :text
77
+ def initialize(section_text_hash)
78
+ @type = section_text_hash['type']
79
+ @text = section_text_hash['text']
80
+ end
84
81
 
85
- def option_groups=(meta_option_groups)
86
- @option_groups = []
87
- meta_option_groups.each { |h| @option_groups << OptionGroup.new(h) } if meta_option_groups
82
+ def to_s
83
+ @text
84
+ end
88
85
  end
89
86
 
90
- end
91
-
92
- class OptionGroup
93
- attr_accessor :type, :text
94
- attr_reader :options
87
+ class Item
88
+ attr_reader :type, :name, :description, :price, :option_groups
89
+ def initialize(item_hash)
90
+ @type = item_hash['type']
91
+ @name = item_hash['name']
92
+ @description = item_hash['description']
93
+ @price = item_hash['price']
94
+ self.option_groups = item_hash['option_groups']
95
+ end
95
96
 
96
- def initialize(meta_option_group)
97
- @type = meta_option_group['type']
98
- @text = meta_option_group['text']
99
- self.options = meta_option_group['options']
97
+ def option_groups=(option_groups_list)
98
+ @option_groups = []
99
+ return if option_groups_list.nil?
100
+ option_groups_list.each { |option_group| @option_groups << OptionGroup.new(option_group) }
101
+ end
100
102
  end
101
103
 
102
- def options=(meta_options)
103
- @options = []
104
- meta_options.each {|h| @options << Option.new(h) } if meta_options
104
+ class MenuItem < Item
105
+ attr_reader :menu_name, :section_name, :subsection_name, :section_text, :type, :currency_symbol, :photos
106
+ def initialize(menu_item_hash)
107
+ @menu_name = menu_item_hash['menu_name']
108
+ @section_name = menu_item_hash['section_name']
109
+ @subsection_name = menu_item_hash['subsection_name']
110
+ @section_text = menu_item_hash['section_text']
111
+ @currency_symbol = menu_item_hash['currency_symbol']
112
+ @photos = menu_item_hash['photos']
113
+ super
114
+ end
105
115
  end
106
116
 
117
+ class OptionGroup
118
+ attr_reader :options, :type, :text
119
+ def initialize(option_group_hash)
120
+ @type = option_group_hash['type']
121
+ @text = option_group_hash['text']
122
+ self.options = option_group_hash['options']
123
+ end
107
124
 
108
- end
109
-
110
- class Option
111
- attr_accessor :name
112
- attr_writer :price
113
-
114
- def initialize(meta_option)
115
- @name = meta_option['name']
116
- @price = meta_option['price']
125
+ def options=(options_list)
126
+ @options = []
127
+ options_list.each { |option| @options << Option.new(option) } unless options_list.nil?
128
+ end
117
129
  end
118
130
 
119
- def price
120
- # price can be relative indicated by a + in the price
121
- # not currently doing anything w it
122
- @price
123
- end
131
+ class Option
132
+ attr_reader :name, :price
133
+ def initialize(option_hash)
134
+ @name = option_hash['name']
135
+ @price = option_hash['price']
136
+ # price can be relative indicated by a + in the price
137
+ end
124
138
 
125
- def to_s
126
- "#{name} #{price}"
139
+ def to_s
140
+ "#{name} #{price}"
141
+ end
127
142
  end
128
-
129
143
  end
130
- end
144
+ end
@@ -0,0 +1,55 @@
1
+ module Rlocu
2
+ module QueryBuilder
3
+ def base_url
4
+ "https://api.locu.com/v2/venue/search"
5
+ end
6
+
7
+ class QueryCondition
8
+ attr_reader :key_value_conditions
9
+ def initialize(key_value_conditions:)
10
+ @key_value_conditions = key_value_conditions
11
+ end
12
+
13
+ def to_h
14
+ h = {}
15
+ key_value_conditions.each { |condition| h.merge!(condition.to_h) }
16
+ h
17
+ end
18
+ end
19
+
20
+ class KeyValueCondition
21
+ attr_reader :key, :value, :condition
22
+ ValidConditions = %w{$present $gt $lt $ge $le $contains_any $contains_all $contains_none $in_lat_lng_bbox $in_lat_lng_radius} << nil
23
+ def initialize(key:, value:, condition: nil)
24
+ @key = key
25
+ @value = value
26
+ @condition = condition
27
+ raise ArgumentError unless valid?
28
+ end
29
+
30
+ def to_h
31
+ if condition
32
+ if value.is_a?(Utilities::LatLongRadius)
33
+ {key => {'geo' => {condition => value.to_a}}}
34
+ elsif key == 'categories'
35
+ {key => {'str_id' => {condition => value}}}
36
+ else
37
+ {key => {condition => value}}
38
+ end
39
+ else
40
+ {key => value}
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def valid?
47
+ # check if the condition and value are appropriate
48
+ return false unless ValidConditions.include? condition
49
+ return false if condition =~ /\$in_lat_lng/ && !value.is_a?(Utilities::LatLongRadius)
50
+ return false if condition == '$contains_any' && !value.is_a?(Array)
51
+ true
52
+ end
53
+ end
54
+ end
55
+ end