yum 0.1.6

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: e5d26bebf5c6c74410fc71d51da982ce75de56c0
4
+ data.tar.gz: 58d9f153290b02e63befbd6e6da11a0169cfcdc5
5
+ SHA512:
6
+ metadata.gz: aac9925d5c3dcecd5e47447da91c90d19573f9da85a74bcc4895b61661110748c33e26ac24dcd3aaf1831c4b1acab8d32b05ec4656e10ad79584fa165664ff46
7
+ data.tar.gz: 508ae09de4c28b3b7a34a14f852248c82a7cce77df6b22c20e6fbaac92b78d53ad3e5b1ad9defc68bc334c184c1636191768ff24178f9a2ff99fd1d0b93e38d0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Simon Štefunko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 halfdeadpie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,98 @@
1
+ # Yum
2
+
3
+ The Yum is the application with command line interface. The main purpose of this application is to increase your cookings
4
+ skills, which are represented as the level. All you need to do is to generate the recipe which you like, cook the food, eat it
5
+ and review it using scale from 0 to 3. The better food you prepare, the more XP points you get. Increasing your XP points leads
6
+ to increasing your level. And your level and XP points "unlock" for you more difficult recipes, which needs more time too cook.
7
+
8
+ The application uses the Yummly API to access the recipes of the food:
9
+
10
+ ```https://developer.yummly.com/```
11
+
12
+ There is also used the Backendless REST as the storage of users data:
13
+
14
+ ```https://backendless.com/docs/rest/doc.html```
15
+
16
+ Application uses Faraday adapter for executing the requests:
17
+
18
+ ```http://www.rubydoc.info/gems/faraday/Faraday/Adapter```
19
+
20
+ As the new user, you should registrate first with your email and password. After login, you start at level 0 and you
21
+ are able to generate recipe. So let's cook!
22
+
23
+ ## Installation
24
+
25
+ You are able to install this gem using this command:
26
+
27
+ $ gem install yum
28
+
29
+ Or you can download it from Github and install it this way:
30
+
31
+ $ gem build yum.gemspec
32
+ $ gem install yum-[VERSION].gem
33
+
34
+ ## Usage
35
+
36
+ All functionality of this command line application is provided trough the command ```yum```. Before using this
37
+ application, you must set two environment variables ```BACKENDLESS_KEY``` and ```YUMMLY_KEY```, which
38
+ are specific for this application.
39
+
40
+ _Because it is an academic application, you need to ask for the keys from author._
41
+
42
+ Registration:
43
+
44
+ $ yum register EMAIL PASSWORD
45
+
46
+ Login:
47
+
48
+ $ yum login EMAIL PASSWORD
49
+
50
+ Recipe may be generated only for the logged user, because its difficulty _(maximal time for cooking)_ is computed from user's level.
51
+ User is also able to set filter for searching in recipes _(holiday recipes, diet, natioanl cuisines, ingredients, phrase,...)_
52
+ The command that provides recipe looks like this:
53
+
54
+ $ yum [OPTIONS] recipe
55
+
56
+ The user is able to view his last recipe
57
+
58
+ $ yum last
59
+
60
+ Expected scenario how to play this _game_ is when user generaters the recipe, he cooks it after that. After all that
61
+ cooking and eating, user is able to review his prepared food from 0 to 3 points. The reviews provides XP points to user.
62
+ The user is able to review his last food only once. After that, his last food is deleted from the database.
63
+
64
+ $ yum review [0..3]
65
+
66
+ To display the user's stats _(email, level, xp)_, you should use this command:
67
+
68
+ $ yum user
69
+
70
+ ## Documentation
71
+
72
+ To generate the documentation using the ```yard``` use the command
73
+
74
+ $ yard doc
75
+
76
+ ## Tests
77
+
78
+ To run the tests using the ```rspec``` set the ```BACKENDLESS_KEY``` and ```YUMMLY_KEY``` and use the command:
79
+
80
+ $ rspec spec
81
+
82
+ ## License
83
+
84
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
85
+
86
+ ## Tasks
87
+
88
+ - [x] generates recipes from https://www.yummly.com/ for user depending on user's level
89
+ - [x] after cooking the food, the user or user's friend is able to evaluate the food
90
+ - [x] the user gain XP depending on the evaluation of the cooked food
91
+ - [x] the user is able to filter diet
92
+ - [x] the user is able to filter national cuisine
93
+ - [x] the user is able to filter ingredients
94
+ - [x] the user is able to choose aprox. time for cooking - _time depends on user's level_
95
+ - [x] the user is able to filter the holiday recipes
96
+ - [x] the user is able to set personal data in configuration file - _user doesn't need configuration file, application uses Backendless database_
97
+ - [x] docummentation
98
+ - [x] tests
data/bin/yum ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yum'
4
+
5
+ Yum::Yummer.cli
@@ -0,0 +1,116 @@
1
+ # Provides communication with Backendless API
2
+ # that is used to store data about the users
3
+ class Backendless
4
+ # Backendless application ID - this shouldn't change
5
+ @app_id = 'B04D050F-9131-884B-FFBE-F3A2EC48B300'
6
+
7
+ # Backendless application KEY - this must be set as
8
+ # the ENV variable 'BACKENDLESS_KEY'
9
+ @app_key = ENV['BACKENDLESS_KEY']
10
+
11
+ # URI to the Backendless including Application ID and KEY
12
+ @uri = "https://api.backendless.com/#{@app_id}/#{@app_key}/"
13
+
14
+ # Creates the generic connection to Backendless and add
15
+ # mandatory headers
16
+ #
17
+ # @return [Faraday connection] generic connection
18
+ def self.generic_connection
19
+ conn = Faraday.new(url: @uri)
20
+ conn.headers['Content-Type'] = 'application/json'
21
+ conn.headers['application-type'] = 'REST'
22
+ conn
23
+ end
24
+
25
+ # Register the user in the Backendless database
26
+ # and provides response
27
+ #
28
+ # @param email [String] email of the user
29
+ # @param password [String] password of the user
30
+ # @return [String, nil] The response of registration
31
+ def self.register(email, password)
32
+ conn = generic_connection
33
+ body = build_registration_body email, password
34
+ response = conn.post 'users/register', body.to_json
35
+ process_response response
36
+ end
37
+
38
+ # Login the user in the Backendless database
39
+ # and provides the response
40
+ #
41
+ # @param email [String] email of the user
42
+ # @param password [String] password of the user
43
+ # @return [String, nil] The response of login
44
+ def self.login(email, password)
45
+ conn = generic_connection
46
+ body = build_login_body email, password
47
+ response = conn.post 'users/login', body.to_json
48
+ process_response response
49
+ end
50
+
51
+ # Process the response of request according to status
52
+ #
53
+ # @param response [Faraday response] the response of request
54
+ # @return [String, nil] The response body
55
+ def self.process_response(response)
56
+ if response.status.equal? 200
57
+ response.body
58
+ elsif [409, 400, 401].include? response.status
59
+ nil
60
+ else
61
+ abort('I can not communicate with the user data. Set the BACKENDLESS_KEY')
62
+ end
63
+ end
64
+
65
+ # Builds the body of registration request
66
+ # with email and password
67
+ #
68
+ # @param email [String] email of the user
69
+ # @param password [String] password of the user
70
+ # @return [Hash] the body for registration
71
+ def self.build_registration_body(email, password)
72
+ body = {
73
+ email: email,
74
+ password: password
75
+ }
76
+ body
77
+ end
78
+
79
+ # Builds the body of login request
80
+ # with email and password
81
+ #
82
+ # @param login [String] email of the user
83
+ # @param password [String] password of the user
84
+ # @return [Hash] the body for login
85
+ def self.build_login_body(login, password)
86
+ body = {
87
+ login: login,
88
+ password: password
89
+ }
90
+ body
91
+ end
92
+
93
+ # Provides the last food of user
94
+ #
95
+ # @param user_object_id [String] ObjectID of the user data
96
+ # @return [String] The food id of last recipe
97
+ def self.last(user_object_id)
98
+ conn = generic_connection
99
+ response = conn.get "users/#{user_object_id}"
100
+ Parser.extract_food_id response.body
101
+ end
102
+
103
+ # Update the property in userdata
104
+ #
105
+ # @param object_id [String] the object ID of user data
106
+ # @param user_token [String] token receved from login operation
107
+ # @return [Faraday response, nil] The body of the response
108
+ def self.update(object_id, user_token, key, value)
109
+ conn = generic_connection
110
+ conn.headers['user-token'] = user_token
111
+ body = {}
112
+ body[key] = value
113
+ response = conn.put "data/users/#{object_id}", body.to_json
114
+ process_response response
115
+ end
116
+ end
@@ -0,0 +1,122 @@
1
+ # The command line interface parser
2
+ class CliParser
3
+ # Options of the command line parser
4
+ @options = {}
5
+
6
+ # help message
7
+ @help = {
8
+ holiday: 'Include/exclude holiday recipes',
9
+ ing: 'Include/exclude recipes with ingredients',
10
+ cui: 'Include/exclude recipes of national cuisines',
11
+ diet: 'Include/exclude diet recipes',
12
+ phrase: 'Includes recipes with phrase in name'
13
+ }
14
+
15
+ # building the banner
16
+ @banner = "Usage: yum [options] SUBCOMMAND\n"
17
+ @banner += "\nSUBCOMMANDS:\n"
18
+ @banner += "\trecipe\t\t-\t\tGenerates recipe according to parameters\n\n"
19
+ @banner += "\tregister USERNAME PASSWORD\t\t-\t\tRegister the new user\n\n"
20
+ @banner += "\tlogin USERNAME PASSWORD\t\t-\t\tLogin the user\n\n"
21
+ @banner += "\tlast\t\t-\t\tPrint the last generated food\n\n"
22
+ @banner += "\treview REVIEW\t\t-\t\tUser reviews the last generated"
23
+ @banner += "food from 0 to 3 points and gain the XP points\n\n"
24
+ @banner += "\tuser\t\t-\t\tPrints the user stats\n\n"
25
+
26
+ # Parse the options in command line
27
+ def self.parse_opts
28
+ OptionParser.new do |opts|
29
+ opts.banner = @banner
30
+
31
+ # help message option
32
+ opts.on('--help',
33
+ 'Prints help message') do
34
+ puts opts
35
+ exit
36
+ end
37
+
38
+ # version message option
39
+ opts.on('--version',
40
+ 'Prints version') do
41
+ puts "yum #{Yum::VERSION}"
42
+ exit
43
+ end
44
+
45
+ # phrase -----------------------------------------------------------------
46
+ # include recipe with phrase
47
+ opts.on('-p', '--phrase PHRASE', @help[:phrase]) do |phrase|
48
+ @options[:q] = phrase
49
+ end
50
+
51
+ # holiday ----------------------------------------------------------------
52
+ # include holiday recipes
53
+ opts.on('-h', '--holiday HOLIDAY', @help[:holiday]) do |holiday|
54
+ @options[:allowedHoliday] = holiday
55
+ end
56
+
57
+ # exclude holiday recipes
58
+ opts.on('--eh', '--excluded-holiday HOLIDAY', @help[:holiday]) do |hol|
59
+ @options[:excludedHoliday] = hol
60
+ end
61
+
62
+ # ingredients ------------------------------------------------------------
63
+ # include ingredients in recipes
64
+ opts.on('-i', '--ingredients INGREDIENT', @help[:ing]) do |ing|
65
+ @options[:allowedIngredient] = ing
66
+ end
67
+
68
+ # exclude holiday in recipes
69
+ opts.on('--ei', '--excluded-ingredient INGREDIENT', @help[:ing]) do |ing|
70
+ @options[:excludedIngredient] = ing
71
+ end
72
+
73
+ # cuisines ---------------------------------------------------------------
74
+ # include cuisines in recipes
75
+ opts.on('-c', '--cuisine CUISINE', @help[:cui]) do |cui|
76
+ @options[:allowedCuisine] = cui
77
+ end
78
+
79
+ # exclude cuisines in recipes
80
+ opts.on('--ec', '--excluded-cuisine CUISINE', @help[:cui]) do |cui|
81
+ @options[:excludedCuisine] = cui
82
+ end
83
+ # diets ------------------------------------------------------------------
84
+ # include diets in recipes
85
+ opts.on('-d', '--diet DIET', @help[:diet]) do |diet|
86
+ @options[:allowedDiet] = diet
87
+ end
88
+
89
+ # exclude diets in recipes
90
+ opts.on('--ed', '--excluded-diet DIET', @help[:diet]) do |diet|
91
+ @options[:excludedDiet] = diet
92
+ end
93
+ end.parse!
94
+ rescue OptionParser::InvalidOption => e
95
+ abort(e)
96
+ end
97
+
98
+ # Parse the arguments in the command line
99
+ def self.parse_argv
100
+ client = Client.new
101
+ subcommands = {
102
+ recipe: -> { client.recipe @options },
103
+ register: -> { client.register ARGV[1], ARGV[2] },
104
+ login: -> { client.login ARGV[1], ARGV[2] },
105
+ last: -> { client.last },
106
+ review: -> { client.review ARGV[1] },
107
+ user: -> { client.user }
108
+ }
109
+ call_subcommand subcommands[ARGV[0].to_sym]
110
+ end
111
+
112
+ # Tries to call subcommand extracted from hash
113
+ #
114
+ # @param command [String] subcommand to call
115
+ def self.call_subcommand(command)
116
+ if command.nil?
117
+ abort(@banner)
118
+ else
119
+ command.call
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,65 @@
1
+ # Encapsulate all the main functionality and represents the client
2
+ # using the user database and Yummly API
3
+ class Client
4
+ # Provides the recipe for the logged user according
5
+ # to his level and searching parameters
6
+ #
7
+ # @param parameters [Hash] searching parameters from from the command line
8
+ def recipe(parameters)
9
+ user = User.new
10
+ user.read_creddentials
11
+ user.login
12
+ generated_food = YumConnector.search parameters, user.xp
13
+ abort("Sorry, I can't find food for you like that") if generated_food.nil?
14
+ user.update_last_food_id generated_food.id
15
+ puts generated_food.to_str
16
+ end
17
+
18
+ # Register the user using Backendless
19
+ #
20
+ # @param email [String] email of the user
21
+ # @param password [String] password of the user
22
+ def register(email, password)
23
+ user = User.new
24
+ puts user.register email, password
25
+ end
26
+
27
+ # Login the user using Backendless
28
+ #
29
+ # @param email [String] email of the user
30
+ # @param password [String] password of the user
31
+ def login(email, password)
32
+ user = User.new
33
+ puts user.login email, password
34
+ end
35
+
36
+ # Provides the last recipe of logged user
37
+ def last
38
+ user = User.new
39
+ user.read_creddentials
40
+ user.login
41
+ last_food = YumConnector.food_by_id user.last
42
+ puts last_food.to_str
43
+ end
44
+
45
+ # Reviews the cooked food of the last recipe of logged user
46
+ def review(rating)
47
+ user = User.new
48
+ user.read_creddentials
49
+ user.login
50
+ response = user.review rating
51
+ if response
52
+ puts 'Last food reviewed!'
53
+ else
54
+ puts 'Nothing to review'
55
+ end
56
+ end
57
+
58
+ # Provides the stats of the user
59
+ def user
60
+ user = User.new
61
+ user.read_creddentials
62
+ user.login
63
+ puts user.to_str
64
+ end
65
+ end
@@ -0,0 +1,39 @@
1
+ # Represents single recipe from Yummly
2
+ class Food
3
+ # the id of the recipe on Yummly
4
+ attr_reader :id
5
+
6
+ # Creates food with the parameters from JSON
7
+ #
8
+ # @param food_json [JSON] recipe encapsulated in json from Yummly
9
+ def initialize(food_json)
10
+ @name = food_json['name']
11
+ @id = food_json['id']
12
+ @aprox_time = food_json['totalTime']
13
+ @image_url = image_url food_json
14
+ @ingredients = food_json['ingredientLines']
15
+ @guide_url = food_json['source']['sourceRecipeUrl']
16
+ end
17
+
18
+ # Extract image from json
19
+ #
20
+ # @param food_json [JSON] recipe encapsulated in json from Yummly
21
+ # @return [String] URL of the image
22
+ def image_url(food_json)
23
+ food_json['images'][0].values.first
24
+ end
25
+
26
+ # Provides stats of the user in human-readble form
27
+ #
28
+ # @return [String] stats of the user
29
+ def to_str
30
+ result = @name
31
+ result += "\nID: #{@id}"
32
+ result += "\n\nApproximate time: #{@aprox_time}"
33
+ result += "\nImage URL: #{@image_url}\n"
34
+ result += "\nIngredients:\n"
35
+ result += @ingredients.join("\n")
36
+ result += "\n\nStep-by-step: #{@guide_url}\n"
37
+ result
38
+ end
39
+ end
@@ -0,0 +1,85 @@
1
+ # Provides parsing methods of all kind
2
+ class Parser
3
+ # Extract the search value from json according to the description
4
+ #
5
+ # @param json [JSON] metadata from Yummly API
6
+ # @param description [String] local description of data
7
+ # @return [String] search value from metadata for Yummly API
8
+ def self.get_search_value(json, description)
9
+ json.each do |metapar|
10
+ ext_hash = metapar.select { |k, _| k.downcase.include? 'description' }
11
+ extern = ext_hash.values.first.downcase
12
+ local = description.downcase
13
+ return metapar['searchValue'] if "\b#{local}\b".match(extern)
14
+ end
15
+ end
16
+
17
+ # Parse the jsonp to json
18
+ #
19
+ # @param jsonp [String] jsonp format to parse
20
+ # @param key [String] key used in slicing jsonp
21
+ # @return [JSON] parsed json from jsonp
22
+ def self.jsonp_to_json(jsonp, key)
23
+ metadata_key = slice_metadata_key key
24
+ first_split = jsonp.split("set_metadata('#{metadata_key}', ")[1]
25
+ second_split = first_split.split(');')[0]
26
+ JSON.parse second_split
27
+ end
28
+
29
+ # Extracts the food id from json
30
+ #
31
+ # @param food_json [JSON] the food json
32
+ # @return [String] id of the food from json
33
+ def self.food_id(food_json)
34
+ abort("Sorry, I can't find food for you like that :(") if food_json.nil?
35
+ id = food_json['id']
36
+ id
37
+ end
38
+
39
+ # Extracts the metadata key from search parameter
40
+ #
41
+ # @param key [String] key to be sliced
42
+ # @return [String] metadata key
43
+ def self.slice_metadata_key(key)
44
+ metadata_key = key.to_s
45
+ metadata_key.slice!('allowed') if metadata_key.include? 'allowed'
46
+ metadata_key.slice!('excluded') if metadata_key.include? 'exclude'
47
+ metadata_key.downcase
48
+ end
49
+
50
+ # Extracts object ID from lofin response
51
+ #
52
+ # @param login_response [JSON] response from login
53
+ # @return [Strng] object ID of the user data
54
+ def self.login_object_id(login_response)
55
+ j = JSON.parse login_response
56
+ j['objectId']
57
+ end
58
+
59
+ # Extracts the food id from response
60
+ #
61
+ # @param response [String] response body containing the recipe
62
+ # @return [String] food id from response
63
+ def self.extract_food_id(response)
64
+ j = JSON.parse response
65
+ j['food_id']
66
+ end
67
+
68
+ # Extracts the user token from response
69
+ #
70
+ # @param response [String] response body containing user token after login
71
+ # @return [String] user token generated when logged in
72
+ def self.extract_user_token(response)
73
+ j = JSON.parse response
74
+ j['user-token']
75
+ end
76
+
77
+ # Extract value of XP points
78
+ #
79
+ # @param response [String] response bode containing the XP value
80
+ # @return [String] XP points of the user
81
+ def self.extract_xp(response)
82
+ j = JSON.parse response
83
+ j['xp']
84
+ end
85
+ end
@@ -0,0 +1,157 @@
1
+ # Manages Yummly requests using the Faraday adapter
2
+ class Request
3
+ # Yummly URI
4
+ @uri = 'http://api.yummly.com/v1/api/'
5
+
6
+ # application ID should not be changed
7
+ @app_id = 'a9ceda48'
8
+
9
+ # application KEY from the ENV variable
10
+ @app_key = ENV['YUMMLY_KEY']
11
+
12
+ # the maximum searched results
13
+ @max_results = 3
14
+
15
+ # Creates the generic connection for Yummly API
16
+ #
17
+ # @return [Faraday connection] connection to Yummly with mandatory headers
18
+ def self.generic_connection
19
+ conn = Faraday.new(url: @uri)
20
+ conn.headers['X-Yummly-App-ID'] = @app_id
21
+ conn.headers['X-Yummly-App-Key'] = @app_key
22
+ conn
23
+ end
24
+
25
+ # ----------------------------------------------------------------------------
26
+ # Search the recipes filtrated with parameters
27
+ #
28
+ # @param parameters [Hash] searching parameters from command line
29
+ # @param xp [Integer] XP points of user for setting max cooking time
30
+ # @return [String] Response body containing founded recipes
31
+ def self.search(parameters = {}, xp = 0)
32
+ # build generic connection
33
+ conn = generic_connection
34
+
35
+ # set the search parameters
36
+ search_parameters = {}
37
+
38
+ # add phrase if exists
39
+ add_phrase parameters, search_parameters
40
+
41
+ # add other search parameters
42
+ add_search_parameters parameters, search_parameters
43
+
44
+ # add max searching results
45
+ add_max_results search_parameters
46
+
47
+ # calculate max cooking time according to the user's XP
48
+ add_max_time search_parameters, xp
49
+
50
+ # execute the request to /recipes with search parameters
51
+ response = conn.get 'recipes', search_parameters
52
+
53
+ # return body if OK response
54
+ process_response response
55
+ end
56
+
57
+ # Provides recipe by ID
58
+ #
59
+ # @param food_id [String] recipe id of food on Yummly
60
+ # @return [String] response body containing the recipe data
61
+ def self.get_recipe(food_id)
62
+ # build generic connection
63
+ conn = generic_connection
64
+
65
+ # get food by its ID on yummly
66
+ response = conn.get "recipe/#{food_id}"
67
+
68
+ # return body if OK response
69
+ process_response response
70
+ end
71
+
72
+ # Processes response of all kind from Yummly
73
+ #
74
+ # @param response [Faraday response] response from Yummly
75
+ # @return [String] body of response
76
+ def self.process_response(response)
77
+ if response.status.eql? 200
78
+ response.body
79
+ else
80
+ abort('I can not communicate with Yummly. Set the YUMMLY_KEY')
81
+ end
82
+ end
83
+
84
+ # Add max results parameter to searching parameters
85
+ #
86
+ # @param parameters [Hash] searching parameters
87
+ def self.add_max_results(parameters)
88
+ parameters['maxResult'] = @max_results
89
+ end
90
+
91
+ # Add parameters from command line to searching parameters
92
+ #
93
+ # @param parameters [Hash] parameters from command line
94
+ # @param search_parameters [Hash] search parameters in proper form for request
95
+ def self.add_search_parameters(parameters, search_parameters)
96
+ parameters.each do |key, value|
97
+ metadata = get_metadata key
98
+ metadata_json = Parser.jsonp_to_json metadata, key
99
+ init_search_parameters search_parameters, key
100
+ multiple_parameters = value.split(',')
101
+ multiple_parameters.each do |single_parameter|
102
+ search_value = Parser.get_search_value metadata_json, single_parameter
103
+ build_search_parameters search_parameters, key, search_value
104
+ end
105
+ end
106
+ end
107
+
108
+ # Add phrase to searching parameters
109
+ #
110
+ # @param parameters [Hash] parameters from command line
111
+ # @param search_parameters [Hash] search parameters in proper form for request
112
+ def self.add_phrase(parameters, search_parameters)
113
+ hash_phrase = {}
114
+ hash_phrase[:q] = parameters[:q] if parameters[:q]
115
+ parameters.delete :q
116
+ init_search_parameters search_parameters, :q
117
+ build_search_parameters search_parameters, :q, hash_phrase.values.first
118
+ end
119
+
120
+ # Provides metadata by key
121
+ #
122
+ # @param key [String] key of metadata
123
+ # @return [String] response body of metadata
124
+ def self.get_metadata(key)
125
+ metadata_key = Parser.slice_metadata_key key
126
+ conn = generic_connection
127
+ meta_response = conn.get "metadata/#{metadata_key}"
128
+ process_response meta_response
129
+ end
130
+
131
+ # Initialize the search parameters array in hash on key position
132
+ #
133
+ # @param search_parameters [Hash] search parameters for request
134
+ # @param key [String] key of metadata
135
+ def self.init_search_parameters(search_parameters, key)
136
+ search_parameters[key] = []
137
+ end
138
+
139
+ # Build the array in hash on key position
140
+ #
141
+ # @param search_parameters [Hash] search parameters for request
142
+ # @param key [String] key from metadata
143
+ # @param search_value [String] value from metadata for searching
144
+ def self.build_search_parameters(search_parameters, key, search_value)
145
+ search_parameters[key].push search_value
146
+ end
147
+
148
+ # Add maximal time for cooking computed from XP points of user
149
+ #
150
+ # @param parameters [Hash] search parameters for request
151
+ # @param xp [String] XP points of the user
152
+ def self.add_max_time(parameters, xp)
153
+ parameter = xp * @max_results * 10
154
+ parameter = 80 if (xp * @max_results).eql? 0
155
+ parameters['maxTotalTimeInSeconds'] = parameter
156
+ end
157
+ end
@@ -0,0 +1,137 @@
1
+ # Represents the user of this application
2
+ class User
3
+ # has email, password, token and xp points during the runtime
4
+ attr_reader :email, :password, :token, :xp
5
+
6
+ # Constructor creates the new user with unset email and password
7
+ def self.initialize
8
+ @email = nil
9
+ @password = nil
10
+ end
11
+
12
+ # Register the user using Backendless
13
+ #
14
+ # @param email [String] email of the user
15
+ # @param password [String] password of the user
16
+ # @return [String] state of the registration in human-readable form
17
+ def register(email, password)
18
+ response = Backendless.register email, password
19
+ if response
20
+ 'Registration sucessful'
21
+ else
22
+ 'Registration failed'
23
+ end
24
+ end
25
+
26
+ # Login the user using Backendless
27
+ #
28
+ # @param email [String] email of the user
29
+ # @param password [String] password of the user
30
+ # @return [String] state of the login in human-readable form
31
+ def login(email = @email, password = @password)
32
+ response = Backendless.login email, password
33
+ if response
34
+ process_login_response email, password, response
35
+ store_creddentials
36
+ 'Login success'
37
+ else
38
+ abort('Login failed')
39
+ end
40
+ end
41
+
42
+ # Process the login response and set the attributes of user
43
+ #
44
+ # @param email [String] email of the user
45
+ # @param password [String] password of the user
46
+ # @param response [JSON] response from login to Backendless
47
+ def process_login_response(email, password, response)
48
+ @email = email
49
+ @password = password
50
+ @token = Parser.extract_user_token response
51
+ @id = Parser.login_object_id response
52
+ @xp = Parser.extract_xp response
53
+ end
54
+
55
+ # Provides the last recipe of user
56
+ #
57
+ # @return [String] the last food id of the user
58
+ def last
59
+ login_response = Backendless.login @email, @password
60
+ object_id = Parser.login_object_id login_response
61
+ Backendless.last(object_id)
62
+ end
63
+
64
+ # Stores the credentials to 'creddentials.csv' to achieve persistence
65
+ def store_creddentials
66
+ CSV.open('creddentials.csv', 'w') do |csv|
67
+ csv << [@email, @password]
68
+ end
69
+ read_creddentials
70
+ end
71
+
72
+ # Reads the credentials from the 'creddentials.csv' and set them
73
+ def read_creddentials
74
+ creddentials = []
75
+ CSV.foreach 'creddentials.csv' do |record|
76
+ creddentials << record
77
+ end
78
+ @email = creddentials[0][0]
79
+ @password = creddentials[0][1]
80
+ rescue StandardError => e
81
+ abort("You are not logged in!\nERROR:\n#{e}")
82
+ end
83
+
84
+ # Updates the last food id in Backendless database
85
+ #
86
+ # @param food_id [String] the id of food from Yummly
87
+ # @return [true, nil] the state of the operation
88
+ def update_last_food_id(food_id)
89
+ response = Backendless.update(@id, @token, 'food_id', food_id)
90
+ true if response
91
+ end
92
+
93
+ # Reviews the last food of the user
94
+ #
95
+ # @param rating [String] the rating of the last food from 0 to 3
96
+ # @return [true, nil] the status of the operation
97
+ def review(rating)
98
+ real_rating = check_rating rating
99
+ @xp += real_rating
100
+ last_food = Backendless.last @id
101
+ return false if last_food.eql? 'Empty plate'
102
+ response = Backendless.update(@id, @token, 'xp', @xp)
103
+ update_last_food_id 'Empty plate' if response
104
+ true if response
105
+ end
106
+
107
+ # Checks the boundary on food rating
108
+ #
109
+ # @param rating [String] rating of food
110
+ # @return [String] real rating in proper interval
111
+ def check_rating(rating)
112
+ abort('Rating points missing!') if rating.nil?
113
+ real_rating = Integer(rating)
114
+ real_rating = 3 if real_rating > 3
115
+ real_rating = 0 if real_rating < 0
116
+ real_rating
117
+ end
118
+
119
+ # Provides the user stats in human-readable form
120
+ #
121
+ # @return [String] user stats
122
+ def to_str
123
+ result = "Email: #{@email}\n"
124
+ lvl = level xp
125
+ result += "Level: #{lvl}\n"
126
+ result += "XP: #{@xp}\n"
127
+ result
128
+ end
129
+
130
+ # Computes the level from XP
131
+ #
132
+ # @param xp [Integer] XP points of the user gained from ratings of his food
133
+ # @return [String] level of the user
134
+ def level(xp)
135
+ xp / 10
136
+ end
137
+ end
@@ -0,0 +1,25 @@
1
+ require 'yum/version'
2
+ require 'csv'
3
+ require 'faraday'
4
+ require 'optparse'
5
+ require 'json'
6
+ require 'backendless'
7
+ require 'cli_parser'
8
+ require 'client'
9
+ require 'food'
10
+ require 'parser'
11
+ require 'request'
12
+ require 'user'
13
+ require 'yum_connector'
14
+
15
+ # Encapsulate the Yum application
16
+ module Yum
17
+ # Uniters all main methods of user interface
18
+ class Yummer
19
+ # Unites Command Line parser methods
20
+ def self.cli
21
+ CliParser.parse_opts
22
+ CliParser.parse_argv
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ # This is the module of Yum application.
2
+ # This module encapsulate functionality that provides this application
3
+ # Example:
4
+ # user regtration,
5
+ # user login,
6
+ # user stats,
7
+ # recipe generation,
8
+ # recipe review,
9
+ # leveling in cooking skills,
10
+ module Yum
11
+ # version of the gem
12
+ VERSION = '0.1.6'
13
+ end
@@ -0,0 +1,26 @@
1
+ # Handles connection using Requests and provides the results to client
2
+ class YumConnector
3
+ # Searches the food according to search parameters from command line
4
+ #
5
+ # @param parameters [Hash] parameters from the command line
6
+ # @param xp [Integer] the XP points of the user
7
+ # @return [Food] new food randomly chosen from searched results
8
+ def self.search(parameters, xp)
9
+ result = Request.search parameters, xp
10
+ return nil if result.nil?
11
+ searched_result = JSON.parse result
12
+ id = Parser.food_id searched_result['matches'].sample
13
+ food_json = JSON.parse Request.get_recipe id
14
+ Food.new food_json
15
+ end
16
+
17
+ # Provides the food from Yummly by its id
18
+ #
19
+ # @param food_id [String] the id of the food on Yummly
20
+ # @return [Food] the food recipe from Yummly
21
+ def self.food_by_id(food_id)
22
+ return food_id if food_id.eql? 'Empty plate'
23
+ food_json = JSON.parse Request.get_recipe food_id
24
+ Food.new food_json
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path('lib/yum/version', __dir__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'yum'
5
+ s.version = Yum::VERSION
6
+ s.homepage = 'https://github.com/HalfDeadPie/yum-lite-ruby'
7
+ s.license = 'MIT'
8
+ s.author = 'Simon Stefunko'
9
+ s.email = 's.stefunko@gmail.com'
10
+
11
+ s.summary = 'Improve your cooking skills'
12
+
13
+ s.files = Dir['bin/*', 'lib/**/*', '*.gemspec', 'LICENSE*', 'README*']
14
+ s.executables = Dir['bin/*'].map { |f| File.basename(f) }
15
+ s.has_rdoc = 'yard'
16
+
17
+ s.required_ruby_version = '>= 2.2'
18
+ s.add_runtime_dependency 'faraday', '~> 0.14', '>= 0.14.0'
19
+ s.add_runtime_dependency 'json', '~> 2.0', '>= 2.0.4'
20
+
21
+ s.add_development_dependency 'rake', '~> 12.0'
22
+ s.add_development_dependency 'rspec', '~> 3.6'
23
+ s.add_development_dependency 'yard', '~> 0.9'
24
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ platform: ruby
6
+ authors:
7
+ - Simon Stefunko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.14'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.14.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.14'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.14.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: json
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.0.4
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '2.0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 2.0.4
53
+ - !ruby/object:Gem::Dependency
54
+ name: rake
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '12.0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '12.0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rspec
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '3.6'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '3.6'
81
+ - !ruby/object:Gem::Dependency
82
+ name: yard
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.9'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '0.9'
95
+ description:
96
+ email: s.stefunko@gmail.com
97
+ executables:
98
+ - yum
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - LICENSE
103
+ - LICENSE.txt
104
+ - README.md
105
+ - bin/yum
106
+ - lib/backendless.rb
107
+ - lib/cli_parser.rb
108
+ - lib/client.rb
109
+ - lib/food.rb
110
+ - lib/parser.rb
111
+ - lib/request.rb
112
+ - lib/user.rb
113
+ - lib/yum.rb
114
+ - lib/yum/version.rb
115
+ - lib/yum_connector.rb
116
+ - yum.gemspec
117
+ homepage: https://github.com/HalfDeadPie/yum-lite-ruby
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '2.2'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.6.13
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Improve your cooking skills
141
+ test_files: []