yum 0.1.6

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: 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: []