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