rlocu 0.1.0 → 0.2.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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/README.md +27 -26
- data/Rakefile +8 -7
- data/lib/rlocu.rb +21 -6
- data/lib/rlocu/geo_json.rb +22 -0
- data/lib/rlocu/menu.rb +113 -99
- data/lib/rlocu/query_builder.rb +55 -0
- data/lib/rlocu/utilities/lat_long_radius.rb +27 -0
- data/lib/rlocu/venue.rb +107 -24
- data/lib/rlocu/venue_query.rb +35 -0
- data/lib/rlocu/venue_search.rb +39 -21
- data/lib/rlocu/version.rb +1 -1
- data/rlocu.gemspec +13 -12
- data/spec/config_rlocu_spec_helper.rb +5 -0
- data/spec/geo_json_spec.rb +12 -0
- data/spec/menu/item_spec.rb +13 -0
- data/spec/menu/menu_item_spec.rb +13 -0
- data/spec/menu/option_group_spec.rb +10 -0
- data/spec/menu/option_spec.rb +9 -0
- data/spec/menu/section_spec.rb +9 -0
- data/spec/menu/section_text_spec.rb +9 -0
- data/spec/menu/subsection_spec.rb +9 -0
- data/spec/menu_spec.rb +9 -0
- data/spec/query/key_value_condition_spec.rb +37 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/support/rspec_helpers.rb +53 -0
- data/spec/utilities/lat_long_radius_spec.rb +7 -0
- data/spec/venue_query_spec.rb +29 -0
- data/spec/venue_search_spec.rb +31 -0
- metadata +59 -25
- data/lib/rlocu/config.rb +0 -23
- data/lib/rlocu/venue_details.rb +0 -35
- data/lib/utilities.rb +0 -40
- data/test/test_truth.rb +0 -7
- data/test/test_utilities.rb +0 -21
- data/test/test_venue.rb +0 -15
- data/test/test_venue_details.rb +0 -11
- data/test/test_venue_search.rb +0 -26
checksums.yaml
ADDED
@@ -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
data/README.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
# Rlocu
|
2
2
|
|
3
|
-
A simple, unoffical, and
|
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
|
21
|
+
Or install it yourself:
|
16
22
|
|
17
23
|
$ gem install rlocu
|
18
24
|
|
19
25
|
## Getting Started
|
20
26
|
|
21
|
-
|
27
|
+
Before executing any Rlocu queries you need to configure it.
|
22
28
|
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
37
|
-
puts v.name
|
38
|
-
end
|
40
|
+
*See Locu Developer Docs for available fields.
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
|
data/lib/rlocu.rb
CHANGED
@@ -1,8 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
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
|
data/lib/rlocu/menu.rb
CHANGED
@@ -1,130 +1,144 @@
|
|
1
1
|
module Rlocu
|
2
2
|
class Menu
|
3
|
-
|
4
|
-
attr_reader :sections
|
3
|
+
attr_reader :menu_name, :sections
|
5
4
|
|
6
|
-
def initialize(
|
7
|
-
@
|
8
|
-
self.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=(
|
10
|
+
def sections=(sections_list)
|
12
11
|
@sections = []
|
13
|
-
|
12
|
+
sections_list.each { |section| @sections << Section.new(section) } unless sections_list.nil?
|
14
13
|
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
67
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
82
|
+
def to_s
|
83
|
+
@text
|
84
|
+
end
|
88
85
|
end
|
89
86
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
126
|
-
|
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
|