yaks-html 0.6.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +36 -0
  4. data/ADDING_FORMATS.md +13 -0
  5. data/CHANGELOG.md +149 -0
  6. data/Gemfile +4 -0
  7. data/IDENTIFIERS.md +113 -0
  8. data/LICENSE +7 -0
  9. data/README.md +526 -0
  10. data/Rakefile +43 -0
  11. data/bench/bench.rb +16 -0
  12. data/bench/bench_1000.rb +60 -0
  13. data/notes.org +72 -0
  14. data/shaved_yak.gif +0 -0
  15. data/yaks/README.md +526 -0
  16. data/yaks/lib/yaks/breaking_changes.rb +49 -0
  17. data/yaks/lib/yaks/collection_mapper.rb +52 -0
  18. data/yaks/lib/yaks/collection_resource.rb +73 -0
  19. data/yaks/lib/yaks/config/dsl.rb +165 -0
  20. data/yaks/lib/yaks/config.rb +65 -0
  21. data/yaks/lib/yaks/default_policy.rb +100 -0
  22. data/yaks/lib/yaks/format/collection_json.rb +42 -0
  23. data/yaks/lib/yaks/format/hal.rb +82 -0
  24. data/yaks/lib/yaks/format/json_api.rb +86 -0
  25. data/yaks/lib/yaks/format.rb +87 -0
  26. data/yaks/lib/yaks/fp/callable.rb +9 -0
  27. data/yaks/lib/yaks/fp/hash_updatable.rb +19 -0
  28. data/yaks/lib/yaks/fp/updatable.rb +17 -0
  29. data/yaks/lib/yaks/fp.rb +26 -0
  30. data/yaks/lib/yaks/mapper/association.rb +43 -0
  31. data/yaks/lib/yaks/mapper/association_mapper.rb +42 -0
  32. data/yaks/lib/yaks/mapper/attribute.rb +17 -0
  33. data/yaks/lib/yaks/mapper/class_methods.rb +37 -0
  34. data/yaks/lib/yaks/mapper/config.rb +44 -0
  35. data/yaks/lib/yaks/mapper/has_many.rb +29 -0
  36. data/yaks/lib/yaks/mapper/has_one.rb +15 -0
  37. data/yaks/lib/yaks/mapper/link.rb +91 -0
  38. data/yaks/lib/yaks/mapper.rb +75 -0
  39. data/yaks/lib/yaks/null_resource.rb +40 -0
  40. data/yaks/lib/yaks/primitivize.rb +45 -0
  41. data/yaks/lib/yaks/resource/link.rb +21 -0
  42. data/yaks/lib/yaks/resource.rb +51 -0
  43. data/yaks/lib/yaks/runner.rb +101 -0
  44. data/yaks/lib/yaks/util.rb +56 -0
  45. data/yaks/lib/yaks/version.rb +3 -0
  46. data/yaks/lib/yaks.rb +62 -0
  47. data/yaks/spec/acceptance/acceptance_spec.rb +48 -0
  48. data/yaks/spec/acceptance/json_shared_examples.rb +8 -0
  49. data/yaks/spec/acceptance/models.rb +48 -0
  50. data/yaks/spec/fixture_helpers.rb +14 -0
  51. data/yaks/spec/integration/map_to_resource_spec.rb +28 -0
  52. data/yaks/spec/json/confucius.collection.json +28 -0
  53. data/yaks/spec/json/confucius.hal.json +59 -0
  54. data/yaks/spec/json/confucius.json_api.json +51 -0
  55. data/yaks/spec/json/john.hal.json +29 -0
  56. data/yaks/spec/json/plant_collection.collection.json +32 -0
  57. data/yaks/spec/json/plant_collection.hal.json +34 -0
  58. data/yaks/spec/json/youtypeitwepostit.collection.json +45 -0
  59. data/yaks/spec/spec_helper.rb +30 -0
  60. data/yaks/spec/support/classes_for_policy_testing.rb +36 -0
  61. data/yaks/spec/support/deep_eql.rb +123 -0
  62. data/yaks/spec/support/fixtures.rb +6 -0
  63. data/yaks/spec/support/friends_mapper.rb +29 -0
  64. data/yaks/spec/support/models.rb +23 -0
  65. data/yaks/spec/support/pet_mapper.rb +3 -0
  66. data/yaks/spec/support/pet_peeve_mapper.rb +3 -0
  67. data/yaks/spec/support/shared_contexts.rb +57 -0
  68. data/yaks/spec/support/youtypeit_models_mappers.rb +20 -0
  69. data/yaks/spec/unit/yaks/collection_mapper_spec.rb +165 -0
  70. data/yaks/spec/unit/yaks/collection_resource_spec.rb +77 -0
  71. data/yaks/spec/unit/yaks/config/dsl_spec.rb +87 -0
  72. data/yaks/spec/unit/yaks/config_spec.rb +74 -0
  73. data/yaks/spec/unit/yaks/default_policy/derive_mapper_from_object_spec.rb +80 -0
  74. data/yaks/spec/unit/yaks/default_policy_spec.rb +70 -0
  75. data/yaks/spec/unit/yaks/format/collection_json_spec.rb +41 -0
  76. data/yaks/spec/unit/yaks/format/hal_spec.rb +44 -0
  77. data/yaks/spec/unit/yaks/format/json_api_spec.rb +42 -0
  78. data/yaks/spec/unit/yaks/format_spec.rb +37 -0
  79. data/yaks/spec/unit/yaks/fp/callable_spec.rb +13 -0
  80. data/yaks/spec/unit/yaks/fp/hash_updatable_spec.rb +22 -0
  81. data/yaks/spec/unit/yaks/fp/updatable_spec.rb +22 -0
  82. data/yaks/spec/unit/yaks/fp_spec.rb +31 -0
  83. data/yaks/spec/unit/yaks/mapper/association_mapper_spec.rb +60 -0
  84. data/yaks/spec/unit/yaks/mapper/association_spec.rb +135 -0
  85. data/yaks/spec/unit/yaks/mapper/attribute_spec.rb +20 -0
  86. data/yaks/spec/unit/yaks/mapper/class_methods_spec.rb +67 -0
  87. data/yaks/spec/unit/yaks/mapper/config_spec.rb +166 -0
  88. data/yaks/spec/unit/yaks/mapper/has_many_spec.rb +89 -0
  89. data/yaks/spec/unit/yaks/mapper/has_one_spec.rb +51 -0
  90. data/yaks/spec/unit/yaks/mapper/link_spec.rb +196 -0
  91. data/yaks/spec/unit/yaks/mapper_spec.rb +303 -0
  92. data/yaks/spec/unit/yaks/null_resource_spec.rb +32 -0
  93. data/yaks/spec/unit/yaks/primitivize_spec.rb +77 -0
  94. data/yaks/spec/unit/yaks/resource/link_spec.rb +20 -0
  95. data/yaks/spec/unit/yaks/resource_spec.rb +108 -0
  96. data/yaks/spec/unit/yaks/runner_spec.rb +260 -0
  97. data/yaks/spec/unit/yaks/util_spec.rb +49 -0
  98. data/yaks/spec/yaml/confucius.yaml +28 -0
  99. data/yaks/spec/yaml/youtypeitwepostit.yaml +9 -0
  100. data/yaks/yaks.gemspec +41 -0
  101. data/yaks-html/README.md +3 -0
  102. data/yaks-html/lib/yaks/format/html.rb +70 -0
  103. data/yaks-html/lib/yaks/format/template.html +56 -0
  104. data/yaks-html/lib/yaks-html.rb +3 -0
  105. data/yaks-html/spec/spec_helper.rb +6 -0
  106. data/yaks-html/yaks-html.gemspec +22 -0
  107. metadata +178 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ad2e8ddea305f3b9c4197e27c908f81df3a6aad7
4
+ data.tar.gz: b15fa02453e1b71c6de06ac9fc157dddf09be27d
5
+ SHA512:
6
+ metadata.gz: 169685112529c42eb30ffc8250344f10ba137367f50d73661346a110b701a533a91a609cb088b995488aaf0c277dec32ca23b048e551652fc0a11b224de77cc2
7
+ data.tar.gz: 03103204631478678810e10eccaca2cd66ed9bafbb3b25cf891b8f23e4aab01ed1b2493c918ebf3622f1a20d32d34e42476d8c3ac4b8fdea52752bd557d49066
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ pkg
2
+ .bundle
3
+ coverage
4
+ Gemfile.lock
5
+ *~
6
+ .yardoc
7
+ doc
data/.travis.yml ADDED
@@ -0,0 +1,36 @@
1
+ language: ruby
2
+ script: bundle exec rake $TASK
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0
6
+ - 2.1
7
+ - ruby-head
8
+ - rbx-2
9
+ - jruby
10
+ - jruby-head
11
+ env:
12
+ - TASK=yaks:rspec
13
+ - TASK=yaks:mutant
14
+ - TASK=yaks-html:rspec
15
+ - TASK=yaks-html:mutant
16
+ matrix:
17
+ allow_failures:
18
+ - rvm: ruby-head
19
+ - rvm: jruby-head
20
+ - env: TASK=yaks:mutant
21
+ - env: TASK=yaks-html:mutant
22
+ exclude:
23
+ - rvm: jruby
24
+ env: TASK=yaks:mutant
25
+ - rvm: jruby
26
+ env: TASK=yaks-html:mutant
27
+
28
+ - rvm: jruby-head
29
+ env: TASK=yaks:mutant
30
+ - rvm: jruby-head
31
+ env: TASK=yaks-html:mutant
32
+
33
+ - rvm: rbx-2
34
+ env: TASK=yaks:mutant
35
+ - rvm: rbx-2
36
+ env: TASK=yaks-html:mutant
data/ADDING_FORMATS.md ADDED
@@ -0,0 +1,13 @@
1
+ # Adding Extra Output Formats to Yaks
2
+
3
+ Individual output formats are each handled by a dedicated `Yaks::Serializer` class. These take a `Yaks::Resource` as input, and turn it into the requested output format.
4
+
5
+ A `Yaks::Resource` is created by "mapping" domain models by a `Yaks::Mapper`. In a `Yaks::Mapper` subclass a DSL is available to specify how to extract different types of information, for example attributes or links, and store them in a generalized way in a `Resource`.
6
+
7
+ Different formats have different features. Simple formats might just represent attributes, links, and subresources, other formats have queries, forms, or RDF identifiers. If a format represents data of a different nature, then the first step is to decide on a good and straightforward syntax to specify how to derive this data. This can then be stored in a `Yaks::Resource`, and formats that support it can use it, other formats can ignore it.
8
+
9
+ This is already the case, JSON-API ignores links for example.
10
+
11
+ So adding an output format is generally straightforward, as long as the information that the output format supports is already available in `Yaks::Resource`. In that case adding a `Yaks::Serializer::YourFormat` is all that is needed.
12
+
13
+ If the format has features that are not yet available then syntax needs to be added for those features. The guiding idea there is to try and find more than one format with the given feature, to make sure the intermediate abstraction is general and not tied to the specifics and vocabulary of a single format.
data/CHANGELOG.md ADDED
@@ -0,0 +1,149 @@
1
+ ### Development
2
+ [full changelog](http://github.com/plexus/yaks/compare/v0.5.0...master)
3
+
4
+ ### 0.5.0
5
+
6
+ * Yaks now serializes (returns a string), instead of returning a data structure. This is a preparatory step for supporting non-JSON formats. To get the old behavior back, do this
7
+
8
+ ``` ruby
9
+ yaks = Yaks.new do
10
+ skip :serialize
11
+ end
12
+ ```
13
+
14
+ * The old `after` hook has been removed, instead there are now generic hooks for all steps: `before`, `after`, `around`, `skip`; `:map`, `:format`, `:primitivize`, `:serialize`.
15
+
16
+ * By default Yaks uses `JSON.pretty_generate` as a JSON unparser. To use something else, for example `Oj.dump`, do this
17
+
18
+ ``` ruby
19
+ yaks = Yaks.new do
20
+ json_serializer &Oj.method(:dump)
21
+ end
22
+ ```
23
+
24
+ * Mapping a non-empty collection will try to infer the type, and hence rel of the nested items, based on the first object in the collection. This is only relevant for formats like HAL that don't have a top-level collection representation, and only matters when mapping a collection at the top level, not when mapping a collection from an association.
25
+
26
+ * Collection+JSON uses a link's "title" attribute to output a link's "name", to better correspond with other formats
27
+
28
+ * When registering a custom format (Yaks::Format subclass), the signature has changed
29
+
30
+ ``` ruby
31
+ # 0.4.3
32
+ Format.register self, :collection_json, 'application/vnd.collection+json'
33
+
34
+ # 0.5.0
35
+ register :collection_json, :json, 'application/vnd.collection+json'
36
+ ```
37
+
38
+ * `yaks.call` is now the preferred interface, rather than `yaks.serialize`, although there are no plans yet to remove the alias.
39
+
40
+ * The result of a call to `Yaks.new` now responds to `to_proc`, so you can treat it as a Proc/Symbol, e.g. `some_method &yaks`
41
+
42
+ * Improved YARD documentation
43
+
44
+ * 100% mutation coverage :trumpet: :tada:
45
+
46
+ ### 0.4.3
47
+
48
+ * when specifying a rel_template, instead of allowing for {src} and {dest} fields, now a single {rel} field is expected, which corresponds more with typical usage.
49
+
50
+ ```ruby
51
+ Yaks.new do
52
+ rel_template 'http://my-api/docs/relationships/{rel}'
53
+ end
54
+ ```
55
+
56
+ * Yaks::Serializer has been renamed to Yaks::Format
57
+
58
+ * Yaks::Mapper#{map_attributes,map_links,map_subresource} signature has changed, they now are responsible for adding themselves to a resource instance.
59
+
60
+ ```ruby
61
+ class FooMapper < Yaks::Mapper
62
+ def map_attributes(resource)
63
+ resource.update_attributes(:example => 'attribute')
64
+ end
65
+ end
66
+ ```
67
+
68
+ * Conditionally turn associations into links
69
+
70
+ ```ruby
71
+ class ShowMapper < Yaks::Mapper
72
+ has_many :events, href: '/show/{id}/events', link_if: ->{ events.count > 50 }
73
+ end
74
+ ```
75
+
76
+ * Reify `Yaks::Mapper::Attribute`
77
+
78
+ * Remove `Yaks::Mapper#filter`, instead override `#attributes` or `#associations` to filter things out, for example:
79
+
80
+ ```ruby
81
+ class SongMapper
82
+ attributes :title, :duration, :lyrics
83
+ has_one :artist
84
+ has_one :album
85
+
86
+ def minimal?
87
+ env['HTTP_PREFER'] =~ /minimal/
88
+ end
89
+
90
+ def attributes
91
+ if minimal?
92
+ super.reject {|attr| attr.name.equal? :lyrics } # These are instances of Yaks::Mapper::Attribute
93
+ else
94
+ super
95
+ end
96
+ end
97
+
98
+ def associations
99
+ return [] if minimal?
100
+ super
101
+ end
102
+ end
103
+ ```
104
+
105
+ * Give Attribute, Link, Association a common interface : `add_to_resource(resource, mapper, context)`
106
+ * Add persistent update methods to `Yaks::Resource`
107
+
108
+ ### v0.4.2
109
+
110
+ * JSON-API: render self links as href attributes
111
+ * HAL: render has_one returning nil as null, not as {}
112
+ * Keep track of the mapper stack, useful for figuring out if mapping the top level response or not, or for accessing parent
113
+ * Change Serializer.new(resource, options).serialize to Serializer.new(options).call(resource) for cosistency of "pipeline" interface
114
+ * Make Yaks::CollectionMapper#collection overridable for pagination
115
+ * Don't render links from custom link methods (link :foo, :method_that_generates_url) that return nil
116
+
117
+ ### v0.4.1
118
+
119
+ * Change how env is passed to yaks.serialize to match docs
120
+ * Fix JSON-API bug (#18 reported by Nicolas Blanco)
121
+ * Don't pluralize has_one association names in JSON-API
122
+
123
+ ## v0.4.0
124
+
125
+ * Introduce after {} post-processing hook
126
+ * Streamline interfaces and variable names, especially the use of `call`
127
+ * Improve deriving mappers automatically, even with Rails style autoloading
128
+ * Give CollectionResource a members_rel, for HAL-like formats with no top-level collection concept
129
+ * Switch back to using `src` and `dest` as the rel-template keys, instead of `association_name`
130
+ * deprecate `mapper_namespace` in favor of `namespace`
131
+
132
+ ### v0.4.0.rc1
133
+
134
+ * Introduce Yaks.new as the main public interface
135
+ * Fix JsonApiSerializer and make it compliant with current spec
136
+ * Remove Hamster dependency, Yaks new uses plain old Ruby arrays and hashes
137
+ * Remove `RelRegistry` and `ProfileRegistry` in favor of a simpler explicit syntax + policy based fallback
138
+ * Add more policy derivation hooks, plus make `DefaultPolicy` template for rel urls configurable
139
+ * Optionally take a Rack env hash, pass it around so mappers can inspect it
140
+ * Honor the HTTP Accept header if it is present in the rack env
141
+ * Add map_to_primitive configuration option
142
+
143
+ ## v0.3.0
144
+
145
+ * Allow partial expansion of templates, expand certain fields, leave others as URI template in the result.
146
+
147
+ ## v0.2.0
148
+
149
+ * links can now take a simple for a template to compute a link just like an attribute
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: 'yaks'
4
+ gemspec path: 'yaks-html'
data/IDENTIFIERS.md ADDED
@@ -0,0 +1,113 @@
1
+ # Identifiers
2
+
3
+ In Yaks, and Hypermedia message formats in general, a number of different types of identifiers are used. Some are full URIs and correspond with well defined specs. Some are just short identifers that are easy to program with.
4
+
5
+ Understanding these types of identifiers is key to creating a unifying model of a "Resource" that can be shared across output formats. We want to unify as much as possible across formats, without conflating things that are really not the same.
6
+
7
+ This document reflects my current limited understanding of things, based on possibly incorrect assumptions. Feedback is more than welcome.
8
+
9
+ ## rels
10
+
11
+ As used in HTML and Atom, these identifiers say what the relationship is between a resource and another resource it links to. There is a [registry of names](http://www.iana.org/assignments/link-relations/link-relations.xhtml), e.g. self, next, profile, stylesheet. Custom rels need to be fully qualified URLs. Keep in mind that these are simply opaque identifiers, but by using a known protocol like http they can be used to point at documentation.
12
+
13
+ Some examples
14
+
15
+ ```
16
+ copyright
17
+ stylesheet
18
+ http://api.example.com/rel/author
19
+ http://api.example.com/api-docs/relationships#comment
20
+ custom_scheme:foo
21
+ /order
22
+ ```
23
+
24
+ The last example is a relative URL, which would have to be expanded against the source URL of the document it is mentioned in.
25
+
26
+ In Yaks both links and subresources are specified with their rel(ationship).
27
+
28
+ ```ruby
29
+ class PersonMapper < Yaks::Mapper
30
+ link :self, '/people/{id}'
31
+ link 'http://api.example.com/rels#friends', '/people/{id}/friends'
32
+
33
+ has_one :address, rel: 'http://api.example.com/rels#address'
34
+ end
35
+ ```
36
+
37
+ For subresources the rel can be omitted, in which case it will be inferred based on the rel_template:
38
+
39
+ ```ruby
40
+ $yaks = Yaks.new do
41
+ rel_template 'http://api.example.com/rels/{dest}'
42
+ end
43
+ ```
44
+
45
+ Links and subresources are rendered keyed by rel in HAL and Collection+JSON. JSON-API renders `self` links as the `href` of a resource.
46
+
47
+ ## profiles
48
+
49
+ A specific IANA registered rel type is profile.
50
+
51
+ > Profile: Identifying that a resource representation conforms to a certain profile, without affecting the non-profile semantics of the resource representation.
52
+
53
+ Profile basically adds a layer of semantics on top of the hypermedia message format (e.g. HAL, Collection+JSON), which in turns defines semantics on top of a serialization format (JSON, XML, EDN). Loosely speaking it could be seen as the "type" or "class". For example if you know the profile of a resource, you might know you can expect to find a "name", "date_of_birth", or "post_body" field.
54
+
55
+ ## "type"
56
+
57
+ Despite the appealing rigor of having fully qualified URIs to identify things, sometimes you just want to call a person a `person`. In Yaks we call these short identifier the *type* for lack of a better word. In some cases, notably JSON-API, they are used literally in the output. More often they are used to derive full URIs based on a template.
58
+
59
+ The type of a mapper is inferred from its class name, but can be set explicitly as well.
60
+
61
+ ```ruby
62
+ class CatMapper < Yaks::Mapper
63
+ end
64
+
65
+ # type = "cat"
66
+ ```
67
+
68
+ ```ruby
69
+ class CatMapper < Yaks::Mapper
70
+ type 'feline'
71
+ end
72
+
73
+ # type => "feline"
74
+ ```
75
+
76
+ ## rdf class
77
+
78
+ RDF (Resource Description Framework) is a set of specifications for use in "semantic web" applications. RDF is based on "ontologies" that precisely define a "vocabulary" of "classes" and "predicates". An example class identifier for all Merlot wines could be
79
+
80
+ > http://www.w3.org/TR/2004/REC-owl-guide-20040210/wine#Merlot
81
+
82
+ (source [wikipedia](http://en.wikipedia.org/wiki/Resource_Description_Framework))
83
+
84
+ Not currently used by Yaks, but might become important when implementing support for JSON-LD or other RDF serialization formats.
85
+
86
+ ## CURIES
87
+
88
+ CURIES are "compact uris". The HAL format uses this so it can have the rigor of fully specified rels, with the ease of use of short-name "type" identifiers. The mechanism is similar to how one specifies and uses XML namespaces.
89
+
90
+ From the HAL spec:
91
+
92
+ ```json
93
+ {
94
+ "_links": {
95
+ "self": { "href": "/orders" },
96
+ "curies": [{ "name": "ea", "href": "http://example.com/docs/rels/{rel}", "templated": true }],
97
+ "next": { "href": "/orders?page=2" },
98
+ "ea:find": {
99
+ "href": "/orders{?id}",
100
+ "templated": true
101
+ },
102
+ "ea:admin": [{
103
+ "href": "/admins/2",
104
+ "title": "Fred"
105
+ }, {
106
+ "href": "/admins/5",
107
+ "title": "Kate"
108
+ }]
109
+ }
110
+ }
111
+ ```
112
+
113
+ In this case "ea:find" is just a shorthand for "http://example.com/docs/rels/find".
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2013-2014 Arne Brasseur
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.