hypertext_application_language 0.0.0

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: 1c9c7296b2e50bdb822d5af41b4ff5b562be1e2f
4
+ data.tar.gz: e863ec8f7f2ed73ef83d6a2a1fc3dc5f80f2b1fc
5
+ SHA512:
6
+ metadata.gz: 035ba935018b647c548641f9005aebb79bc913ee99cde200a240dcd7a0cba64aeea0020c1526d88aeb3618a1a698d08217d7395b1d3e8b0caea8db54e1c318f2
7
+ data.tar.gz: c3e2785fa7a9cdc226b9c82b0077347114bad4c2b696411c788a1bf1e7bf8358b69f3e345bfad72e9ad45eaa119c70ad6dda86a508810f935b0625d2ffae4c93
data/.gitignore ADDED
File without changes
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --no-private
2
+ --protected
3
+ --main README.md
4
+ -
5
+ MIT-LICENSE.txt
6
+ ChangeLog.md
data/ChangeLog.md ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hypertext_application_language (0.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ docile (1.1.5)
11
+ json (1.8.3)
12
+ json (1.8.3-java)
13
+ rspec (3.3.0)
14
+ rspec-core (~> 3.3.0)
15
+ rspec-expectations (~> 3.3.0)
16
+ rspec-mocks (~> 3.3.0)
17
+ rspec-core (3.3.2)
18
+ rspec-support (~> 3.3.0)
19
+ rspec-expectations (3.3.1)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.3.0)
22
+ rspec-mocks (3.3.2)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.3.0)
25
+ rspec-support (3.3.0)
26
+ simplecov (0.10.0)
27
+ docile (~> 1.1.0)
28
+ json (~> 1.8)
29
+ simplecov-html (~> 0.10.0)
30
+ simplecov-html (0.10.0)
31
+
32
+ PLATFORMS
33
+ java
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ bundler
38
+ hypertext_application_language!
39
+ rspec
40
+ simplecov
41
+
42
+ BUNDLED WITH
43
+ 1.10.6
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright © 2015, Roy Ratcliffe, Pioneering Software, United Kingdom
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the “Software”), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EITHER
14
+ EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
17
+ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Hypertext Application Language (HAL)
2
+
3
+ What HTML does for web browsers, HAL does for applications.
4
+
5
+ HTML gives you pages of marked-up information for presentation to users; HAL
6
+ gives you marked-up representations for application consumption. Applications
7
+ can easily extract information about remote resources, including relationships
8
+ between resources. What HAL calls a representation, HTML calls a web page; such
9
+ pages have links to other pages. HTML was designed for presenting information
10
+ to humans. HAL was designed for presenting information to applications.
11
+
12
+ This gem provides a suite of Ruby classes for rendering and parsing resource
13
+ representations, including their links, properties and nested representations
14
+ of embedded resources.
15
+
16
+ ## Deviations
17
+
18
+ The interfaces and implementations largely echo those written in Java, but
19
+ there are some deliberate deviations.
20
+
21
+ The Ruby gem adds some consistency in naming. Representation is the name used
22
+ to describe an object that represents some remote resource. The term “resource”
23
+ describes the actual remote resource. Representations represent resources only;
24
+ they are not the resource themselves.
25
+
26
+ The use of “currie” has been replaced as it partially overlaps the idea of
27
+ currying and curried functions in mathematics and computer science, when in
28
+ fact the term ‘curie’ only refers to a compact URI. The acronym only
29
+ coincidentally resembles the word “curry,” a delicious Asian meal.
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,21 @@
1
+ File.expand_path('../lib', __FILE__).tap do |path|
2
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
3
+ end
4
+
5
+ require 'hypertext_application_language/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'hypertext_application_language'
9
+ spec.version = HypertextApplicationLanguage::VERSION
10
+ spec.summary = %q{Hypertext Application Language}
11
+ spec.description = %q{}
12
+ spec.homepage = 'http://stateless.co/hal_specification.html'
13
+ spec.authors = ['Roy Ratcliffe']
14
+ spec.email = ['roy@pioneeringsoftware.co.uk']
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.license = 'MIT'
17
+
18
+ spec.add_development_dependency 'bundler'
19
+ spec.add_development_dependency 'rspec'
20
+ spec.add_development_dependency 'simplecov'
21
+ end
@@ -0,0 +1,86 @@
1
+ # coding: utf-8
2
+ module HypertextApplicationLanguage
3
+ # An object that parses a representation.
4
+ #
5
+ # The parser does not “parse” the hash, strictly speaking. Nor does it check
6
+ # the hash for strict compliance. Instead it just picks out pieces of the hash
7
+ # matching HAL expectations. Nothing more than that.
8
+ class HashRepresentationParser
9
+ # Parses a hash, loading the given representation with its
10
+ # hypertext-application-language content.
11
+ def parse(representation, object)
12
+ # Takes compact URI pairs from the links. Looks for the +curies+ sub-hash
13
+ # within +_links+ root-level hash. Every CURIE is a hash with a name and a
14
+ # hypertext reference.
15
+ links_object = object[Representation::LINKS]
16
+ if links_object
17
+ curie_objects = links_object[Link::CURIES_REL]
18
+ if curie_objects
19
+ curie_objects = [curie_objects] unless curie_objects.is_a?(Array)
20
+ curie_objects.select { |element| element.is_a?(Hash) }.each do |object|
21
+ # Both the name and the hypertext reference must have string type;
22
+ # there is no scope for representing references except using their
23
+ # string form. Represent with strings, otherwise the parser ignores
24
+ # it.
25
+ #
26
+ # The link's +href+ attribute carries the relative reference, even
27
+ # though the reference is not a true hypertext reference since it
28
+ # contains the +{rel}+ token as a placeholder for substitution.
29
+ name = object[Link::NAME]
30
+ next unless name.is_a?(String)
31
+ ref = object[Link::HREF]
32
+ next unless ref.is_a?(String)
33
+ representation.with_namespace(name, ref)
34
+ end
35
+ end
36
+
37
+ # The links object is a hash of strings paired with a hash or an array
38
+ # of hashes.
39
+ links_object.each do |rel, link_objects|
40
+ next if rel == Link::CURIES_REL
41
+ link_objects = [link_objects] unless link_objects.is_a?(Array)
42
+ link_objects.each do |link_object|
43
+ # Makes you wonder. Should the following pass the name? Doing so
44
+ # allows name-spaces to sneak into the links. Name-spaces should
45
+ # only appear in the +curies+ hash. They will never function as a
46
+ # CURIE unless they do.
47
+ href = object[Link::HREF]
48
+ next unless href
49
+ link = Link.new(rel, href)
50
+ link.name = object[Link::NAME] if object[Link::NAME]
51
+ link.title = object[Link::TITLE] if object[Link::TITLE]
52
+ link.hreflang = object[Link::HREFLANG] if object[Link::HREFLANG]
53
+ link.profile = object[Link::PROFILE] if object[Link::PROFILE]
54
+ representation.with_link(link)
55
+ end
56
+ end
57
+ end
58
+
59
+ # Properties should only contain primitive types: string, numbers,
60
+ # booleans or arrays of the same.
61
+ object.each do |name, value|
62
+ next if [Representation::LINKS, Representation::EMBEDDED].include?(name)
63
+ # if value.is_a?(Array)
64
+ # representation.with_property(name, value.map(&:to_s))
65
+ # else
66
+ # representation.with_property(name, value.to_s)
67
+ # end
68
+ representation.with_property(name, value)
69
+ end
70
+
71
+ embedded = object[Representation::EMBEDDED]
72
+ if embedded
73
+ embedded.each do |rel, objects|
74
+ # The relation key must be a string. Turn the value into an array of
75
+ # hashes, parsing an embedded representation from each hash.
76
+ objects = [objects] unless objects.is_a?(Array)
77
+ objects.each do |object|
78
+ embedded_representation = Representation.new
79
+ parse(embedded_representation, object)
80
+ representation.with_representation(rel, embedded_representation)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,88 @@
1
+ module HypertextApplicationLanguage
2
+ # Renders a representation as a Hash.
3
+ class HashRepresentationRenderer
4
+ # Renders a representation to a Hash.
5
+ # @return [Hash] The resulting Hash representation.
6
+ def render(representation)
7
+ render_representation(representation)
8
+ end
9
+
10
+ private
11
+
12
+ # Renders either a top-level representation or an embedded resource.
13
+ def render_representation(representation, embedded=false)
14
+ object = {}
15
+
16
+ # Render the name-spaces and links but only if there are name-spaces and
17
+ # links; also render links if there are name-spaces to render, assuming
18
+ # not embedded. Create a hash representation without links if there are
19
+ # none. Merge the name-spaces and links.
20
+ unless representation.links.empty? && (embedded || representation.namespaces.empty?)
21
+ links_object = object[Representation::LINKS] ||= {}
22
+ links = []
23
+ unless embedded
24
+ representation.namespaces.each do |name, ref|
25
+ links.push(Link.new(Link::CURIES_REL, ref, Link::NAME => name))
26
+ end
27
+ end
28
+ links.concat(representation.links)
29
+ links_for_rel = {}
30
+ links.each do |link|
31
+ (links_for_rel[link.rel] ||= []).push(link)
32
+ end
33
+ links_for_rel.each do |rel, links|
34
+ link_objects = links.map do |link|
35
+ # Render a link as a hash. Importantly, the following does not
36
+ # render the link relation; it only renders the link content. The
37
+ # relation appears in the rendered output as the key, not as part of
38
+ # the hash value paired with the key.
39
+ link_object = {}
40
+
41
+ # There is always a relation and a hypertext reference for every
42
+ # link; no need to check for +nil+. If you set up a link with a
43
+ # +nil+ reference, the output will contain a blank string, since
44
+ # +nil.to_s+ answers +""+.
45
+ link_object[Link::HREF] = link.href.to_s
46
+
47
+ link_object[Link::NAME] = link.name.to_s if link.name
48
+ link_object[Link::TITLE] = link.title.to_s if link.title
49
+ link_object[Link::HREFLANG] = link.hreflang.to_s if link.hreflang
50
+ link_object[Link::PROFILE] = link.profile.to_s if link.profile
51
+
52
+ link_object
53
+ end
54
+ links_object[rel] = link_objects.length == 1 ? link_objects.first : link_objects
55
+ end
56
+ end
57
+
58
+ # Merge the representation's properties. Properties live at the root of
59
+ # the hash. Merging is just another way to assign values to their name
60
+ # keys.
61
+ #
62
+ # representation.properties.each do |name, value|
63
+ # hash[name] = value
64
+ # end
65
+ #
66
+ # This makes some assumptions about the representation properties. It
67
+ # assumes that the property values are primitive types: strings, numbers,
68
+ # booleans. They should never be hashes or custom classes.
69
+ object.merge!(representation.properties)
70
+
71
+ # Render embedded resource representations. Each representation retains
72
+ # zero or more sub-representations by their relation. The relation maps to
73
+ # an array of embedded representations, zero or more for each
74
+ # relation. Render each one recursively.
75
+ unless representation.representations.empty?
76
+ embedded_object = object[Representation::EMBEDDED] ||= {}
77
+ representation.representations_for_rel.each do |rel, representations|
78
+ objects = representations.map do |representation|
79
+ render_representation(representation, true)
80
+ end
81
+ embedded_object[rel] = objects.length == 1 ? objects.first : objects
82
+ end
83
+ end
84
+
85
+ object
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,119 @@
1
+ module HypertextApplicationLanguage
2
+ # Links belong to representations; a representation retains zero or more
3
+ # links. Each link gives a hypertext reference for a relation, at least. Some
4
+ # links provide more information.
5
+ #
6
+ # Links have attributes. The relation and hypertext reference attributes are
7
+ # primary and are always required for every link. Representations may carry
8
+ # multiple links with the _same_ relation, just like a HTML page. Links
9
+ # sharing the same relation refer to the same thing, or things.
10
+ #
11
+ # This is the simplest possible implementation of a
12
+ # hypertext-application-language (HAL) link. It does not support
13
+ # immutability. All links and their attributes remain mutable, even after
14
+ # attaching to a representation. This condition continues until you freeze the
15
+ # instance, according to the Ruby immutability paradigm.
16
+ class Link
17
+ # Creates a new mutable link.
18
+ # @return [Link] Answers a newly initialised link.
19
+ # @param [String] rel Relation.
20
+ # @param [String] href Hypertext reference.
21
+ # @param [Array<String, Hash>] args Array of strings used to initialise the
22
+ # optional attributes in the following order: +name+, +title+, +hreflang+
23
+ # and +profile+. If the last element is a Hash, these become keyword
24
+ # arguments where you can set up the optional link attributes by name,
25
+ # either symbolic or string.
26
+ def initialize(rel, href, *args)
27
+ @rel = rel
28
+ @href = href
29
+
30
+ # Take an array of arguments following the #rel and #href; these arguments
31
+ # assign to the optional link attributes in order. Pick out keyword
32
+ # arguments if the last argument is a #Hash. Be indifferent about the
33
+ # keywords; accept both string and symbols. Do this by converting the
34
+ # string keys to symbols if they do not otherwise match anything in the
35
+ # keyword arguments hash. Take care not to re-invoke the hash fetch again
36
+ # using the subscript operator, otherwise the default #Proc will recurse
37
+ # indefinitely.
38
+ keyword_args = args.last.is_a?(Hash) ? args.pop : {}
39
+ keyword_args.default_proc = proc do |hash, key|
40
+ hash.fetch(key.to_sym, nil)
41
+ end
42
+ @name, @title, @hreflang, @profile = args
43
+ @name ||= keyword_args[NAME]
44
+ @title ||= keyword_args[TITLE]
45
+ @hreflang ||= keyword_args[HREFLANG]
46
+ @profile ||= keyword_args[PROFILE]
47
+ end
48
+
49
+ # When you freeze the object, also freeze all the instance
50
+ # variables. Otherwise, you can still modify the existing instance
51
+ # variables' assigned objects even though you cannot reassign the variables
52
+ # themselves.
53
+ def freeze
54
+ instance_variables.each do |instance_variable|
55
+ instance_variable_get(instance_variable).freeze
56
+ end
57
+ super
58
+ end
59
+
60
+ # @!group Required attributes
61
+
62
+ attr_accessor :rel
63
+
64
+ # Link attribute describing the hypertext reference. The reference can be a
65
+ # full universal resource location, or some element thereof. It can be just
66
+ # the path.
67
+ attr_accessor :href
68
+
69
+ # @!group Optional attributes
70
+
71
+ attr_accessor :name
72
+ attr_accessor :title
73
+
74
+ # Returns the ISO 639-1 code describing the link's language. You can have
75
+ # multiple links for the same relation but for different languages.
76
+ attr_accessor :hreflang
77
+
78
+ attr_accessor :profile
79
+
80
+ # @!group Required link attribute names
81
+
82
+ REL = 'rel'.freeze
83
+ HREF = 'href'.freeze
84
+
85
+ # @!group Optional link attribute names
86
+
87
+ NAME = 'name'.freeze
88
+ TITLE = 'title'.freeze
89
+ HREFLANG = 'hreflang'.freeze
90
+ PROFILE = 'profile'.freeze
91
+
92
+ # @!endgroup
93
+
94
+ # Array of attribute names including those required and those optional.
95
+ ATTRIBUTE_NAMES = [
96
+ # required
97
+ REL,
98
+ HREF,
99
+
100
+ # optional
101
+ NAME,
102
+ TITLE,
103
+ HREFLANG,
104
+ PROFILE,
105
+ ].freeze
106
+
107
+ # @!group Special link relations
108
+
109
+ # This special link relation describes the link to the representations own
110
+ # source, i.e. itself.
111
+ SELF_REL = 'self'.freeze
112
+
113
+ # Special link relation used for name-spaces. Representation name-spaces
114
+ # appear in rendered links under the "curies" relation; where the link
115
+ # +name+ corresponds to the name-space name and the link +href+ corresponds
116
+ # to the name-space reference with its embedded +{rel}+ placeholder.
117
+ CURIES_REL = 'curies'.freeze
118
+ end
119
+ end
@@ -0,0 +1,72 @@
1
+ module HypertextApplicationLanguage
2
+ # Handles _compact_ URIs, a.k.a. CURIEs. Representations and representation
3
+ # factories have CURIEs handled by a name-space manager instance.
4
+ #
5
+ # @see http://www.w3.org/TR/curie/
6
+ class NamespaceManager
7
+ # Defines the relative reference token, the placeholder used in CURIEs.
8
+ REL = '{rel}'.freeze
9
+
10
+ extend Forwardable
11
+
12
+ # @!method namespaces
13
+ # @return [Hash<String, String>] Answers a hash of relative references by
14
+ # their name.
15
+ def_delegator :@ref_for_name, :dup, :namespaces
16
+
17
+ def initialize
18
+ # Retains one relative hypertext reference for one name.
19
+ @ref_for_name = {}
20
+ end
21
+
22
+ # Adds a name-space to this manager.
23
+ # @param [String] name
24
+ # Names the CURIE. This appears in CURIE references as the prefix before
25
+ # the colon. The relative reference comes after the colon.
26
+ # @param [String] ref
27
+ # Gives the CURIE's relative reference. It must include the +{rel}+
28
+ # placeholder identifying where to substitute the CURIE argument, the
29
+ # value that replaces the placeholder.
30
+ def with_namespace(name, ref)
31
+ @ref_for_name[name] = ref
32
+ self
33
+ end
34
+
35
+ # Converts an expanded hypertext reference to a CURIE'd reference based on
36
+ # the current set of CURIE specifications, the name-spaces.
37
+ # @return [String] Answers the CURIE'd reference corresponding to the given
38
+ # hypertext reference, or +nil+ if there is no matching CURIE.
39
+ def curie(href)
40
+ @ref_for_name.each do |name, ref|
41
+ # start_index = ref.index(REL)
42
+ # end_index = start_index + REL.length
43
+ # left = ref[0...start_index]
44
+ # right = ref[end_index..-1]
45
+ # if href.start_with?(left) && href.end_with?(right)
46
+ # middle = href[start_index..(end_index - 2)]
47
+ # return name + ':' + middle
48
+ # end
49
+ left, right = ref.split(REL)
50
+ if href.start_with?(left) && href.end_with?(right)
51
+ return name + ':' + href[left.length...-right.length]
52
+ end
53
+ end
54
+ nil
55
+ end
56
+
57
+ # Converts a CURIE'd reference to a hypertext reference.
58
+ # @param [String] curie The argument is a string comprising a name prefix
59
+ # followed by a colon delimiter, followed by a CURIE argument.
60
+ #
61
+ # Splits the name at the first colon. The prefix portion before the colon
62
+ # identifies the name of the CURIE. The portion after the colon replaces the
63
+ # +{rel}+ placeholder. This is a very basic way to parse a CURIE, but it
64
+ # works.
65
+ def href(curie)
66
+ name, arg = curie.split(':', 2)
67
+ ref = @ref_for_name[name]
68
+ return nil unless ref
69
+ ref.sub(REL, arg)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,97 @@
1
+ require 'hypertext_application_language/namespace_manager'
2
+ require 'hypertext_application_language/link'
3
+
4
+ module HypertextApplicationLanguage
5
+ # Represents a resource. This includes sub-resources which also have their own
6
+ # representation. Representations have links, properties and sub-resources.
7
+ #
8
+ # Resource is the name of a representation embedded within another
9
+ # super-representation. Representations have zero or resources. They will
10
+ # appear in the rendered results as embedded resources.
11
+ class Representation
12
+ LINKS = '_links'.freeze
13
+ EMBEDDED = '_embedded'.freeze
14
+
15
+ # Array of links.
16
+ attr_accessor :links
17
+
18
+ attr_accessor :properties
19
+
20
+ # Hash of string-array pairs. The arrays contain embedded representations,
21
+ # zero or more.
22
+ attr_accessor :representations_for_rel
23
+
24
+ def initialize
25
+ @namespace_manager = NamespaceManager.new
26
+ @links = []
27
+ @properties = {}
28
+ @representations_for_rel = {}
29
+ end
30
+
31
+ # @!group Namespaces
32
+
33
+ def namespaces
34
+ @namespace_manager.namespaces
35
+ end
36
+
37
+ def with_namespace(name, ref)
38
+ @namespace_manager.with_namespace(name, ref)
39
+ self
40
+ end
41
+
42
+ # @!group Links
43
+
44
+ def link
45
+ link_for(Representation::SELF_REL)
46
+ end
47
+
48
+ def link_for(href_or_rel)
49
+ links_for(href_or_rel).first
50
+ end
51
+
52
+ # Answers the representation's links selected by either a hypertext
53
+ # reference or by a relation.
54
+ def links_for(href_or_rel)
55
+ rel = @namespace_manager.curie(href_or_rel) || href_or_rel
56
+ @links.select { |link| link.rel == rel }
57
+ end
58
+
59
+ # Adds a link to this representation.
60
+ #
61
+ # Ruby does not support argument overloading. If there is just one argument,
62
+ # assume that it is a +Link+ instance. If not, if more than one argument,
63
+ # assume that they are +String+ instances.
64
+ def with_link(*args)
65
+ @links.push(args.length == 1 ? args.first : Link.new(*args))
66
+ self
67
+ end
68
+
69
+ # @!group Properties
70
+
71
+ def value_for(name, default_value=nil)
72
+ @properties[name] || default_value
73
+ end
74
+
75
+ def with_property(name, value)
76
+ @properties[name] = value
77
+ self
78
+ end
79
+
80
+ # @!group Representations
81
+
82
+ # Takes the array values from the representations by relation, then flattens
83
+ # the array of arrays of representations. The result becomes an array of
84
+ # representations, all of them but without their relation to the
85
+ # super-representation that having been stripped away.
86
+ def representations
87
+ @representations_for_rel.values.flatten
88
+ end
89
+
90
+ # Associates a given embedded representation with this representation by a
91
+ # given relation.
92
+ def with_representation(rel, representation)
93
+ (@representations_for_rel[rel] ||= []).push(representation)
94
+ self
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,6 @@
1
+ # Describes the Hypertext Application Language in Ruby, a set of classes for
2
+ # parsing and rendering between external representations and internal Ruby
3
+ # classes.
4
+ module HypertextApplicationLanguage
5
+ VERSION = '0.0.0'.freeze
6
+ end
@@ -0,0 +1,8 @@
1
+ require 'hypertext_application_language/version'
2
+
3
+ require 'hypertext_application_language/namespace_manager'
4
+ require 'hypertext_application_language/link'
5
+ require 'hypertext_application_language/representation'
6
+
7
+ require 'hypertext_application_language/hash_representation_parser'
8
+ require 'hypertext_application_language/hash_representation_renderer'
@@ -0,0 +1,30 @@
1
+ describe HypertextApplicationLanguage::Link do
2
+ let(:link) { described_class.new('rel', '/path') }
3
+
4
+ it 'initializes' do
5
+ expect(link.rel).to eq('rel')
6
+ expect(link.href).to eq('/path')
7
+
8
+ other_link = described_class.new('other_rel', '/other_path', name: 'other_name')
9
+ expect(other_link.name).to eq('other_name')
10
+ end
11
+
12
+ it 'mutates' do
13
+ link.rel = 'otherRel'
14
+ expect(link.rel).to eq('otherRel')
15
+ end
16
+
17
+ it 'freezes' do
18
+ link.freeze
19
+ expect do
20
+ link.rel = 'frozenRel'
21
+ end.to raise_error(RuntimeError)
22
+ end
23
+
24
+ it 'freezes attributes' do
25
+ link.freeze
26
+ expect do
27
+ link.rel.prepend('other_')
28
+ end.to raise_error(RuntimeError)
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ describe HypertextApplicationLanguage::NamespaceManager do
2
+ let(:manager) { described_class.new }
3
+
4
+ it 'initializes' do
5
+ expect(manager).not_to be_nil
6
+ expect(manager.namespaces).to be_empty
7
+ expect(manager.namespaces.length).to eq(0)
8
+
9
+ namespaces = manager.namespaces
10
+ namespaces['name'] = 'href'
11
+ expect(manager.namespaces.length).to eq(0)
12
+ end
13
+
14
+ it 'loads with namespaces' do
15
+ expect(manager.namespaces.length).to eq(0)
16
+ manager.with_namespace('name', 'http://localhost/' + described_class::REL)
17
+ expect(manager.namespaces['name']).to eq('http://localhost/{rel}')
18
+ expect(manager.namespaces.length).to eq(1)
19
+ end
20
+
21
+ let(:ns_manager) { manager.with_namespace('ns', 'http://localhost/{rel}/to') }
22
+
23
+ it 'answers curie for href' do
24
+ expect(ns_manager.curie('http://localhost/path/to')).to eq('ns:path')
25
+ end
26
+
27
+ it 'answers nil when no matching href' do
28
+ expect(ns_manager.curie('http://localhost:8080/to')).to be_nil
29
+ end
30
+
31
+ it 'answers href for curie' do
32
+ expect(ns_manager.href('ns:arg')).to eq('http://localhost/arg/to')
33
+ end
34
+
35
+ it 'answers nil when no matching curie' do
36
+ expect(ns_manager.href('n$:arg')).to be_nil
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ describe HypertextApplicationLanguage::Representation do
2
+ let(:representation) { described_class.new }
3
+
4
+ it 'retains links' do
5
+ expect(representation.links).to be_empty
6
+ representation.with_link(HypertextApplicationLanguage::Link::SELF_REL, 'http://localhost/rel/1')
7
+ expect(representation.links).not_to be_empty
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ # Do not forget to place `--require spec_helper` in your `.rspec` file. RSpec
2
+ # loads local command line options from this file found in the project's root
3
+ # folder.
4
+
5
+ require 'simplecov'
6
+ SimpleCov.start
7
+
8
+ require 'hypertext_application_language'
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hypertext_application_language
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Roy Ratcliffe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ name: bundler
20
+ prerelease: false
21
+ type: :development
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ name: rspec
34
+ prerelease: false
35
+ type: :development
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ name: simplecov
48
+ prerelease: false
49
+ type: :development
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: ''
56
+ email:
57
+ - roy@pioneeringsoftware.co.uk
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".yardopts"
65
+ - ChangeLog.md
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - MIT-LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - hypertext_application_language.gemspec
72
+ - lib/hypertext_application_language.rb
73
+ - lib/hypertext_application_language/hash_representation_parser.rb
74
+ - lib/hypertext_application_language/hash_representation_renderer.rb
75
+ - lib/hypertext_application_language/link.rb
76
+ - lib/hypertext_application_language/namespace_manager.rb
77
+ - lib/hypertext_application_language/representation.rb
78
+ - lib/hypertext_application_language/version.rb
79
+ - spec/hypertext_application_language/link_spec.rb
80
+ - spec/hypertext_application_language/namespace_manager_spec.rb
81
+ - spec/hypertext_application_language/representation_spec.rb
82
+ - spec/spec_helper.rb
83
+ homepage: http://stateless.co/hal_specification.html
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.8
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Hypertext Application Language
107
+ test_files: []
108
+ has_rdoc: