grape-transformations 0.0.1

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.
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