yum 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +98 -0
- data/bin/yum +5 -0
- data/lib/backendless.rb +116 -0
- data/lib/cli_parser.rb +122 -0
- data/lib/client.rb +65 -0
- data/lib/food.rb +39 -0
- data/lib/parser.rb +85 -0
- data/lib/request.rb +157 -0
- data/lib/user.rb +137 -0
- data/lib/yum.rb +25 -0
- data/lib/yum/version.rb +13 -0
- data/lib/yum_connector.rb +26 -0
- data/yum.gemspec +24 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -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.
|
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
data/lib/backendless.rb
ADDED
@@ -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
|
data/lib/cli_parser.rb
ADDED
@@ -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
|
data/lib/client.rb
ADDED
@@ -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
|
data/lib/food.rb
ADDED
@@ -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
|
data/lib/parser.rb
ADDED
@@ -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
|
data/lib/request.rb
ADDED
@@ -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
|
data/lib/user.rb
ADDED
@@ -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
|
data/lib/yum.rb
ADDED
@@ -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
|
data/lib/yum/version.rb
ADDED
@@ -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
|
data/yum.gemspec
ADDED
@@ -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: []
|