graphiti-rails 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +72 -0
  5. data/Rakefile +20 -0
  6. data/lib/generators/graphiti/api_test_generator.rb +70 -0
  7. data/lib/generators/graphiti/generator_mixin.rb +45 -0
  8. data/lib/generators/graphiti/install_generator.rb +80 -0
  9. data/lib/generators/graphiti/resource_generator.rb +180 -0
  10. data/lib/generators/graphiti/resource_test_generator.rb +56 -0
  11. data/lib/generators/graphiti/templates/application_resource.rb.erb +15 -0
  12. data/lib/generators/graphiti/templates/controller.rb.erb +61 -0
  13. data/lib/generators/graphiti/templates/create_request_spec.rb.erb +35 -0
  14. data/lib/generators/graphiti/templates/destroy_request_spec.rb.erb +22 -0
  15. data/lib/generators/graphiti/templates/index_request_spec.rb.erb +22 -0
  16. data/lib/generators/graphiti/templates/resource.rb.erb +11 -0
  17. data/lib/generators/graphiti/templates/resource_reads_spec.rb.erb +78 -0
  18. data/lib/generators/graphiti/templates/resource_writes_spec.rb.erb +67 -0
  19. data/lib/generators/graphiti/templates/show_request_spec.rb.erb +21 -0
  20. data/lib/generators/graphiti/templates/update_request_spec.rb.erb +32 -0
  21. data/lib/graphiti-rails.rb +4 -0
  22. data/lib/graphiti/rails.rb +60 -0
  23. data/lib/graphiti/rails/context.rb +33 -0
  24. data/lib/graphiti/rails/debugging.rb +18 -0
  25. data/lib/graphiti/rails/exception_handlers.rb +83 -0
  26. data/lib/graphiti/rails/graphiti_errors_testing.rb +20 -0
  27. data/lib/graphiti/rails/railtie.rb +143 -0
  28. data/lib/graphiti/rails/responders.rb +21 -0
  29. data/lib/graphiti/rails/test_helpers.rb +7 -0
  30. data/lib/graphiti/rails/version.rb +7 -0
  31. data/lib/tasks/graphiti.rake +53 -0
  32. metadata +245 -0
@@ -0,0 +1,33 @@
1
+ module Graphiti
2
+ module Rails
3
+ # Wraps controller actions in a [Graphiti Context](https://www.graphiti.dev/guides/concepts/resources#context) which points to the
4
+ # controller instance by default.
5
+ module Context
6
+ def self.included(klass)
7
+ klass.class_eval do
8
+ include Graphiti::Context
9
+ around_action :wrap_graphiti_context
10
+ end
11
+ end
12
+
13
+ # Called by [`#around_action`](https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-around_action)
14
+ # to wrap the current action in a Graphiti context defined by {#graphiti_context}.
15
+ def wrap_graphiti_context
16
+ Graphiti.with_context(graphiti_context, action_name.to_sym) do
17
+ yield
18
+ end
19
+ end
20
+
21
+ # The context to use for Graphiti Resources. Defaults to the controller instance.
22
+ # Can be redefined for different behavior.
23
+ def graphiti_context
24
+ if respond_to?(:jsonapi_context)
25
+ DEPRECATOR.deprecation_warning("Overriding jsonapi_context", "Override #graphiti_context instead")
26
+ jsonapi_context
27
+ else
28
+ self
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ module Graphiti
2
+ module Rails
3
+ # Wraps controller actions in a [Graphiti Debugger](https://www.graphiti.dev/guides/concepts/debugging#debugger).
4
+ module Debugging
5
+ def self.included(klass)
6
+ klass.around_action :debug_graphiti
7
+ end
8
+
9
+ # Called by [`#around_action`](https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-around_action)
10
+ # to wrap the current action in a Graphiti Debugger.
11
+ def debug_graphiti
12
+ Debugger.debug do
13
+ yield
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,83 @@
1
+ module Graphiti
2
+ module Rails
3
+ class ExceptionHandler < RescueRegistry::ExceptionHandler
4
+ # We've actually changed the signature here which is somewhat risky...
5
+ def build_payload(show_details: false, traces: nil, style: :rails)
6
+ case style
7
+ when :standard
8
+ super(show_details: show_details, traces: traces)
9
+ when :rails
10
+ # TODO: Find way to not duplicate RailsExceptionHandler
11
+ body = {
12
+ status: status_code,
13
+ error: title
14
+ }
15
+
16
+ if show_details
17
+ body[:exception] = exception.inspect
18
+ if traces
19
+ body[:traces] = traces
20
+ end
21
+ end
22
+
23
+ body
24
+ else
25
+ raise ArgumentError, "unknown style #{style}"
26
+ end
27
+ end
28
+
29
+ def formatted_response(content_type, **options)
30
+ # We're relying on the fact that `formatted_response` passes through unknown options to `build_payload`
31
+ if Graphiti::Rails.handled_exception_formats.include?(content_type.to_sym)
32
+ options[:style] = :standard
33
+ end
34
+ super
35
+ end
36
+ end
37
+
38
+ class FallbackHandler < ExceptionHandler
39
+ def formatted_response(content_type, **options)
40
+ if Graphiti::Rails.handled_exception_formats.include?(content_type.to_sym)
41
+ super
42
+ else
43
+ # The nil response will cause the default Rails handler to be used
44
+ nil
45
+ end
46
+ end
47
+ end
48
+
49
+ class InvalidRequestHandler < ExceptionHandler
50
+ # Mostly copied from GraphitiErrors could use some cleanup
51
+ # NOTE: That `style` is ignored
52
+ def build_payload(show_details: false, traces: nil, style: nil)
53
+ errors = exception.errors
54
+
55
+ errors_payload = []
56
+ errors.details.each_pair do |attribute, att_errors|
57
+ att_errors.each_with_index do |error, idx|
58
+ code = error[:error]
59
+ message = errors.messages[attribute][idx]
60
+
61
+ errors_payload << {
62
+ code: "bad_request",
63
+ status: "400",
64
+ title: "Request Error",
65
+ detail: errors.full_message(attribute, message),
66
+ source: {
67
+ pointer: attribute.to_s.tr(".", "/").gsub(/\[(\d+)\]/, '/\1'),
68
+ },
69
+ meta: {
70
+ attribute: attribute,
71
+ message: message,
72
+ code: code,
73
+ },
74
+ }
75
+ end
76
+ end
77
+
78
+ { errors: errors_payload }
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,20 @@
1
+ module Graphiti
2
+ module Rails
3
+ module GraphitiErrorsTesting
4
+ include RescueRegistry::RailsTestHelpers
5
+
6
+ def enable!
7
+ DEPRECATOR.deprecation_warning("GraphitiError.enable! in tests", "This method may cause leaked behavior between tests! Wrap in Graphiti::Rails::TestHelpers#handle_request_exceptions instead.")
8
+ super
9
+ handle_request_exceptions(true)
10
+ end
11
+
12
+ def disable!
13
+ DEPRECATOR.deprecation_warning("GraphitiError.disable! in tests", "This method may cause leaked behavior between tests! Wrap in Graphiti::Rails::TestHelpers#handle_request_exceptions instead. Note that exceptions are no longer caught by default during testing.")
14
+ handle_request_exceptions(false)
15
+ ensure
16
+ super
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Graphiti
4
+ module Rails
5
+ # This Railtie exposes some configuration options:
6
+ # * `config.graphiti.handled_exception_formats`, defaulting to `[:jsonapi]`.
7
+ # Formats in this list will always have their exceptions handled by Graphiti.
8
+ # * `config.graphiti.respond_to_formats`, defaulting to `[:json, :jsonapi, :xml]`.
9
+ # When {Graphiti::Rails::Responders} is included in a controller it will respond
10
+ # to these mime types by default.
11
+ class Railtie < ::Rails::Railtie
12
+ config.graphiti = ActiveSupport::OrderedOptions.new
13
+
14
+ config.graphiti.handled_exception_formats = [:jsonapi]
15
+
16
+ old_respond_to_formats = Graphiti::DEPRECATOR.silence { Graphiti.config.try(:respond_to) }
17
+ config.graphiti.respond_to_formats = old_respond_to_formats || [:json, :jsonapi, :xml]
18
+
19
+ rake_tasks do
20
+ load File.expand_path("../../tasks/graphiti.rake", __dir__)
21
+ end
22
+
23
+ initializer "graphiti-rails.config" do |app|
24
+ Graphiti::Rails.handled_exception_formats = app.config.graphiti.handled_exception_formats
25
+ Graphiti::Rails.respond_to_formats = app.config.graphiti.respond_to_formats
26
+
27
+ root = ::Rails.root
28
+
29
+ config_file = root.join(".graphiticfg.yml")
30
+ if config_file.exist?
31
+ cfg = YAML.load_file(config_file)
32
+ Graphiti.config.schema_path = root.join("public#{cfg["namespace"]}/schema.json")
33
+ else
34
+ Graphiti.config.schema_path = root.join("public/schema.json")
35
+ end
36
+ end
37
+
38
+ initializer "graphti-rails.logger" do
39
+ config.after_initialize do
40
+ Graphiti.config.debug = ::Rails.logger.level.zero?
41
+ Graphiti.logger = ::Rails.logger
42
+ end
43
+ end
44
+
45
+ initializer "graphiti-rails.init" do
46
+ if ::Rails.application.config.eager_load
47
+ config.after_initialize do |app|
48
+ ::Rails.application.reload_routes!
49
+ Graphiti.setup!
50
+ end
51
+ end
52
+
53
+ register_mime_type
54
+ register_parameter_parser
55
+ register_renderers
56
+ establish_concurrency
57
+ configure_endpoint_lookup
58
+ end
59
+
60
+ # from jsonapi-rails
61
+ MEDIA_TYPE = 'application/vnd.api+json'.freeze
62
+
63
+ PARSER = lambda do |body|
64
+ data = JSON.parse(body)
65
+ data[:format] = :jsonapi
66
+ data.with_indifferent_access
67
+ end
68
+
69
+ def register_mime_type
70
+ Mime::Type.register(MEDIA_TYPE, :jsonapi)
71
+ end
72
+
73
+ def register_parameter_parser
74
+ if ::Rails::VERSION::MAJOR >= 5
75
+ ActionDispatch::Request.parameter_parsers[:jsonapi] = PARSER
76
+ else
77
+ ActionDispatch::ParamsParser::DEFAULT_PARSERS[Mime[:jsonapi]] = PARSER
78
+ end
79
+ end
80
+
81
+ def register_renderers
82
+ ActiveSupport.on_load(:action_controller) do
83
+ ::ActionController::Renderers.add(:jsonapi) do |proxy, options|
84
+ self.content_type ||= Mime[:jsonapi]
85
+
86
+ # opts = {}
87
+ # if respond_to?(:default_jsonapi_render_options)
88
+ # opts = default_jsonapi_render_options
89
+ # end
90
+
91
+ if proxy.is_a?(Hash) # for destroy
92
+ render(options.merge(json: proxy))
93
+ else
94
+ proxy.to_jsonapi(options)
95
+ end
96
+ end
97
+ end
98
+
99
+ ActiveSupport.on_load(:action_controller) do
100
+ ActionController::Renderers.add(:jsonapi_errors) do |proxy, options|
101
+ self.content_type ||= Mime[:jsonapi]
102
+
103
+ validation = GraphitiErrors::Validation::Serializer.new \
104
+ proxy.data, proxy.payload.relationships
105
+
106
+ render \
107
+ json: {errors: validation.errors},
108
+ status: :unprocessable_entity
109
+ end
110
+ end
111
+ end
112
+
113
+ # Only run concurrently if our environment supports it
114
+ def establish_concurrency
115
+ Graphiti.config.concurrency = !::Rails.env.test? &&
116
+ ::Rails.application.config.cache_classes
117
+ end
118
+
119
+ def configure_endpoint_lookup
120
+ Graphiti.config.context_for_endpoint = ->(path, action) {
121
+ method = :GET
122
+ case action
123
+ when :show then path = "#{path}/1"
124
+ when :create then method = :POST
125
+ when :update
126
+ path = "#{path}/1"
127
+ method = :PUT
128
+ when :destroy
129
+ path = "#{path}/1"
130
+ method = :DELETE
131
+ end
132
+
133
+ route = begin
134
+ ::Rails.application.routes.recognize_path(path, method: method)
135
+ rescue
136
+ nil
137
+ end
138
+ "#{route[:controller]}_controller".classify.safe_constantize if route
139
+ }
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,21 @@
1
+ # For use with the `responders` gem responders gem to get respond_with
2
+ module Graphiti
3
+ module Rails
4
+ module Responders
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include ActionController::MimeResponds
9
+ respond_to(*Graphiti::Rails.respond_to_formats)
10
+ end
11
+
12
+ # Override to avoid location url generation (for now)
13
+ def respond_with(*args, &blk)
14
+ opts = args.extract_options!
15
+ opts[:location] = nil
16
+ args << opts
17
+ super(*args, &blk)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ module Graphiti
2
+ module Rails
3
+ module TestHelpers
4
+ include RescueRegistry::RailsTestHelpers
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Graphiti
4
+ module Rails
5
+ VERSION = '0.2.0'
6
+ end
7
+ end
@@ -0,0 +1,53 @@
1
+ namespace :graphiti do
2
+ include RescueRegistry::RailsTestHelpers
3
+
4
+ def session
5
+ @session ||= ActionDispatch::Integration::Session.new(Rails.application)
6
+ end
7
+
8
+ def setup_rails!
9
+ Rails.application.eager_load!
10
+ Rails.application.config.cache_classes = true
11
+ Rails.application.config.action_controller.perform_caching = false
12
+ end
13
+
14
+ def make_request(path, debug = false)
15
+ if path.split("/").length == 2
16
+ path = "#{ApplicationResource.endpoint_namespace}#{path}"
17
+ end
18
+ path << if path.include?("?")
19
+ "&cache=bust"
20
+ else
21
+ "?cache=bust"
22
+ end
23
+ path = "#{path}&debug=true" if debug
24
+ handle_request_exceptions do
25
+ session.get(path.to_s)
26
+ end
27
+ JSON.parse(session.response.body)
28
+ end
29
+
30
+ desc "Execute request without web server."
31
+ task :request, [:path, :debug] => [:environment] do |_, args|
32
+ setup_rails!
33
+ Graphiti.logger = Graphiti.stdout_logger
34
+ Graphiti::Debugger.preserve = true
35
+ require "pp"
36
+ path, debug = args[:path], args[:debug]
37
+ puts "Graphiti Request: #{path}"
38
+ json = make_request(path, debug)
39
+ pp json
40
+ Graphiti::Debugger.flush if debug
41
+ end
42
+
43
+ desc "Execute benchmark without web server."
44
+ task :benchmark, [:path, :requests] => [:environment] do |_, args|
45
+ setup_rails!
46
+ took = Benchmark.ms {
47
+ args[:requests].to_i.times do
48
+ make_request(args[:path])
49
+ end
50
+ }
51
+ puts "Took: #{(took / args[:requests].to_f).round(2)}ms"
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,245 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphiti-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter Wagenet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: graphiti
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rescue_registry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: graphiti_spec_helpers
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: kaminari
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: factory_bot
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: responders
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: yard
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: kramdown-parser-gfm
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rouge
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ description:
182
+ email:
183
+ - peter.wagenet@gmail.com
184
+ executables: []
185
+ extensions: []
186
+ extra_rdoc_files: []
187
+ files:
188
+ - CHANGELOG.md
189
+ - MIT-LICENSE
190
+ - README.md
191
+ - Rakefile
192
+ - lib/generators/graphiti/api_test_generator.rb
193
+ - lib/generators/graphiti/generator_mixin.rb
194
+ - lib/generators/graphiti/install_generator.rb
195
+ - lib/generators/graphiti/resource_generator.rb
196
+ - lib/generators/graphiti/resource_test_generator.rb
197
+ - lib/generators/graphiti/templates/application_resource.rb.erb
198
+ - lib/generators/graphiti/templates/controller.rb.erb
199
+ - lib/generators/graphiti/templates/create_request_spec.rb.erb
200
+ - lib/generators/graphiti/templates/destroy_request_spec.rb.erb
201
+ - lib/generators/graphiti/templates/index_request_spec.rb.erb
202
+ - lib/generators/graphiti/templates/resource.rb.erb
203
+ - lib/generators/graphiti/templates/resource_reads_spec.rb.erb
204
+ - lib/generators/graphiti/templates/resource_writes_spec.rb.erb
205
+ - lib/generators/graphiti/templates/show_request_spec.rb.erb
206
+ - lib/generators/graphiti/templates/update_request_spec.rb.erb
207
+ - lib/graphiti-rails.rb
208
+ - lib/graphiti/rails.rb
209
+ - lib/graphiti/rails/context.rb
210
+ - lib/graphiti/rails/debugging.rb
211
+ - lib/graphiti/rails/exception_handlers.rb
212
+ - lib/graphiti/rails/graphiti_errors_testing.rb
213
+ - lib/graphiti/rails/railtie.rb
214
+ - lib/graphiti/rails/responders.rb
215
+ - lib/graphiti/rails/test_helpers.rb
216
+ - lib/graphiti/rails/version.rb
217
+ - lib/tasks/graphiti.rake
218
+ homepage: https://www.graphiti.dev
219
+ licenses:
220
+ - MIT
221
+ metadata:
222
+ bug_tracker_uri: https://github.com/graphiti-api/graphiti-rails/issues
223
+ changelog_uri: https://github.com/graphiti-api/graphiti-rails/CHANGELOG.md
224
+ source_code_uri: https://github.com/graphiti-api/graphiti-rails
225
+ post_install_message:
226
+ rdoc_options: []
227
+ require_paths:
228
+ - lib
229
+ required_ruby_version: !ruby/object:Gem::Requirement
230
+ requirements:
231
+ - - ">="
232
+ - !ruby/object:Gem::Version
233
+ version: '0'
234
+ required_rubygems_version: !ruby/object:Gem::Requirement
235
+ requirements:
236
+ - - ">="
237
+ - !ruby/object:Gem::Version
238
+ version: '0'
239
+ requirements: []
240
+ rubyforge_project:
241
+ rubygems_version: 2.7.6
242
+ signing_key:
243
+ specification_version: 4
244
+ summary: Rails integration for Graphiti
245
+ test_files: []