grape-transformations 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +19 -0
  5. data/lib/grape/generators/templates/entity.rb +12 -0
  6. data/lib/grape/generators/templates/grape-transformations.rb +4 -0
  7. data/lib/grape/generators/transformations/entity_generator.rb +54 -0
  8. data/lib/grape/generators/transformations/install_generator.rb +29 -0
  9. data/lib/grape/tasks/grapi_tasks.rake +4 -0
  10. data/lib/grape/transformations/base.rb +67 -0
  11. data/lib/grape/transformations/engine.rb +14 -0
  12. data/lib/grape/transformations/loader.rb +86 -0
  13. data/lib/grape/transformations/version.rb +5 -0
  14. data/lib/grape/transformations.rb +112 -0
  15. data/spec/api/animal_spec.rb +37 -0
  16. data/spec/api/user_spec.rb +55 -0
  17. data/spec/generators/entity_generator_spec.rb +25 -0
  18. data/spec/generators/install_generator_spec.rb +33 -0
  19. data/spec/grapi_spec.rb +132 -0
  20. data/spec/spec_helper.rb +78 -0
  21. data/spec/test_app/README.rdoc +28 -0
  22. data/spec/test_app/Rakefile +6 -0
  23. data/spec/test_app/app/api/api.rb +7 -0
  24. data/spec/test_app/app/api/test_app/entities/animals/compact.rb +10 -0
  25. data/spec/test_app/app/api/test_app/entities/animals/full.rb +12 -0
  26. data/spec/test_app/app/api/test_app/entities/food.rb +8 -0
  27. data/spec/test_app/app/api/test_app/entities/users/compact.rb +10 -0
  28. data/spec/test_app/app/api/test_app/entities/users/default.rb +13 -0
  29. data/spec/test_app/app/api/test_app/modules/animal.rb +39 -0
  30. data/spec/test_app/app/api/test_app/modules/user.rb +39 -0
  31. data/spec/test_app/app/assets/javascripts/application.js +13 -0
  32. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  33. data/spec/test_app/app/controllers/application_controller.rb +5 -0
  34. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  35. data/spec/test_app/app/models/animal.rb +12 -0
  36. data/spec/test_app/app/models/food.rb +7 -0
  37. data/spec/test_app/app/models/user.rb +13 -0
  38. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  39. data/spec/test_app/bin/bundle +3 -0
  40. data/spec/test_app/bin/rails +4 -0
  41. data/spec/test_app/bin/rake +4 -0
  42. data/spec/test_app/config/application.rb +32 -0
  43. data/spec/test_app/config/boot.rb +5 -0
  44. data/spec/test_app/config/database.yml +25 -0
  45. data/spec/test_app/config/environment.rb +5 -0
  46. data/spec/test_app/config/environments/development.rb +37 -0
  47. data/spec/test_app/config/environments/production.rb +78 -0
  48. data/spec/test_app/config/environments/test.rb +39 -0
  49. data/spec/test_app/config/initializers/assets.rb +8 -0
  50. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  51. data/spec/test_app/config/initializers/cookies_serializer.rb +3 -0
  52. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  53. data/spec/test_app/config/initializers/grapi.rb +4 -0
  54. data/spec/test_app/config/initializers/inflections.rb +16 -0
  55. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  56. data/spec/test_app/config/initializers/session_store.rb +3 -0
  57. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/test_app/config/locales/en.yml +23 -0
  59. data/spec/test_app/config/routes.rb +3 -0
  60. data/spec/test_app/config/secrets.yml +22 -0
  61. data/spec/test_app/config.ru +4 -0
  62. data/spec/test_app/db/development.sqlite3 +0 -0
  63. data/spec/test_app/log/development.log +72 -0
  64. data/spec/test_app/public/404.html +67 -0
  65. data/spec/test_app/public/422.html +67 -0
  66. data/spec/test_app/public/500.html +66 -0
  67. data/spec/test_app/public/favicon.ico +0 -0
  68. data/spec/test_app/tmp/cache/7F3/650/registered_entities +1 -0
  69. metadata +400 -0
@@ -0,0 +1,37 @@
1
+ describe 'Animal Endpoint', :type => :request do
2
+
3
+ context :v1 do
4
+
5
+ let(:first_animal) { Animal.new name: 'Cow', description: 'Are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae', phylum: 'Chordata', diet: 'vegetarian' }
6
+ let(:second_animal) { Animal.new name: 'Capybara', description: 'is the largest rodent in the world. Its closest relatives are guinea pigs and rock cavies', phylum: 'Chordata', diet: 'vegetarian' }
7
+
8
+ context 'GET' do
9
+ describe '/foo' do
10
+ before(:each){ allow(::Animal).to receive(:all) { [first_animal, second_animal] } }
11
+
12
+ it 'gets all animals with default transformation' do
13
+ expected_response = "[{\"name\":\"Cow\",\"description\":\"Are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae\",\"phylum\":\"Chordata\",\"diet\":\"vegetarian\"},{\"name\":\"Capybara\",\"description\":\"is the largest rodent in the world. Its closest relatives are guinea pigs and rock cavies\",\"phylum\":\"Chordata\",\"diet\":\"vegetarian\"}]"
14
+ get '/api/v1/animals/foo'
15
+ expect(response.status).to eq 200
16
+ expect(response.body).to match expected_response
17
+ #RSpec::Mocks.space.proxy_for(::User).reset # It is not necessary
18
+ end
19
+
20
+ end
21
+ describe '/bar/:id' do
22
+ before(:each){ allow(::Animal).to receive(:find) { first_animal } }
23
+
24
+ it 'gets specific animal with default transformation' do
25
+ expected_response = "{\"name\":\"Cow\",\"description\":\"Are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae\"}"
26
+ get '/api/v1/animals/bar/0'
27
+ expect(response.status).to eq 200
28
+ expect(response.body).to match expected_response
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,55 @@
1
+ describe 'User Endpoint', :type => :request do
2
+
3
+ context :v1 do
4
+
5
+ let(:first_user) { User.new name: 'Allam Britto', age: 24, birthday: Date.parse('15-06-1990'), phone: '555-5555', address: 'Fake st. 1-23' }
6
+ let(:second_user) { User.new name: 'Elva Lasso', age: 25, birthday: Date.parse('15-06-1989'), phone: '777-5555', address: 'Fake st. 1-25' }
7
+
8
+ context 'GET' do
9
+ describe 'default transformation' do
10
+ before(:each){ allow(::User).to receive(:all) { [first_user, second_user] } }
11
+ describe '/' do
12
+ it 'gets all users with default transformation' do
13
+ expected_response = "[{\"name\":\"Allam Britto\",\"age\":24,\"birthday\":\"1990-06-15T00:00:00.000+00:00\",\"phone\":\"555-5555\",\"address\":\"Fake st. 1-23\"},{\"name\":\"Elva Lasso\",\"age\":25,\"birthday\":\"1989-06-15T00:00:00.000+00:00\",\"phone\":\"777-5555\",\"address\":\"Fake st. 1-25\"}]"
14
+ get '/api/v1/users'
15
+ expect(response.status).to eq 200
16
+ expect(response.body).to match expected_response
17
+ #RSpec::Mocks.space.proxy_for(::User).reset # It is not necessary
18
+ end
19
+ end
20
+
21
+ describe '/compact' do
22
+ it 'gets all users with compact transformation' do
23
+ expected_response = "[{\"name\":\"Allam Britto\",\"phone\":\"555-5555\"},{\"name\":\"Elva Lasso\",\"phone\":\"777-5555\"}]"
24
+ get '/api/v1/users/compact'
25
+ expect(response.status).to eq 200
26
+ expect(response.body).to match expected_response
27
+ end
28
+ end
29
+ end
30
+ describe 'compact transformation' do
31
+ before(:each){ allow(::User).to receive(:find) { first_user } }
32
+ describe '/:id' do
33
+ it 'gets specific user with default transformation' do
34
+ expected_response = "{\"name\":\"Allam Britto\",\"age\":24,\"birthday\":\"1990-06-15T00:00:00.000+00:00\",\"phone\":\"555-5555\",\"address\":\"Fake st. 1-23\"}"
35
+ get '/api/v1/users/0'
36
+ expect(response.status).to eq 200
37
+ expect(response.body).to match expected_response
38
+ end
39
+ end
40
+
41
+ describe 'compact/:id' do
42
+ it 'gets specific user with default transformation' do
43
+ expected_response = "{\"name\":\"Allam Britto\",\"phone\":\"555-5555\"}"
44
+ get '/api/v1/users/compact/0'
45
+ expect(response.status).to eq 200
46
+ expect(response.body).to match expected_response
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ require 'grape/generators/transformations/entity_generator'
4
+
5
+ describe Grape::Generators::Transformations::EntityGenerator, :type => :generator do
6
+
7
+ destination File.expand_path('../../../tmp/tests', __FILE__)
8
+
9
+ before { prepare_destination }
10
+
11
+ context 'entity generating process' do
12
+
13
+ describe 'simple entity' do
14
+
15
+ before(:each){ run_generator %w(user) }
16
+
17
+ subject { file 'app/api/test_app/entities/users/default.rb' }
18
+
19
+ it { should exist }
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ require 'grape/generators/transformations/install_generator'
4
+
5
+ describe Grape::Generators::Transformations::InstallGenerator, :type => :generator do
6
+
7
+ destination File.expand_path('../../../tmp/tests', __FILE__)
8
+
9
+ before { prepare_destination }
10
+
11
+ context 'the generated files' do
12
+
13
+ before { run_generator }
14
+
15
+ describe 'initializer' do
16
+
17
+ subject { file 'config/initializers/grape-transformations.rb' }
18
+
19
+ it { should exist }
20
+
21
+ end
22
+
23
+ describe 'entities folder' do
24
+
25
+ subject { file 'app/api/test_app/entities/.keep' }
26
+
27
+ it { should exist }
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,132 @@
1
+ describe Grape::Transformations do
2
+ it 'verifies if Grape::Transformations is defined' do
3
+ expect(defined?(Grape::Transformations)).to_not be_nil
4
+ end
5
+ context 'when grape-transformations conventions have been followed' do
6
+ describe 'grape-transformations paths' do
7
+ it 'verifies the root api path' do
8
+ expected_response = 'grape-transformations/spec/test_app/app/api'
9
+ root_api_path = Grape::Transformations.root_api_path
10
+ expect(root_api_path).to end_with expected_response
11
+ end
12
+ it 'verifies the relative path to entities' do
13
+ expected_response = 'test_app/entities'
14
+ relative_path_to_entities = Grape::Transformations.relative_path_to_entities
15
+ expect(relative_path_to_entities).to match expected_response
16
+ end
17
+ it 'verifies the namespace related to entities' do
18
+ expected_response = 'TestApp::Entities'
19
+ root_entity_namespace = Grape::Transformations.root_entity_namespace
20
+ expect(root_entity_namespace).to match expected_response
21
+ end
22
+ it 'verifies full path to entities' do
23
+ expected_response = 'grape-transformations/spec/test_app/app/api/test_app/entities'
24
+ full_path_to_entities = Grape::Transformations.full_path_to_entities
25
+ expect(full_path_to_entities).to end_with expected_response
26
+ end
27
+ end
28
+ context 'when there are multiple entities already created' do
29
+ it 'has registered entities' do
30
+ expected_response = {
31
+ "Food" => "TestApp::Entities::Food",
32
+ "User" => "TestApp::Entities::Users::Default"
33
+ }
34
+ registered_entities = Grape::Transformations.registered_entities
35
+ expect(registered_entities).to match expected_response
36
+ end
37
+ describe 'User' do
38
+ it 'verifies all entities' do
39
+ expected_response = '[TestApp::Entities::Users::Compact, TestApp::Entities::Users::Default]'
40
+ all_entities_for_user = Grape::Transformations.all_entities_for(User).inspect
41
+ expect(all_entities_for_user).to match expected_response
42
+ end
43
+ it 'verifies entity for compact transformation' do
44
+ expected_response = 'TestApp::Entities::Users::Compact'
45
+ entity_for_compact_transformation = Grape::Transformations.entity_for_transformation('User', 'Compact').inspect
46
+ expect(entity_for_compact_transformation).to match expected_response
47
+ end
48
+ it 'verifies entity for default transformation' do
49
+ expected_response = 'TestApp::Entities::Users::Default'
50
+ entity_for_compact_transformation = Grape::Transformations.entity_for_transformation('User', 'Default').inspect
51
+ expect(entity_for_compact_transformation).to match expected_response
52
+ end
53
+ it 'verifies all available entity transformations' do
54
+ expected_response = '[TestApp::Entities::Users::Compact]'
55
+ all_available_entity_transformations = Grape::Transformations.all_transformation_entities_for('User').inspect
56
+ expect(all_available_entity_transformations).to match expected_response
57
+ end
58
+ it 'verifies the registered entities' do
59
+ expected_response = 'TestApp::Entities::Users::Default'
60
+ registered_entity = Grape::Transformations.registered_entity_for 'User'
61
+ expect(registered_entity).to match expected_response
62
+ end
63
+ it 'verifies all available entity transformations' do
64
+ expected_response = '[:compact]'
65
+ all_available_transformations = Grape::Transformations.transformations_for('User').inspect
66
+ expect(all_available_transformations).to match expected_response
67
+ end
68
+ end
69
+ describe 'Food' do
70
+ it 'verifies the registered entities' do
71
+ expected_response = 'TestApp::Entities::Food'
72
+ registered_entity = Grape::Transformations.registered_entity_for 'Food'
73
+ expect(registered_entity).to match expected_response
74
+ end
75
+ it 'verifies all available entity transformations' do
76
+ expected_response = '[]'
77
+ all_available_transformations = Grape::Transformations.transformations_for('Food').inspect
78
+ expect(all_available_transformations).to match expected_response
79
+ end
80
+ end
81
+ end
82
+ describe 'Grape::Endpoint' do
83
+ describe 'User' do
84
+ let(:user) { User.new name: 'Allam Britto', age: 24, birthday: Date.parse('15-06-1990'), phone: '555-5555', address: 'Fake st. 1-23' }
85
+ let(:endpoint) { Grape::Endpoint.new({}, path: '/', method: 'get')}
86
+
87
+ it 'presents a default representation' do
88
+ endpoint.instance_variable_set :@env, {}
89
+ expected_response = "{\"name\":\"Allam Britto\",\"age\":24,\"birthday\":\"1990-06-15T00:00:00.000+00:00\",\"phone\":\"555-5555\",\"address\":\"Fake st. 1-23\"}"
90
+ representation = endpoint.present(user).to_json
91
+ expect(representation).to match expected_response
92
+ end
93
+ it 'presents a compact representation' do
94
+ endpoint.instance_variable_set :@env, {}
95
+ expected_response = "{\"name\":\"Allam Britto\",\"phone\":\"555-5555\"}"
96
+ representation = endpoint.present(user, with: TestApp::Entities::Users::Compact).to_json
97
+ expect(representation).to match expected_response
98
+ end
99
+ end
100
+ describe 'Food' do
101
+ let(:food) { Food.new name: 'Omelette', description: 'In cuisine, an omelette or omelet is a dish made from beaten eggs quickly cooked with butter or oil in a frying pan' }
102
+ let(:endpoint) { Grape::Endpoint.new({}, path: '/', method: 'get')}
103
+
104
+ it 'presents a default representation' do
105
+ endpoint.instance_variable_set :@env, {}
106
+ expected_response = "{\"name\":\"Omelette\",\"description\":\"In cuisine, an omelette or omelet is a dish made from beaten eggs quickly cooked with butter or oil in a frying pan\"}"
107
+ representation = endpoint.present(food).to_json
108
+ expect(representation).to match expected_response
109
+ end
110
+ end
111
+ end
112
+ end
113
+ context 'when grape-transformations conventions have not been followed' do
114
+ describe 'Animal' do
115
+ let(:animal) { Animal.new name: 'Cow', description: 'Are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae', phylum: 'Chordata', diet: 'vegetarian' }
116
+ let(:endpoint) { Grape::Endpoint.new({}, path: '/', method: 'get')}
117
+
118
+ it 'presents without entity representation' do
119
+ endpoint.instance_variable_set :@env, {}
120
+ expected_response = "{\"name\":\"Cow\",\"description\":\"Are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae\",\"phylum\":\"Chordata\",\"diet\":\"vegetarian\"}"
121
+ representation = endpoint.present(animal).to_json
122
+ expect(representation).to match expected_response
123
+ end
124
+ it 'presents with compact entity representation' do
125
+ endpoint.instance_variable_set :@env, {}
126
+ expected_response = "{\"name\":\"Cow\",\"description\":\"Are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae\"}"
127
+ representation = endpoint.present(animal, with: TestApp::Entities::Animals::Compact).to_json
128
+ expect(representation).to match expected_response
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,78 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ require 'codeclimate-test-reporter'
18
+ require 'simplecov'
19
+
20
+ formatters = [SimpleCov::Formatter::HTMLFormatter]
21
+ if ENV['CODECLIMATE_REPO_TOKEN']
22
+ formatters << CodeClimate::TestReporter::Formatter
23
+ CodeClimate::TestReporter.start
24
+ end
25
+
26
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[*formatters]
27
+ SimpleCov.start do
28
+ add_filter '/spec/'
29
+ end
30
+
31
+ require 'grape/transformations'
32
+ require 'pry'
33
+ require 'grape'
34
+ require 'grape_entity'
35
+
36
+ require File.expand_path("../test_app/config/environment", __FILE__)
37
+
38
+ require 'rails/all'
39
+ require 'rspec/rails'
40
+ require 'rspec/mocks'
41
+
42
+ require 'ammeter/init'
43
+
44
+ RSpec.configure do |config|
45
+
46
+ config.after(:suite) do
47
+ # Removes the tests directory used for generators testing
48
+ File.join File.expand_path("../", __FILE__), "/tmp/tests"
49
+ end
50
+
51
+ config.include RSpec::Rails::RequestExampleGroup, type: :request, parent_example_group: { file_path: /spec\/api/ }
52
+
53
+ # rspec-expectations config goes here. You can use an alternate
54
+ # assertion/expectation library such as wrong or the stdlib/minitest
55
+ # assertions if you prefer.
56
+ config.expect_with :rspec do |expectations|
57
+ # This option will default to `true` in RSpec 4. It makes the `description`
58
+ # and `failure_message` of custom matchers include text for helper methods
59
+ # defined using `chain`, e.g.:
60
+ # be_bigger_than(2).and_smaller_than(4).description
61
+ # # => "be bigger than 2 and smaller than 4"
62
+ # ...rather than:
63
+ # # => "be bigger than 2"
64
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
65
+ end
66
+
67
+ # rspec-mocks config goes here. You can use an alternate test double
68
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
69
+ config.mock_with :rspec do |mocks|
70
+ # Prevents you from mocking or stubbing a method that does not exist on
71
+ # a real object. This is generally recommended, and will default to
72
+ # `true` in RSpec 4.
73
+ mocks.verify_partial_doubles = true
74
+ end
75
+
76
+ config.filter_run :focus => true
77
+ config.run_all_when_everything_filtered = true
78
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,7 @@
1
+ class API < Grape::API
2
+ prefix 'api'
3
+ # Separate the api into smaller
4
+ # modules like this
5
+ mount TestApp::Modules::User
6
+ mount TestApp::Modules::Animal
7
+ end
@@ -0,0 +1,10 @@
1
+ module TestApp
2
+ module Entities
3
+ module Animals
4
+ class Compact < Grape::Entity
5
+ expose :name, documentation: { type: "string", desc: "animal name", example: 'Cow' }
6
+ expose :description, documentation: { type: "string", desc: "animal name", example: 'Are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae' }
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module TestApp
2
+ module Entities
3
+ module Animals
4
+ class Full < Grape::Entity
5
+ expose :name, documentation: { type: "string", desc: "animal name", example: 'Cow' }
6
+ expose :description, documentation: { type: "string", desc: "animal name", example: 'Are the most common type of large domesticated ungulates. They are a prominent modern member of the subfamily Bovinae' }
7
+ expose :phylum, documentation: { type: "string", desc: "user age", example: 'Chordata' }
8
+ expose :diet, documentation: { type: "string", desc: "address", example: 'vegetarian' }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module TestApp
2
+ module Entities
3
+ class Food < Grape::Entity
4
+ expose :name, documentation: { type: "string", desc: "food name", example: 'Mondongo' }
5
+ expose :description, documentation: { type: "string", desc: "food description", example: 'is a soup made from diced tripe (the stomach of a cow) slow-cooked with vegetables such as bell peppers, onions, carrots, cabbage, celery, tomatoes, cilantro (coriander), garlic or root vegetables.' }
6
+ end
7
+ end
8
+ end