collection_json_serializer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +137 -0
  7. data/Rakefile +10 -0
  8. data/collection_json_serializer.gemspec +26 -0
  9. data/lib/collection_json_serializer/builder.rb +56 -0
  10. data/lib/collection_json_serializer/core_ext/hash.rb +9 -0
  11. data/lib/collection_json_serializer/core_ext/symbol.rb +8 -0
  12. data/lib/collection_json_serializer/objects/item.rb +70 -0
  13. data/lib/collection_json_serializer/objects/template.rb +25 -0
  14. data/lib/collection_json_serializer/serializer.rb +71 -0
  15. data/lib/collection_json_serializer/support.rb +36 -0
  16. data/lib/collection_json_serializer/validator/url.rb +22 -0
  17. data/lib/collection_json_serializer/validator/value.rb +28 -0
  18. data/lib/collection_json_serializer/validator.rb +131 -0
  19. data/lib/collection_json_serializer/version.rb +5 -0
  20. data/lib/collection_json_serializer.rb +18 -0
  21. data/test/builder/builder_test.rb +157 -0
  22. data/test/fixtures/models.rb +3 -0
  23. data/test/fixtures/poro.rb +26 -0
  24. data/test/fixtures/serializers/custom_item_links_serializer.rb +7 -0
  25. data/test/fixtures/serializers/custom_item_serializer.rb +3 -0
  26. data/test/fixtures/serializers/custom_template_serializer.rb +5 -0
  27. data/test/fixtures/serializers/invalid_serializer.rb +4 -0
  28. data/test/fixtures/serializers/multiple_href_serializer.rb +3 -0
  29. data/test/fixtures/serializers/unknown_attribute_serializer.rb +3 -0
  30. data/test/fixtures/serializers/user_serializer.rb +7 -0
  31. data/test/fixtures/serializers/valid_serializer.rb +5 -0
  32. data/test/minitest_helper.rb +11 -0
  33. data/test/objects/item_test.rb +82 -0
  34. data/test/objects/template_test.rb +40 -0
  35. data/test/serializer/data_test.rb +29 -0
  36. data/test/serializer/href_test.rb +39 -0
  37. data/test/serializer/links_test.rb +23 -0
  38. data/test/serializer/template_test.rb +30 -0
  39. data/test/support/ext_test.rb +23 -0
  40. data/test/support/support_test.rb +29 -0
  41. data/test/validator/invalid_test.rb +138 -0
  42. data/test/validator/url_validator_test.rb +24 -0
  43. data/test/validator/validator_test.rb +64 -0
  44. data/test/validator/value_validator_test.rb +42 -0
  45. metadata +169 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f682b562eebc07619c02953ffa74bc78cbe6bd20
4
+ data.tar.gz: 9a23b69d8605c95f7faec47e86d2e14752dcb26d
5
+ SHA512:
6
+ metadata.gz: f48bbd38d5c5b65b6cf3592d583379a3e8b67414468fa13ca1c5369179a225f6ecb9267556feded7fa9dc8fad4f50259457f683f44ebbd4be0f9982be9ce75f2
7
+ data.tar.gz: 4b2fe8dbb795bf652085a3d6f5c82bb76fdf839c97c8bd5580e42ee0b7f26b60d23bfc8f59b23745959623a1c4de20b86199ce938b5cec15c32ac855f92275de
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
4
+ - 2.1.3
5
+ - 2.1.0
6
+ before_install:
7
+ - gem install bundler
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in collection_json_serializer.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Carles Jove i Buxeda
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # CollectionJson::Serializer
2
+
3
+ [![Build Status](https://travis-ci.org/carlesjove/collection_json_serializer.svg?branch=master)](https://travis-ci.org/carlesjove/collection_json_serializer)
4
+
5
+ | :warning: ** This is _not finished_ yet, so you better do not use it ** |
6
+ ---------------------------------------------------------------------------
7
+
8
+ A Ruby gem to respond with Collection+JSON.
9
+
10
+ CollectionJson::Serializer formats JSON responses following the Collection+JSON media type by Mike Amudsen. It also handles input data templates.
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'collection_json_serializer'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install collection_json_serializer
27
+
28
+ ## Usage
29
+
30
+ As this gem user, you will be mainly writing/generating and mantaining serializers for your models. A serializer goes like:
31
+
32
+ ```ruby
33
+ class UserSerializer < CollectionJson::Serializer
34
+ href self: "http://example.com/users/1",
35
+ collection: "http://example.com/users"
36
+
37
+ attributes :id, name: { prompt: "Your full name" }, :email
38
+
39
+ template :name, email: { prompt: "My email" }
40
+
41
+ # Please note that links can only be passed as hashes
42
+ links dashboard: { href: "http://example.com/my-dashboard" }
43
+ end
44
+ ```
45
+
46
+ Then, you pass your objects to the serializer:
47
+
48
+ ```ruby
49
+ # Create your object as you wish
50
+ @user = User.new(name: "Carles Jove", email: "hola@carlus.cat")
51
+
52
+ # You can also pass an array of objects
53
+ # user_serializer = UserSerializer.new([@user1, @user2, etc])
54
+
55
+ # Pass it to the serializer
56
+ user_serializer = UserSerializer.new(@user)
57
+
58
+ # Pass the serializer to the builder, and pack it as a hash
59
+ builder = Builder.new(user_serializer)
60
+ builder.pack
61
+ # => { collection: { version: "1.0" } }
62
+
63
+ # Get it as JSON
64
+ builder.to_json
65
+ ```
66
+
67
+ This will generate this Collection+JSON response:
68
+
69
+ ```javascript
70
+ { "collection":
71
+ {
72
+ "version" : "1.0",
73
+ "href" : "http://example.com/users",
74
+ "items" : [{
75
+ "href": "http://example.com/users/1",
76
+ "data": [
77
+ { "name": "id", "value": "1" },
78
+ { "name": "name", "value": "Carles Jove", "prompt": "Your full name" },
79
+ { "name": "email", "value": "email@example.com" },
80
+ ],
81
+ "links": [
82
+ { "name": "dashboard", "href": "http://example.com/my-dashboard" }
83
+ ]
84
+ }],
85
+ "template" : {
86
+ "data": [
87
+ { "name": "name", "value": "" },
88
+ { "name": "email", "value": "", "prompt": "My email" }
89
+ ]
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ #### URL placeholders
96
+
97
+ URLs can be generated dinamically with placeholder. A placeholder is a URL segment wrapped in curly braces. A placeholder can be any method that can be called on the object that the serializer takes (i.e. `id`, `username`, etc.).
98
+
99
+ ```ruby
100
+ class UserSerializer < CollectionJson::Serializer
101
+ href self: "http://example.com/users/{id}"
102
+ end
103
+ ```
104
+
105
+ All placeholders will be called, so you can use more than one if necessary, but you may use only one placeholer per segment.
106
+
107
+ ```ruby
108
+ class UserSerializer < CollectionJson::Serializer
109
+ # This is ok
110
+ href self: "http://example.com/users/{id}/{username}"
111
+
112
+ # This is not ok
113
+ href self: "http://example.com/users/{id}-{username}"
114
+ end
115
+ ```
116
+
117
+ #### Open Attributes Policy
118
+
119
+ Collection+JSON serializer has an __open attributes policy__, which means that objects' attributes can be extended at will. That is good if you want to use many of the [extensions available](https://github.com/collection-json/extensions), and also if you need to add custom extensions to suit your particular needs. Be aware that, [as the specs say](https://github.com/collection-json/spec#7-extensibility), you must only extend attributes in a way that won't break clients that are not aware of them.
120
+
121
+ ```ruby
122
+ class UserSerializer < CollectionJson::Serializer
123
+ attributes :id, name: { css_class: "people" }
124
+
125
+ template name: { regex: "/\A[a-zA-Z0-9_]*\z/" }
126
+
127
+ links profile: { css_class: "button" }
128
+ end
129
+ ```
130
+
131
+ ## Contributing
132
+
133
+ 1. Fork it ( https://github.com/[my-github-username]/collection_json_serializer/fork )
134
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
135
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
136
+ 4. Push to the branch (`git push origin my-new-feature`)
137
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/*_test.rb']
7
+ end
8
+
9
+ task :default => :test
10
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'collection_json_serializer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "collection_json_serializer"
8
+ spec.version = CollectionJson::Serializer::VERSION
9
+ spec.authors = ["Carles Jove i Buxeda"]
10
+ spec.email = ["hola@carlus.cat"]
11
+ spec.summary = %q{Serialize objects as Collection+JSON.}
12
+ spec.description = %q{CollectionJson::Serializer makes it easy to serialize objects into the Collection+JSON hypermedia type.}
13
+ spec.homepage = "https://github.com/carlesjove/collection_json_serializer"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activesupport", "~> 4.1"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.4"
26
+ end
@@ -0,0 +1,56 @@
1
+ module CollectionJson
2
+ class Serializer
3
+ class Builder
4
+ def initialize(serializer)
5
+ @serializer = serializer
6
+ @collection = { version: "1.0" }
7
+ end
8
+
9
+ def pack
10
+ if @serializer.errors.any?
11
+ error = "The #{@serializer.class} has errors: "
12
+ @serializer.errors.each_value { |v| error << v.join(", ") }
13
+ raise Exception, error
14
+ else
15
+ build
16
+ { collection: @collection }
17
+ end
18
+ end
19
+
20
+ def to_json
21
+ pack.to_json
22
+ end
23
+
24
+ private
25
+
26
+ def build
27
+ # There might be a more elegant way to do it, yes
28
+ add_href if @serializer.href.respond_to? :key
29
+ add_items if @serializer.attributes.present?
30
+ add_template if @serializer.template.present?
31
+ end
32
+
33
+ def add_href
34
+ if @serializer.href.key? :collection
35
+ @collection.store :href, @serializer.href[:collection]
36
+ end
37
+ end
38
+
39
+ def add_items
40
+ @collection.store :items, Array.new
41
+ @serializer.resources.each_index do |i|
42
+ item = CollectionJson::Serializer::Objects::Item.
43
+ new(@serializer, item: i)
44
+ @collection[:items] << item.create
45
+ end
46
+ end
47
+
48
+ def add_template
49
+ @collection.store :template, Hash.new
50
+ template = CollectionJson::Serializer::Objects::Template.
51
+ new(@serializer)
52
+ @collection[:template].store :data, template.create
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,9 @@
1
+ class Hash
2
+ def extract_params
3
+ params = {}
4
+ params[:name] = keys.first
5
+ params[:properties] = self[keys.first]
6
+
7
+ params
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ class Symbol
2
+ def extract_params
3
+ params = {}
4
+ params[:name] = self
5
+
6
+ params
7
+ end
8
+ end
@@ -0,0 +1,70 @@
1
+ module CollectionJson
2
+ class Serializer
3
+ class Objects
4
+ class Item
5
+ include CollectionJson::Serializer::Support
6
+
7
+ def initialize(serializer, item: 0)
8
+ @serializer = serializer
9
+ @index = item >= 0 ? item : 0
10
+ @resource = @serializer.resources[@index]
11
+ @item = Hash.new
12
+ end
13
+
14
+ def create
15
+ add_href if @serializer.href.present?
16
+ add_data if @serializer.attributes.present?
17
+ add_links if @serializer.links.present?
18
+
19
+ @item
20
+ end
21
+
22
+ private
23
+
24
+ def add_href
25
+ if @serializer.href.present?
26
+ @item.store :href, set_href
27
+ end
28
+ end
29
+
30
+ def add_data
31
+ @serializer.attributes.each do |attr|
32
+ params = attr.extract_params
33
+ value = extract_value_from(@resource, params[:name])
34
+
35
+ next unless value
36
+
37
+ c = { name: params[:name], value: value }
38
+ c.merge!(params[:properties]) if params[:properties]
39
+
40
+ start_object :data, Array.new
41
+ @item[:data] << c
42
+ end if @serializer.attributes.present?
43
+ end
44
+
45
+ def add_links
46
+ @serializer.links.each do |attr|
47
+ params = attr.extract_params
48
+
49
+ next unless params.key? :properties
50
+
51
+ start_object :links, Array.new
52
+ @item[:links] << {
53
+ name: params[:name].to_s,
54
+ href: params[:properties][:href]
55
+ }.merge!(params[:properties])
56
+ end if @serializer.links.present?
57
+ end
58
+
59
+ def start_object(name, type)
60
+ @item.store(name.to_sym, type) unless @item.key? name.to_sym
61
+ end
62
+
63
+ def set_href
64
+ url = @serializer.href[:self] || @serializer.href
65
+ parse_url(url, @resource)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,25 @@
1
+ module CollectionJson
2
+ class Serializer
3
+ class Objects
4
+ class Template
5
+ def initialize(serializer)
6
+ @serializer = serializer
7
+ @data = Array.new
8
+ end
9
+
10
+ def create
11
+ @serializer.template.each do |attr|
12
+ params = attr.extract_params
13
+
14
+ c = { name: params[:name], value: nil.to_s }
15
+ c.merge!(params[:properties]) if params[:properties]
16
+
17
+ @data << c
18
+ end if @serializer.template.present?
19
+
20
+ @data
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,71 @@
1
+ module CollectionJson
2
+ class Serializer
3
+ class << self
4
+ attr_accessor :href
5
+ attr_accessor :attributes
6
+ attr_accessor :template
7
+ attr_accessor :links
8
+ end
9
+
10
+ def self.inherited(base)
11
+ base.href = []
12
+ base.attributes = []
13
+ base.template = []
14
+ base.links = []
15
+ end
16
+
17
+ def self.href(*attrs)
18
+ @href.concat attrs
19
+ end
20
+
21
+ def self.attributes(*attrs)
22
+ @attributes.concat attrs
23
+ end
24
+
25
+ def self.template(*attrs)
26
+ @template.concat attrs
27
+ end
28
+
29
+ def self.links(*attrs)
30
+ @links.concat attrs
31
+ end
32
+
33
+ attr_accessor :resources
34
+
35
+ def initialize(resource)
36
+ @resources = if resource.respond_to? :to_ary
37
+ resource
38
+ else
39
+ [resource]
40
+ end
41
+ end
42
+
43
+ def href
44
+ self.class.href.first
45
+ end
46
+
47
+ def attributes
48
+ self.class.attributes
49
+ end
50
+
51
+ def template
52
+ self.class.template
53
+ end
54
+
55
+ def links
56
+ self.class.links
57
+ end
58
+
59
+ def invalid?
60
+ Validator.new(self).invalid?
61
+ end
62
+
63
+ def valid?
64
+ Validator.new(self).valid?
65
+ end
66
+
67
+ def errors
68
+ Validator.new(self).errors
69
+ end
70
+ end
71
+ end