introspective_grape 0.3.7 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 09b793ec0cbab1f44378e04625ebdc28c9d41b96
4
- data.tar.gz: 50391f80743668ef3ce2d2e56ff03d58ba64fe7d
2
+ SHA256:
3
+ metadata.gz: d65a93a41cf1a7afd03d352bd9856daa53d2bcfc4f22fa6674ec7f19bb21251c
4
+ data.tar.gz: 907d5178a60e8dedbddc7f9854f8796ba337b4d6eb5c5d0ce99c699bb74535ed
5
5
  SHA512:
6
- metadata.gz: 82dfdfd7bab51b280f7c5c58e319f2798165cab22c92c69ebfecd5d1cfbf5f65cf156e8680594426ee130d1c57130ffa64694b2183a04e906953ccc883e9d936
7
- data.tar.gz: c53697d28d804fd4443515c5c364c3aafc6f8fdc735f82dc56796f62de2a7597d84a029f7ffa51d85cba84a0877d2e1748323e08c0625dd62e487e97ad5ca2dc
6
+ metadata.gz: 43e3835f5f5573a9c896e1189933a7ed7a2ac889fe644e13b6fefedd801fd601ec887e77881224ba9c86055184338521296149460b814970800100fa94400b98
7
+ data.tar.gz: 228f587da2205b9895235c1b5803d52e7b1dcb722a89d67ecde525a1253fa79dc1eef20bba4989155f8be8e35ae05899a74c9728f2ab42d337b6af3f6795fbfb
data/.travis.yml CHANGED
@@ -7,20 +7,12 @@ script:
7
7
  - bundle exec rspec
8
8
 
9
9
  rvm:
10
- # - 2.0.0
11
- # - 2.1.0
12
- - 2.2.2
13
10
  - 2.3.1
14
11
  - 2.4.0
12
+ - 2.5.0
15
13
  - ruby-head
16
14
  - jruby-head
17
15
  gemfile:
18
- # - gemfiles/Gemfile.rails.3.2.22
19
- # - gemfiles/Gemfile.rails.4.1.13
20
- # - gemfiles/Gemfile.rails.4.2.7.1
21
- # - gemfiles/Gemfile.rails.4.2.7.1.new.swagger
22
- # - gemfiles/Gemfile.rails.4.2.8
23
- # - gemfiles/Gemfile.rails.5.0.0
24
16
  - gemfiles/Gemfile.rails.5.0.1
25
17
  - gemfiles/Gemfile.rails.5.1.0
26
18
  - gemfiles/Gemfile.rails.5.2.0
@@ -28,19 +20,6 @@ gemfile:
28
20
 
29
21
  matrix:
30
22
  exclude:
31
- # We'll have to back up to grape-swagger 0.11 for earlier rubies.
32
- - rvm: 2.0.0
33
- gemfile: gemfiles/Gemfile.rails.5.0.1
34
- - rvm: 2.0.0
35
- gemfile: gemfiles/Gemfile.rails.master
36
- - rvm: 2.1.0
37
- gemfile: gemfiles/Gemfile.rails.5.0.1
38
- - rvm: 2.1.0
39
- gemfile: gemfiles/Gemfile.rails.master
40
- - rvm: 2.2.0
41
- gemfile: gemfiles/Gemfile.rails.5.0.1
42
- - rvm: 2.2.0
43
- gemfile: gemfiles/Gemfile.rails.master
44
23
  - rvm: jruby-9.0.4.0
45
24
  gemfile: gemfiles/Gemfile.rails.5.0.1
46
25
  - rvm: jruby-9.0.4.0
data/CHANGELOG.md CHANGED
@@ -1,9 +1,42 @@
1
+ 0.4.1 10/20/2020
2
+ ================
3
+
4
+ We can't expect to ever release a 1.0 if the configuration object doesn't actually persist from the initializer.
5
+
6
+ 0.4.0 10/20/2020
7
+ ================
8
+
9
+ Add an `IntrospectiveGrape.config.skip_object_reload = true` option.
10
+
11
+ 0.3.9 04/01/2019
12
+ ================
13
+
14
+ Add a note to the README that pagination will have to be supported via alexey-klimuk's fork of grape-kaminari from years ago because the middleware support gems have stopped being maintained, and rubygems doesn't support targeting github branches when adding dependencies.
15
+
16
+ gem 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari'
17
+
18
+ Probably we'll just fork it if nobody else can be bothered.
1
19
 
2
- 0.3.6 09/14/2018
20
+ 0.3.8 03/27/2019
3
21
  ================
4
22
 
5
- Updated to support Rails 5+
23
+ Support the latest Grape.
24
+
25
+ 0.3.7 03/26/2019
26
+ ================
27
+
28
+ Updated to support Grape ~> 1.2.0. Ruby 2.2 no longer supported.
29
+
30
+
31
+ 0.3.7 03/25/2019
32
+ ================
33
+
34
+ Updated to support Grape <~ 1.1.0.
35
+
36
+ 0.3.6 03/23/2019
37
+ ================
6
38
 
39
+ Updated to support Rails 5+.
7
40
 
8
41
 
9
42
  0.3.5 09/14/2018
data/Gemfile CHANGED
@@ -5,6 +5,8 @@ source 'https://rubygems.org'
5
5
  # development dependencies will be added by default to the :development group.
6
6
  gemspec
7
7
 
8
+ gem 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari'
9
+
8
10
  gem 'coveralls', require: false
9
11
 
10
12
  # Declare any dependencies that are still in development here instead of in
data/README.md CHANGED
@@ -40,6 +40,7 @@ can speak in their own idioms.
40
40
  In your Gemfile:
41
41
 
42
42
  ```
43
+ gem 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari' # some middleware has fallen into deep disrepair
43
44
  gem 'introspective_grape'
44
45
  ```
45
46
 
@@ -48,6 +49,8 @@ And bundle install.
48
49
 
49
50
  ## Grape Configuration
50
51
 
52
+ ### Camelization
53
+
51
54
  IntrospectiveGrape's default behavior is to camelize all outputs and snake case all inputs. To camel case all your json output you'll need to use its formatter in your API:
52
55
 
53
56
  ```
@@ -60,6 +63,18 @@ You can disable this behavior by setting `IntrospectiveGrape.config.camelize_par
60
63
 
61
64
  To include this behavior in your test coverage you need to either access the API's params hash or you can format the response body to `JSON.parse(response.body).with_snake_keys` in a helper method with the `using CamelSnakeKeys` refinement.
62
65
 
66
+ ### Reloading an object after an update request
67
+
68
+ By default the gem reloads the object instance before presenting the updated model, a lazy but
69
+ effective workaround to updates that may not propagate in the working instance due to actions
70
+ a user may take in hooks, or some updates to has_many :through associations. We want to put up
71
+ APIs with haste rather than digging our way out of tricky minutae that can be handled later as
72
+ technical debt.
73
+
74
+ This behavior can be disabled by setting `IntrospectiveGrape.config.skip_object_reload = true`,
75
+ when you have time for technical debt you can toggle it and work on fixing broken tests (you
76
+ did take the time to write comprehensive test coverage, didn't you?).
77
+
63
78
  ## Authentication and authorization
64
79
 
65
80
  Authentication and authorization are presently enforced on every endpoint. If you have named the authentication helper method in Grape something other than "authenticate!" or "authorize!" you can set it with:
@@ -3,7 +3,8 @@ source 'https://rubygems.org'
3
3
  gemspec :path => '../'
4
4
 
5
5
  gem 'rails', '5.0.0'
6
- gem 'grape', '< 1.0.0'
6
+ gem 'grape'
7
+ gem 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari'
7
8
  gem 'grape-swagger'
8
9
  gem 'nokogiri'
9
10
  gem 'coveralls', require: false
@@ -3,7 +3,8 @@ source 'https://rubygems.org'
3
3
  gemspec :path => '../'
4
4
 
5
5
  gem 'rails', '5.0.1'
6
- gem 'grape', '< 1.0.0'
6
+ gem 'grape'
7
+ gem 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari'
7
8
  gem 'grape-swagger'
8
9
  gem 'nokogiri'
9
10
  gem 'coveralls', require: false
@@ -3,7 +3,8 @@ source 'https://rubygems.org'
3
3
  gemspec :path => '../'
4
4
 
5
5
  gem 'rails', '5.1.0'
6
- gem 'grape', '< 1.0.0'
6
+ gem 'grape'
7
+ gem 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari'
7
8
  gem 'grape-swagger'
8
9
  gem 'nokogiri'
9
10
  gem 'coveralls', require: false
@@ -3,7 +3,8 @@ source 'https://rubygems.org'
3
3
  gemspec :path => '../'
4
4
 
5
5
  gem 'rails', '5.2.0'
6
- gem 'grape', '< 1.0.0'
6
+ gem 'grape'
7
+ gem 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari'
7
8
  gem 'grape-swagger'
8
9
  gem 'nokogiri'
9
10
  gem 'coveralls', require: false
@@ -3,7 +3,8 @@ source 'https://rubygems.org'
3
3
  gemspec :path => '../'
4
4
 
5
5
  gem 'rails'
6
- gem 'grape', '< 1.0.0'
6
+ gem 'grape'
7
+ gem 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari'
7
8
  gem 'grape-swagger'
8
9
  gem 'nokogiri'
9
10
  gem 'coveralls', require: false
@@ -18,19 +18,21 @@ Gem::Specification.new do |s|
18
18
  s.files = `git ls-files`.split("\n").sort
19
19
  s.test_files = `git ls-files -- spec/*`.split("\n")
20
20
 
21
- s.required_ruby_version = '~> 2.1'
21
+ s.required_ruby_version = '>= 2.3'
22
22
 
23
23
  s.add_dependency "rails", '> 5.0.0'
24
+ # grape < 1.3.0 is incompatible with rack 2.1.0
25
+ s.add_dependency "rack", '< 2.0.9'
24
26
 
25
27
  # grape 1.0.0 breaks the pagination solution
26
- s.add_dependency 'grape', '< 1.2.0'
27
- s.add_dependency 'grape-entity'
28
- s.add_dependency 'grape-swagger'
29
- s.add_dependency 'kaminari', '< 1.0' # version 1.0.0 breaks
30
- s.add_dependency 'grape-kaminari'
28
+ s.add_runtime_dependency 'grape', ['~> 1.2.0', '< 1.2.5']
29
+ s.add_runtime_dependency 'grape-entity'
30
+ s.add_runtime_dependency 'grape-swagger'
31
+ s.add_runtime_dependency 'kaminari' #, '< 1.0' # version 1.0.0 breaks
32
+ #s.add_dependency 'grape-kaminari', :github => 'alexey-klimuk/grape-kaminari'
31
33
  # Pundit 2.0 mysteriously made authorize a protected method...
32
- s.add_dependency 'pundit' #, '<2.0'
33
- s.add_dependency 'camel_snake_keys', '>0.0.4'
34
+ s.add_runtime_dependency 'pundit' #, '<2.0'
35
+ s.add_runtime_dependency 'camel_snake_keys', '>0.0.4'
34
36
 
35
37
  if RUBY_PLATFORM == 'java'
36
38
  #s.add_development_dependency "jdbc-sqlite3"
@@ -1,6 +1,8 @@
1
+ require 'introspective_grape/configuration'
1
2
  module IntrospectiveGrape
2
3
  autoload :API, 'introspective_grape/api'
3
4
  autoload :CamelSnake, 'introspective_grape/camel_snake'
5
+ autoload :Configure, 'introspective_grape/configuration'
4
6
  autoload :CreateHelpers, 'introspective_grape/create_helpers'
5
7
  autoload :Doc, 'introspective_grape/doc'
6
8
  autoload :Filters, 'introspective_grape/filters'
@@ -13,6 +15,6 @@ module IntrospectiveGrape
13
15
  end
14
16
 
15
17
  def self.config
16
- @config = OpenStruct.new(camelize_parameters: true)
18
+ @config ||= Configuration.new
17
19
  end
18
20
  end
@@ -1,4 +1,5 @@
1
1
  require 'action_controller'
2
+ require 'kaminari'
2
3
  require 'grape-kaminari'
3
4
  require 'introspective_grape/validators'
4
5
 
@@ -6,7 +7,7 @@ class IntrospectiveGrapeError < StandardError
6
7
  end
7
8
 
8
9
  module IntrospectiveGrape
9
- class API < Grape::API
10
+ class API < Grape::API::Instance
10
11
  extend IntrospectiveGrape::Helpers
11
12
  extend IntrospectiveGrape::CreateHelpers
12
13
  extend IntrospectiveGrape::Filters
@@ -247,9 +248,12 @@ module IntrospectiveGrape
247
248
 
248
249
  @model.update_attributes!( safe_params(params).permit(klass.whitelist) )
249
250
 
250
- default_includes = routes.first.klass.default_includes(routes.first.model)
251
-
252
- present klass.find_leaf(routes, @model.class.includes(default_includes).find(@model.id), params), with: "#{klass}::#{model}Entity".constantize
251
+ if IntrospectiveGrape.config.skip_object_reload
252
+ present klass.find_leaf(routes, @model, params), with: "#{klass}::#{model}Entity".constantize
253
+ else
254
+ default_includes = routes.first.klass.default_includes(routes.first.model)
255
+ present klass.find_leaf(routes, @model.class.includes(default_includes).find(@model.id), params), with: "#{klass}::#{model}Entity".constantize
256
+ end
253
257
  end
254
258
  end
255
259
 
@@ -28,7 +28,7 @@ if IntrospectiveGrape.config.camelize_parameters
28
28
  end
29
29
  end
30
30
 
31
- Grape::API.singleton_class.send(:prepend, CreateCamelizedDocumentationClass)
31
+ Grape::API::Instance.singleton_class.send(:prepend, CreateCamelizedDocumentationClass)
32
32
  else
33
33
  module CallWithCamelized
34
34
  def call(*args)
@@ -0,0 +1,15 @@
1
+ module IntrospectiveGrape
2
+ def self.configure
3
+ self.config ||= Configuration.new
4
+ yield config
5
+ end
6
+
7
+ class Configuration
8
+ attr_accessor :camelize_parameters, :skip_object_reload
9
+
10
+ def initialize
11
+ @camelize_parameters = true
12
+ @skip_object_reload = false
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module IntrospectiveGrape
2
- VERSION = "0.3.7".freeze
2
+ VERSION = "0.4.2".freeze
3
3
  end
data/spec/dummy/Gemfile CHANGED
@@ -10,11 +10,12 @@ gem 'delayed_paperclip'
10
10
 
11
11
  gem 'grape'
12
12
  gem 'grape-entity'
13
- gem 'grape-kaminari', '<1.8.0'
13
+ gem 'grape-kaminari'
14
14
  gem 'grape-swagger'
15
- gem 'introspective_grape', git: 'https://github.com/buermann/introspective_grape', branch: 'rails.5'
15
+ gem 'introspective_grape', git: 'https://github.com/buermann/introspective_grape', branch: 'grape-1.2'
16
16
 
17
17
  gem 'paperclip'
18
18
  gem 'pundit'
19
19
 
20
+ gem 'rack-cors'
20
21
  gem 'sqlite3', '< 1.4.0'
@@ -1,6 +1,8 @@
1
1
  module ApiHelpers
2
2
  def current_user
3
3
  params[:api_key].present? && @user = User.find_by_authentication_token(params[:api_key])
4
+ # for testing in situ
5
+ #@user = User.find_or_create_by(email: 'test@test.com', superuser: true, authentication_token: '1234567890', first_name: "First", last_name: "Last")
4
6
  end
5
7
 
6
8
  def authenticate!
@@ -5,7 +5,7 @@ class Dummy::CompanyAPI < IntrospectiveGrape::API
5
5
 
6
6
  desc "Test default values in an extra endpoint"
7
7
  params do
8
- optional :boolean_default, type: Boolean, default: false
8
+ optional :boolean_default, type: Virtus::Attribute::Boolean, default: false
9
9
  optional :string_default, type: String, default: "foo"
10
10
  optional :integer_default, type: Integer, default: 123
11
11
  end
data/spec/dummy/config.ru CHANGED
@@ -2,3 +2,15 @@
2
2
 
3
3
  require ::File.expand_path('../config/environment', __FILE__)
4
4
  run Rails.application
5
+
6
+ require 'rack/cors'
7
+
8
+ use Rack::Cors do
9
+ allow do
10
+ origins '*'
11
+ resource '*',
12
+ :headers => :any,
13
+ :methods => [:get, :post, :delete, :put, :options],
14
+ expose: %w(X-Server-Date X-Server-Epoch)
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ class AddTestData < ActiveRecord::Migration[5.2]
2
+ def change
3
+ User.create(email: 'test@test.com', superuser: true, authentication_token: '1234567890', first_name: "First", last_name: "Last")
4
+ end
5
+ end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # This file is auto-generated from the current state of the database. Instead
3
2
  # of editing this file, please use the migrations feature of Active Record to
4
3
  # incrementally modify your database, and then regenerate this schema definition.
@@ -11,269 +10,251 @@
11
10
  #
12
11
  # It's strongly recommended that you check this file into your version control system.
13
12
 
14
- ActiveRecord::Schema.define(version: 20150909225019) do
13
+ ActiveRecord::Schema.define(version: 2019_03_25_231304) do
15
14
 
16
15
  create_table "active_admin_comments", force: :cascade do |t|
17
- t.string "namespace"
18
- t.text "body"
19
- t.string "resource_id", null: false
20
- t.string "resource_type", null: false
21
- t.integer "author_id"
22
- t.string "author_type"
23
- t.datetime "created_at", null: false
24
- t.datetime "updated_at", null: false
16
+ t.string "namespace"
17
+ t.text "body"
18
+ t.string "resource_id", null: false
19
+ t.string "resource_type", null: false
20
+ t.integer "author_id"
21
+ t.string "author_type"
22
+ t.datetime "created_at", null: false
23
+ t.datetime "updated_at", null: false
24
+ t.index ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id"
25
+ t.index ["namespace"], name: "index_active_admin_comments_on_namespace"
26
+ t.index ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id"
25
27
  end
26
28
 
27
- add_index "active_admin_comments", ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id"
28
- add_index "active_admin_comments", ["namespace"], name: "index_active_admin_comments_on_namespace"
29
- add_index "active_admin_comments", ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id"
30
-
31
29
  create_table "admin_users", force: :cascade do |t|
32
- t.string "email", default: "", null: false
33
- t.string "encrypted_password", default: "", null: false
34
- t.string "reset_password_token"
30
+ t.string "email", default: "", null: false
31
+ t.string "encrypted_password", default: "", null: false
32
+ t.string "reset_password_token"
35
33
  t.datetime "reset_password_sent_at"
36
34
  t.datetime "remember_created_at"
37
- t.integer "sign_in_count", default: 0, null: false
35
+ t.integer "sign_in_count", default: 0, null: false
38
36
  t.datetime "current_sign_in_at"
39
37
  t.datetime "last_sign_in_at"
40
- t.string "current_sign_in_ip"
41
- t.string "last_sign_in_ip"
42
- t.datetime "created_at", null: false
43
- t.datetime "updated_at", null: false
38
+ t.string "current_sign_in_ip"
39
+ t.string "last_sign_in_ip"
40
+ t.datetime "created_at", null: false
41
+ t.datetime "updated_at", null: false
42
+ t.index ["email"], name: "index_admin_users_on_email", unique: true
43
+ t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
44
44
  end
45
45
 
46
- add_index "admin_users", ["email"], name: "index_admin_users_on_email", unique: true
47
- add_index "admin_users", ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
48
-
49
46
  create_table "chat_message_users", force: :cascade do |t|
50
- t.integer "chat_message_id"
51
- t.integer "user_id"
47
+ t.integer "chat_message_id"
48
+ t.integer "user_id"
52
49
  t.datetime "read_at"
53
- t.datetime "created_at", null: false
54
- t.datetime "updated_at", null: false
50
+ t.datetime "created_at", null: false
51
+ t.datetime "updated_at", null: false
52
+ t.index ["chat_message_id"], name: "index_chat_message_users_on_chat_message_id"
53
+ t.index ["user_id"], name: "index_chat_message_users_on_user_id"
55
54
  end
56
55
 
57
- add_index "chat_message_users", ["chat_message_id"], name: "index_chat_message_users_on_chat_message_id"
58
- add_index "chat_message_users", ["user_id"], name: "index_chat_message_users_on_user_id"
59
-
60
56
  create_table "chat_messages", force: :cascade do |t|
61
- t.integer "chat_id"
62
- t.integer "author_id"
63
- t.text "message"
57
+ t.integer "chat_id"
58
+ t.integer "author_id"
59
+ t.text "message"
64
60
  t.datetime "created_at", null: false
65
61
  t.datetime "updated_at", null: false
62
+ t.index ["author_id"], name: "index_chat_messages_on_author_id"
63
+ t.index ["chat_id"], name: "index_chat_messages_on_chat_id"
66
64
  end
67
65
 
68
- add_index "chat_messages", ["author_id"], name: "index_chat_messages_on_author_id"
69
- add_index "chat_messages", ["chat_id"], name: "index_chat_messages_on_chat_id"
70
-
71
66
  create_table "chat_users", force: :cascade do |t|
72
- t.integer "chat_id"
73
- t.integer "user_id"
67
+ t.integer "chat_id"
68
+ t.integer "user_id"
74
69
  t.datetime "departed_at"
75
- t.datetime "created_at", null: false
76
- t.datetime "updated_at", null: false
70
+ t.datetime "created_at", null: false
71
+ t.datetime "updated_at", null: false
72
+ t.index ["chat_id"], name: "index_chat_users_on_chat_id"
73
+ t.index ["user_id"], name: "index_chat_users_on_user_id"
77
74
  end
78
75
 
79
- add_index "chat_users", ["chat_id"], name: "index_chat_users_on_chat_id"
80
- add_index "chat_users", ["user_id"], name: "index_chat_users_on_user_id"
81
-
82
76
  create_table "chats", force: :cascade do |t|
83
- t.integer "creator_id"
77
+ t.integer "creator_id"
84
78
  t.datetime "created_at", null: false
85
79
  t.datetime "updated_at", null: false
86
80
  end
87
81
 
88
82
  create_table "companies", force: :cascade do |t|
89
- t.string "name", limit: 256, null: false
90
- t.string "short_name", limit: 10, null: false
91
- t.datetime "created_at", null: false
92
- t.datetime "updated_at", null: false
83
+ t.string "name", limit: 256, null: false
84
+ t.string "short_name", limit: 10, null: false
85
+ t.datetime "created_at", null: false
86
+ t.datetime "updated_at", null: false
87
+ t.index ["name"], name: "index_companies_on_name", unique: true
93
88
  end
94
89
 
95
- add_index "companies", ["name"], name: "index_companies_on_name", unique: true
96
-
97
90
  create_table "images", force: :cascade do |t|
98
- t.integer "imageable_id"
99
- t.string "imageable_type"
100
- t.string "file_file_name"
101
- t.string "file_content_type"
102
- t.integer "file_file_size"
91
+ t.integer "imageable_id"
92
+ t.string "imageable_type"
93
+ t.string "file_file_name"
94
+ t.string "file_content_type"
95
+ t.integer "file_file_size"
103
96
  t.datetime "file_updated_at"
104
- t.boolean "file_processing", default: false, null: false
105
- t.text "meta"
106
- t.string "source"
107
- t.float "lat"
108
- t.float "lng"
97
+ t.boolean "file_processing", default: false, null: false
98
+ t.text "meta"
99
+ t.string "source"
100
+ t.float "lat"
101
+ t.float "lng"
109
102
  t.datetime "taken_at"
110
- t.datetime "created_at", null: false
111
- t.datetime "updated_at", null: false
103
+ t.datetime "created_at", null: false
104
+ t.datetime "updated_at", null: false
112
105
  end
113
106
 
114
107
  create_table "jobs", force: :cascade do |t|
115
- t.string "title", null: false
108
+ t.string "title", null: false
116
109
  t.datetime "created_at", null: false
117
110
  t.datetime "updated_at", null: false
118
111
  end
119
112
 
120
113
  create_table "locatables", force: :cascade do |t|
121
- t.integer "location_id"
122
- t.integer "locatable_id"
123
- t.string "locatable_type"
124
- t.datetime "created_at", null: false
125
- t.datetime "updated_at", null: false
114
+ t.integer "location_id"
115
+ t.integer "locatable_id"
116
+ t.string "locatable_type"
117
+ t.datetime "created_at", null: false
118
+ t.datetime "updated_at", null: false
119
+ t.index ["locatable_type", "locatable_id"], name: "index_locatables_on_locatable_type_and_locatable_id"
126
120
  end
127
121
 
128
- add_index "locatables", ["locatable_type", "locatable_id"], name: "index_locatables_on_locatable_type_and_locatable_id"
129
-
130
122
  create_table "location_beacons", force: :cascade do |t|
131
- t.integer "location_id"
132
- t.integer "company_id", null: false
133
- t.string "mac_address", limit: 12
134
- t.string "uuid", limit: 32, null: false
135
- t.integer "major", null: false
136
- t.integer "minor", null: false
137
- t.datetime "created_at", null: false
138
- t.datetime "updated_at", null: false
123
+ t.integer "location_id"
124
+ t.integer "company_id", null: false
125
+ t.string "mac_address", limit: 12
126
+ t.string "uuid", limit: 32, null: false
127
+ t.integer "major", null: false
128
+ t.integer "minor", null: false
129
+ t.datetime "created_at", null: false
130
+ t.datetime "updated_at", null: false
131
+ t.index ["company_id", "uuid", "major", "minor"], name: "index_location_beacons_unique_company_identifier", unique: true
139
132
  end
140
133
 
141
- add_index "location_beacons", ["company_id", "uuid", "major", "minor"], name: "index_location_beacons_unique_company_identifier", unique: true
142
-
143
134
  create_table "location_gps", force: :cascade do |t|
144
- t.integer "location_id"
145
- t.float "lat", null: false
146
- t.float "lng", null: false
147
- t.float "alt", default: 0.0
148
- t.datetime "created_at", null: false
149
- t.datetime "updated_at", null: false
135
+ t.integer "location_id"
136
+ t.float "lat", null: false
137
+ t.float "lng", null: false
138
+ t.float "alt", default: 0.0
139
+ t.datetime "created_at", null: false
140
+ t.datetime "updated_at", null: false
141
+ t.index ["location_id"], name: "index_location_gps_on_location_id"
150
142
  end
151
143
 
152
- add_index "location_gps", ["location_id"], name: "index_location_gps_on_location_id"
153
-
154
144
  create_table "locations", force: :cascade do |t|
155
- t.string "name", null: false
156
- t.string "kind"
157
- t.integer "parent_location_id"
158
- t.datetime "created_at", null: false
159
- t.datetime "updated_at", null: false
145
+ t.string "name", null: false
146
+ t.string "kind"
147
+ t.integer "parent_location_id"
148
+ t.datetime "created_at", null: false
149
+ t.datetime "updated_at", null: false
150
+ t.index ["parent_location_id", "kind", "name"], name: "index_locations_on_parent_location_id_and_kind_and_name", unique: true
151
+ t.index ["parent_location_id"], name: "index_locations_on_parent_location_id"
160
152
  end
161
153
 
162
- add_index "locations", ["parent_location_id", "kind", "name"], name: "index_locations_on_parent_location_id_and_kind_and_name", unique: true
163
- add_index "locations", ["parent_location_id"], name: "index_locations_on_parent_location_id"
164
-
165
154
  create_table "project_jobs", force: :cascade do |t|
166
- t.integer "project_id", null: false
167
- t.integer "job_id", null: false
155
+ t.integer "project_id", null: false
156
+ t.integer "job_id", null: false
168
157
  t.datetime "created_at", null: false
169
158
  t.datetime "updated_at", null: false
159
+ t.index ["job_id"], name: "index_project_jobs_on_job_id"
160
+ t.index ["project_id", "job_id"], name: "index_project_jobs_on_project_id_and_job_id", unique: true
161
+ t.index ["project_id"], name: "index_project_jobs_on_project_id"
170
162
  end
171
163
 
172
- add_index "project_jobs", ["job_id"], name: "index_project_jobs_on_job_id"
173
- add_index "project_jobs", ["project_id", "job_id"], name: "index_project_jobs_on_project_id_and_job_id", unique: true
174
- add_index "project_jobs", ["project_id"], name: "index_project_jobs_on_project_id"
175
-
176
164
  create_table "projects", force: :cascade do |t|
177
- t.string "name", null: false
178
- t.integer "owner_id"
179
- t.datetime "created_at", null: false
180
- t.datetime "updated_at", null: false
181
- t.string "default_password"
165
+ t.string "name", null: false
166
+ t.integer "owner_id"
167
+ t.datetime "created_at", null: false
168
+ t.datetime "updated_at", null: false
169
+ t.string "default_password"
170
+ t.index ["owner_id"], name: "index_projects_on_owner_id"
182
171
  end
183
172
 
184
- add_index "projects", ["owner_id"], name: "index_projects_on_owner_id"
185
-
186
173
  create_table "roles", force: :cascade do |t|
187
- t.integer "user_id", null: false
188
- t.integer "ownable_id"
189
- t.string "ownable_type"
190
- t.datetime "created_at", null: false
191
- t.datetime "updated_at", null: false
174
+ t.integer "user_id", null: false
175
+ t.integer "ownable_id"
176
+ t.string "ownable_type"
177
+ t.datetime "created_at", null: false
178
+ t.datetime "updated_at", null: false
179
+ t.index ["ownable_type", "ownable_id"], name: "index_roles_on_ownable_type_and_ownable_id"
180
+ t.index ["user_id", "ownable_type", "ownable_id"], name: "index_roles_on_user_id_and_ownable_type_and_ownable_id", unique: true
192
181
  end
193
182
 
194
- add_index "roles", ["ownable_type", "ownable_id"], name: "index_roles_on_ownable_type_and_ownable_id"
195
- add_index "roles", ["user_id", "ownable_type", "ownable_id"], name: "index_roles_on_user_id_and_ownable_type_and_ownable_id", unique: true
196
-
197
183
  create_table "team_users", force: :cascade do |t|
198
- t.integer "user_id"
199
- t.integer "team_id"
184
+ t.integer "user_id"
185
+ t.integer "team_id"
200
186
  t.datetime "created_at", null: false
201
187
  t.datetime "updated_at", null: false
188
+ t.index ["team_id"], name: "index_team_users_on_team_id"
189
+ t.index ["user_id", "team_id"], name: "index_team_users_on_user_id_and_team_id", unique: true
190
+ t.index ["user_id"], name: "index_team_users_on_user_id"
202
191
  end
203
192
 
204
- add_index "team_users", ["team_id"], name: "index_team_users_on_team_id"
205
- add_index "team_users", ["user_id", "team_id"], name: "index_team_users_on_user_id_and_team_id", unique: true
206
- add_index "team_users", ["user_id"], name: "index_team_users_on_user_id"
207
-
208
193
  create_table "teams", force: :cascade do |t|
209
- t.string "name"
210
- t.integer "project_id"
211
- t.integer "creator_id"
194
+ t.string "name"
195
+ t.integer "project_id"
196
+ t.integer "creator_id"
212
197
  t.datetime "created_at", null: false
213
198
  t.datetime "updated_at", null: false
199
+ t.index ["creator_id"], name: "index_teams_on_creator_id"
200
+ t.index ["project_id"], name: "index_teams_on_project_id"
214
201
  end
215
202
 
216
- add_index "teams", ["creator_id"], name: "index_teams_on_creator_id"
217
- add_index "teams", ["project_id"], name: "index_teams_on_project_id"
218
-
219
203
  create_table "user_locations", force: :cascade do |t|
220
- t.integer "user_id"
221
- t.integer "location_id"
222
- t.integer "detectable_id"
223
- t.string "detectable_type"
224
- t.float "lat"
225
- t.float "lng"
226
- t.float "alt"
227
- t.datetime "created_at", null: false
228
- t.datetime "updated_at", null: false
204
+ t.integer "user_id"
205
+ t.integer "location_id"
206
+ t.integer "detectable_id"
207
+ t.string "detectable_type"
208
+ t.float "lat"
209
+ t.float "lng"
210
+ t.float "alt"
211
+ t.datetime "created_at", null: false
212
+ t.datetime "updated_at", null: false
213
+ t.index ["detectable_type", "detectable_id"], name: "index_user_locations_on_detectable_type_and_detectable_id"
214
+ t.index ["location_id"], name: "index_user_locations_on_location_id"
215
+ t.index ["user_id"], name: "index_user_locations_on_user_id"
229
216
  end
230
217
 
231
- add_index "user_locations", ["detectable_type", "detectable_id"], name: "index_user_locations_on_detectable_type_and_detectable_id"
232
- add_index "user_locations", ["location_id"], name: "index_user_locations_on_location_id"
233
- add_index "user_locations", ["user_id"], name: "index_user_locations_on_user_id"
234
-
235
218
  create_table "user_project_jobs", force: :cascade do |t|
236
- t.integer "user_id", null: false
237
- t.integer "project_id", null: false
238
- t.integer "job_id", null: false
219
+ t.integer "user_id", null: false
220
+ t.integer "project_id", null: false
221
+ t.integer "job_id", null: false
239
222
  t.datetime "created_at", null: false
240
223
  t.datetime "updated_at", null: false
224
+ t.index ["job_id"], name: "index_user_project_jobs_on_job_id"
225
+ t.index ["project_id"], name: "index_user_project_jobs_on_project_id"
226
+ t.index ["user_id", "project_id", "job_id"], name: "index_user_project_jobs_on_user_id_and_project_id_and_job_id", unique: true
227
+ t.index ["user_id"], name: "index_user_project_jobs_on_user_id"
241
228
  end
242
229
 
243
- add_index "user_project_jobs", ["job_id"], name: "index_user_project_jobs_on_job_id"
244
- add_index "user_project_jobs", ["project_id"], name: "index_user_project_jobs_on_project_id"
245
- add_index "user_project_jobs", ["user_id", "project_id", "job_id"], name: "index_user_project_jobs_on_user_id_and_project_id_and_job_id", unique: true
246
- add_index "user_project_jobs", ["user_id"], name: "index_user_project_jobs_on_user_id"
247
-
248
230
  create_table "users", force: :cascade do |t|
249
- t.string "email", default: "", null: false
250
- t.string "encrypted_password", default: "", null: false
251
- t.string "reset_password_token"
231
+ t.string "email", default: "", null: false
232
+ t.string "encrypted_password", default: "", null: false
233
+ t.string "reset_password_token"
252
234
  t.datetime "reset_password_sent_at"
253
235
  t.datetime "remember_created_at"
254
- t.integer "sign_in_count", default: 0, null: false
236
+ t.integer "sign_in_count", default: 0, null: false
255
237
  t.datetime "current_sign_in_at"
256
238
  t.datetime "last_sign_in_at"
257
- t.string "current_sign_in_ip"
258
- t.string "last_sign_in_ip"
259
- t.datetime "created_at", null: false
260
- t.datetime "updated_at", null: false
261
- t.integer "failed_attempts", default: 0, null: false
262
- t.string "unlock_token"
239
+ t.string "current_sign_in_ip"
240
+ t.string "last_sign_in_ip"
241
+ t.datetime "created_at", null: false
242
+ t.datetime "updated_at", null: false
243
+ t.integer "failed_attempts", default: 0, null: false
244
+ t.string "unlock_token"
263
245
  t.datetime "locked_at"
264
- t.boolean "superuser", default: false, null: false
265
- t.string "authentication_token"
266
- t.string "confirmation_token"
246
+ t.boolean "superuser", default: false, null: false
247
+ t.string "authentication_token"
248
+ t.string "confirmation_token"
267
249
  t.datetime "confirmed_at"
268
250
  t.datetime "confirmation_sent_at"
269
- t.string "unconfirmed_email"
270
- t.string "first_name"
271
- t.string "last_name"
251
+ t.string "unconfirmed_email"
252
+ t.string "first_name"
253
+ t.string "last_name"
254
+ t.index ["authentication_token"], name: "index_users_on_authentication_token", unique: true
255
+ t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
256
+ t.index ["email"], name: "index_users_on_email", unique: true
257
+ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
272
258
  end
273
259
 
274
- add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true
275
- add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
276
- add_index "users", ["email"], name: "index_users_on_email", unique: true
277
- add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
278
-
279
260
  end
@@ -174,7 +174,7 @@ describe Dummy::ProjectAPI, type: :request do
174
174
  it "should respect the maximum number of results" do
175
175
  get '/api/v1/projects', params: { per_page: 20, offset: 0 }
176
176
  response.code.should eq "400"
177
- json['error'].should eq "per_page must be less than 10"
177
+ json['error'].should eq "per_page must be less than or equal 10"
178
178
  end
179
179
  end
180
180
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: introspective_grape
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Buermann
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-25 00:00:00.000000000 Z
11
+ date: 2021-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -25,35 +25,41 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.0.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: grape
28
+ name: rack
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.2.0
33
+ version: 2.0.9
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "<"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.2.0
40
+ version: 2.0.9
41
41
  - !ruby/object:Gem::Dependency
42
- name: grape-entity
42
+ name: grape
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 1.2.0
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: 1.2.5
48
51
  type: :runtime
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
- - - ">="
55
+ - - "~>"
53
56
  - !ruby/object:Gem::Version
54
- version: '0'
57
+ version: 1.2.0
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: 1.2.5
55
61
  - !ruby/object:Gem::Dependency
56
- name: grape-swagger
62
+ name: grape-entity
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - ">="
@@ -67,21 +73,21 @@ dependencies:
67
73
  - !ruby/object:Gem::Version
68
74
  version: '0'
69
75
  - !ruby/object:Gem::Dependency
70
- name: kaminari
76
+ name: grape-swagger
71
77
  requirement: !ruby/object:Gem::Requirement
72
78
  requirements:
73
- - - "<"
79
+ - - ">="
74
80
  - !ruby/object:Gem::Version
75
- version: '1.0'
81
+ version: '0'
76
82
  type: :runtime
77
83
  prerelease: false
78
84
  version_requirements: !ruby/object:Gem::Requirement
79
85
  requirements:
80
- - - "<"
86
+ - - ">="
81
87
  - !ruby/object:Gem::Version
82
- version: '1.0'
88
+ version: '0'
83
89
  - !ruby/object:Gem::Dependency
84
- name: grape-kaminari
90
+ name: kaminari
85
91
  requirement: !ruby/object:Gem::Requirement
86
92
  requirements:
87
93
  - - ">="
@@ -246,14 +252,6 @@ files:
246
252
  - app/models/.keep
247
253
  - app/views/.keep
248
254
  - bin/rails
249
- - gemfiles/2.0.0-Gemfile
250
- - gemfiles/2.2.0-Gemfile
251
- - gemfiles/Gemfile.rails.3.2.22
252
- - gemfiles/Gemfile.rails.4.1.13
253
- - gemfiles/Gemfile.rails.4.2.10
254
- - gemfiles/Gemfile.rails.4.2.7.1
255
- - gemfiles/Gemfile.rails.4.2.7.1.new.swagger
256
- - gemfiles/Gemfile.rails.4.2.8
257
255
  - gemfiles/Gemfile.rails.5.0.0
258
256
  - gemfiles/Gemfile.rails.5.0.1
259
257
  - gemfiles/Gemfile.rails.5.1.0
@@ -263,6 +261,7 @@ files:
263
261
  - lib/introspective_grape.rb
264
262
  - lib/introspective_grape/api.rb
265
263
  - lib/introspective_grape/camel_snake.rb
264
+ - lib/introspective_grape/configuration.rb
266
265
  - lib/introspective_grape/create_helpers.rb
267
266
  - lib/introspective_grape/doc.rb
268
267
  - lib/introspective_grape/filters.rb
@@ -383,6 +382,7 @@ files:
383
382
  - spec/dummy/db/migrate/20150820190524_add_user_names.rb
384
383
  - spec/dummy/db/migrate/20150824215701_create_images.rb
385
384
  - spec/dummy/db/migrate/20150909225019_add_password_to_project.rb
385
+ - spec/dummy/db/migrate/20190325231304_add_test_data.rb
386
386
  - spec/dummy/db/schema.rb
387
387
  - spec/dummy/lib/assets/.keep
388
388
  - spec/dummy/log/.keep
@@ -419,24 +419,23 @@ homepage: https://github.com/buermann/introspective_grape
419
419
  licenses:
420
420
  - MIT
421
421
  metadata: {}
422
- post_install_message:
422
+ post_install_message:
423
423
  rdoc_options: []
424
424
  require_paths:
425
425
  - lib
426
426
  required_ruby_version: !ruby/object:Gem::Requirement
427
427
  requirements:
428
- - - "~>"
428
+ - - ">="
429
429
  - !ruby/object:Gem::Version
430
- version: '2.1'
430
+ version: '2.3'
431
431
  required_rubygems_version: !ruby/object:Gem::Requirement
432
432
  requirements:
433
433
  - - ">="
434
434
  - !ruby/object:Gem::Version
435
435
  version: '0'
436
436
  requirements: []
437
- rubyforge_project:
438
- rubygems_version: 2.5.1
439
- signing_key:
437
+ rubygems_version: 3.1.2
438
+ signing_key:
440
439
  specification_version: 4
441
440
  summary: Introspectively configure deeply nested RESTful Grape APIs for ActiveRecord
442
441
  models.
@@ -551,6 +550,7 @@ test_files:
551
550
  - spec/dummy/db/migrate/20150820190524_add_user_names.rb
552
551
  - spec/dummy/db/migrate/20150824215701_create_images.rb
553
552
  - spec/dummy/db/migrate/20150909225019_add_password_to_project.rb
553
+ - spec/dummy/db/migrate/20190325231304_add_test_data.rb
554
554
  - spec/dummy/db/schema.rb
555
555
  - spec/dummy/lib/assets/.keep
556
556
  - spec/dummy/log/.keep
@@ -1,22 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Declare your gem's dependencies in introspective_grape.gemspec.
4
- # Bundler will treat runtime dependencies like base dependencies, and
5
- # development dependencies will be added by default to the :development group.
6
-
7
- gemspec :path => '../'
8
-
9
- gem 'grape', '< 0.17'
10
- gem 'grape-swagger', '< 0.12.0'
11
- gem 'nokogiri', '< 1.6.9'
12
- gem 'coveralls', require: false
13
-
14
- # Declare any dependencies that are still in development here instead of in
15
- # your gemspec. These might include edge Rails or gems from your path or
16
- # Git. Remember to move these dependencies to your gemspec before releasing
17
- # your gem to rubygems.org.
18
-
19
- group :development,:test do
20
- gem 'rspec'
21
- # gem 'byebug'
22
- end
@@ -1,21 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Declare your gem's dependencies in introspective_grape.gemspec.
4
- # Bundler will treat runtime dependencies like base dependencies, and
5
- # development dependencies will be added by default to the :development group.
6
-
7
- gemspec :path => '../'
8
-
9
- gem 'grape', '< 1.0.0'
10
- gem 'grape-swagger'
11
- gem 'coveralls', require: false
12
-
13
- #
14
- # Declare any dependencies that are still in development here instead of in
15
- # your gemspec. These might include edge Rails or gems from your path or
16
- # Git. Remember to move these dependencies to your gemspec before releasing
17
- # your gem to rubygems.org.
18
-
19
- group :development do
20
- # gem 'byebug'
21
- end
@@ -1,13 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec :path => '../'
4
-
5
- gem 'rails', '3.2.22'
6
- gem 'grape', '< 0.17'
7
- gem 'grape-swagger', '< 0.12.0'
8
- gem 'nokogiri', '< 1.6.9'
9
- gem 'coveralls', require: false
10
-
11
- group :development,:test do
12
- gem 'rspec'
13
- end
@@ -1,13 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec :path => '../'
4
-
5
- gem 'rails', '4.1.13'
6
- gem 'grape', '< 0.17'
7
- gem 'grape-swagger', '< 0.12.0'
8
- gem 'nokogiri', '< 1.6.9'
9
- gem 'coveralls', require: false
10
-
11
- group :development,:test do
12
- gem 'rspec'
13
- end
@@ -1,12 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec :path => '../'
4
-
5
- gem 'rails', '4.2.10'
6
- gem 'grape', '> 0.17'
7
- gem 'grape-swagger', '> 0.12.0'
8
- gem 'coveralls', require: false
9
-
10
- group :development,:test do
11
- gem 'rspec'
12
- end
@@ -1,14 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec :path => '../'
4
-
5
- gem 'rails', '4.2.7.1'
6
- gem 'grape', '< 0.17'
7
- gem 'grape-swagger', '< 0.12.0'
8
- gem 'nokogiri', '< 1.6.9'
9
- gem 'machinist'
10
- gem 'coveralls', require: false
11
-
12
- group :development,:test do
13
- gem 'rspec'
14
- end
@@ -1,13 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec :path => '../'
4
-
5
- gem 'rails', '4.2.7.1'
6
- gem 'grape', '> 0.17'
7
- gem 'grape-swagger', '> 0.12.0'
8
- gem 'machinist'
9
- gem 'coveralls', require: false
10
-
11
- group :development,:test do
12
- gem 'rspec'
13
- end
@@ -1,14 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec :path => '../'
4
-
5
- gem 'rails', '4.2.8'
6
- gem 'grape', '> 0.17'
7
- gem 'grape-swagger', '> 0.12.0'
8
- gem 'machinist'
9
- gem 'nokogiri', '< 1.10.1'
10
- gem 'coveralls', require: false
11
-
12
- group :development,:test do
13
- gem 'rspec'
14
- end