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