hypertext_application_language 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: