jbuilder-json_api 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ff4461806b35c5a3d74588a69c25b2114cb229f
4
+ data.tar.gz: 14b6437c8ca3707ee94b7ecea417655976fb06d8
5
+ SHA512:
6
+ metadata.gz: 243d498cce513d4794dfa8babe42ee888f102b5dd328679bf923873597d5c4232425bcb67d329ed5a69ef3808e244066466b4133412b27709ee847ba21d23fc0
7
+ data.tar.gz: 297d98968a8d93e6628f309e5b21ad9954216327dca0d4962405233c540496f60a588d60a7cca8ba1ce226ecca74d1a356012fe8f42781d54c50e9d14e78b733
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Jbuilder::JsonApi | [![Build Status](https://travis-ci.org/vladfaust/jbuilder-json_api.svg?branch=master)](https://travis-ci.org/vladfaust/jbuilder-json_api) [![Code Climate](https://codeclimate.com/github/vladfaust/jbuilder-json_api/badges/gpa.svg)](https://codeclimate.com/github/vladfaust/jbuilder-json_api) [![Test Coverage](https://codeclimate.com/github/vladfaust/jbuilder-json_api/badges/coverage.svg)](https://codeclimate.com/github/vladfaust/jbuilder-json_api/coverage)
2
+
3
+ Adds a `json.api_format!(resources)` method to quickly represent a resource or collection in a valid [JSON API](http://jsonapi.org/) format without any new superclasses or weird setups. Set'n'go! :rocket:
4
+
5
+ ## Motivation
6
+
7
+ Official JSON API [implementations page](http://jsonapi.org/implementations/#server-libraries-ruby) shows us a variety of different serializers and other heavy-weight stuff. I' in love with [Jbuilder](https://github.com/rails/jbuilder), as it allows to format json responses with ease. Therefore I wanted to connect Jbuilder and JsonApi.org specs.
8
+
9
+ I'd like to notice that there already is one gem called [jbuilder-jsonapi](https://github.com/csexton/jbuilder-jsonapi) by [csexton](https://github.com/csexton), but it adds a links helper only. It's not enough for me! :facepunch:
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'jbuilder-json_api'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install jbuilder-json_api
26
+
27
+ ## Usage
28
+
29
+ Replace any content within any `*.json.jbuilder` file with the code below:
30
+ ```ruby
31
+ # Common example
32
+ json.api_format! @resources, @errors, meta: @meta, access_level: @user_access_level
33
+
34
+ # Articles w/o meta or access levels example
35
+ json.api_format! @articles, @errors
36
+ ```
37
+ You can also render formatted JSON straight from controller actions:
38
+ ```ruby
39
+ respond_to do |f|
40
+ f.json { render layout: false, json: JSON.parse(JbuilderTemplate.new(view_context).api_format!(@item).target!) }
41
+ f.html { render nothing: true, status: :bad_request }
42
+ end
43
+ ```
44
+ Each resource instance, as well as the included one, will be invoked with `json_api_attrs` & `json_api_relations` methods. These methods **MAY** be implemented within each model. `api_format!` method will try to get an object's permitted (**you are free do define authentication logic yourself!**) attributes and relations via those two methods.
45
+
46
+ Here is an example of implementation:
47
+ ```ruby
48
+ # Item model
49
+
50
+ def json_api_attr (access_level = nil)
51
+ attrs = []
52
+ attrs += %w(name description price buyoutable item_type category) if %i(user admin).include?access_level
53
+ attrs += %w(real_price in_stock) if access_level == :admin
54
+ attrs
55
+ end
56
+
57
+ def json_api_relations (access_level = nil)
58
+ %w(category orders)
59
+ end
60
+ ```
61
+ **Note** that the gem will call methods pulled with `json_api_relations and _attrs`. As for the above example, methods like `:name`, `:description`, `:orders` will be invoked for an Item instance. And yes, relations are fetched properly if an object responds to `orders`.
62
+
63
+ ## Development
64
+
65
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
66
+
67
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
68
+
69
+ ## Contributing
70
+
71
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/vladfaust/jbuilder-json_api](https://github.com/vladfaust/jbuilder-json_api). It would be really good if someone contributes. :smile:
72
+
73
+ ## ToDo
74
+
75
+ - [ ] Maybe add `Content-Type: application/vnd.api+json`. This spec is ignored right now :smirk:
76
+ - [ ] Add links tests
77
+ - [ ] Somehow implement `[fields]` parameter
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ module JsonAPI
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,154 @@
1
+ require 'jbuilder'
2
+ require 'jbuilder/json_api/version'
3
+
4
+ module JsonAPI
5
+ # Returns a valid-formatted JSON which follows JSON-API specifications:
6
+ # http://jsonapi.org/
7
+ #
8
+ # Arguments:
9
+ # :resources: - list of resources to render (may be even one or nil)
10
+ # :errors: - array of errors in below format:
11
+ # [{ status: 422, detail: 'This error occurs because...' }, {...}]
12
+ #
13
+ # Options:
14
+ # :access_level: - access level, e.g. nil, :user, :admin
15
+ # :meta: - a hash representing meta (additional) information
16
+ #
17
+ def api_format! (resources = nil, errors = nil, options = {})
18
+ options.merge access_level: nil
19
+ options.merge meta: nil
20
+
21
+ # Firstly, print meta
22
+ # http://jsonapi.org/format/#document-meta
23
+ #
24
+ if options[:meta] && !options[:meta].empty?
25
+ meta options[:meta]
26
+ end
27
+
28
+ # Secondly, take care of errors. If there are any,
29
+ # no 'data' section should be represented.
30
+ # http://jsonapi.org/format/#document-top-level
31
+ #
32
+ # Read more at
33
+ # http://jsonapi.org/format/#errors
34
+ #
35
+ if errors && !errors.empty?
36
+ ignore_nil! (@ignore_nil.nil? ? true : @ignore_nil)
37
+ errors errors do |error|
38
+ id error[:id]
39
+ status error[:status]
40
+ detail error[:detail]
41
+ code error[:code]
42
+ title error[:title]
43
+
44
+ source do
45
+ pointer error[:pointer]
46
+ paramater error[:parameter]
47
+ end
48
+
49
+ links do
50
+ about error[:about]
51
+ end
52
+ end
53
+ return self
54
+ end
55
+
56
+ resources = ::Kernel::Array resources
57
+
58
+ # http://jsonapi.org/format/#document-links
59
+ #
60
+ links do
61
+ begin
62
+ set! 'self', @context.request.path
63
+ rescue
64
+ # No @context given, cannot find path
65
+ end
66
+ end
67
+
68
+ data do
69
+ resources.blank? ? array! : _api_resource_objects(resources, options[:access_level])
70
+ end
71
+
72
+ included = []
73
+ resources.each do |resource|
74
+ next unless resource.respond_to?'json_api_relations'
75
+ resource.json_api_relations(options[:access_level]).each do |relationship|
76
+ included += ::Kernel::Array(resource.send(relationship))
77
+ end
78
+ end
79
+ included.uniq!
80
+
81
+ included do
82
+ _api_resource_objects(included, options[:access_level], resources) unless included.blank?
83
+ end
84
+
85
+ self
86
+ end
87
+
88
+ private
89
+
90
+ # Formats a resources array properly
91
+ # http://jsonapi.org/format/#document-resource-objects
92
+ #
93
+ def _api_resource_objects (resources, access_level, parent_resources = nil)
94
+ resources.each do |resource|
95
+ child! do
96
+ type resource.class.name.demodulize.to_s.downcase
97
+ id resource.id
98
+
99
+ # http://jsonapi.org/format/#document-resource-object-attributes
100
+ #
101
+ if resource.respond_to?'json_api_attrs'
102
+ attributes do
103
+ resource.json_api_attrs(access_level).each do |attribute|
104
+ set! attribute, resource.send(attribute)
105
+ end
106
+ end
107
+ end
108
+
109
+ # http://jsonapi.org/format/#document-resource-object-relationships
110
+ #
111
+ if resource.respond_to?'json_api_relations'
112
+ unless resource.json_api_relations(access_level).blank?
113
+ relationships do
114
+ resource.json_api_relations(access_level).each do |relationship|
115
+ set! relationship do
116
+ links do
117
+ begin
118
+ related @context.send("#{ relationship.pluralize }_path")
119
+ rescue
120
+ # No @context given, cannot find path
121
+ end
122
+ # TODO add a link to the relationship itself
123
+ end
124
+
125
+ data do
126
+ ::Kernel::Array(resource.send(relationship)).each do |relationship_instance|
127
+ # Relationships shouldn't ever link to the parent resource
128
+ #
129
+ next if !parent_resources.nil? && parent_resources.include?(relationship_instance)
130
+ child! do
131
+ type relationship_instance.class.name.demodulize.to_s.downcase
132
+ id relationship_instance.id
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ links do
143
+ begin
144
+ set! 'self', @context.send("#{ resource.class.name.demodulize.to_s.downcase }_path", resource)
145
+ rescue
146
+ # No @context given, cannot find path
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ Jbuilder.include JsonAPI
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jbuilder-json_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Vlad Faust
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jbuilder
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.11'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: factory_girl
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.5'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Adds a method to build a valid JSON API (jsonapi.org) response without
98
+ any new superclasses or weird setups. Set'n'go!
99
+ email:
100
+ - vladislav.faust@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - README.md
106
+ - Rakefile
107
+ - lib/jbuilder/json_api.rb
108
+ - lib/jbuilder/json_api/version.rb
109
+ homepage: https://github.com/vladfaust/jbuilder-json_api
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 2.5.2
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: Easily follow jsonapi.org specifications with Jbuilder
133
+ test_files: []