easy-jsonapi 1.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.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/publish-gem.yml +60 -0
  3. data/.github/workflows/rake.yml +35 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +5 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +5 -0
  9. data/Gemfile.lock +106 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +209 -0
  12. data/Rakefile +20 -0
  13. data/UsingTheRequestObject.md +74 -0
  14. data/UsingUserConfigurations.md +95 -0
  15. data/bin/bundle +114 -0
  16. data/bin/console +15 -0
  17. data/bin/htmldiff +29 -0
  18. data/bin/kramdown +29 -0
  19. data/bin/ldiff +29 -0
  20. data/bin/license_finder +29 -0
  21. data/bin/license_finder_pip.py +29 -0
  22. data/bin/maruku +29 -0
  23. data/bin/marutex +29 -0
  24. data/bin/nokogiri +29 -0
  25. data/bin/racc +29 -0
  26. data/bin/rackup +29 -0
  27. data/bin/rake +29 -0
  28. data/bin/redcarpet +29 -0
  29. data/bin/reverse_markdown +29 -0
  30. data/bin/rspec +29 -0
  31. data/bin/rubocop +29 -0
  32. data/bin/ruby-parse +29 -0
  33. data/bin/ruby-rewrite +29 -0
  34. data/bin/setup +8 -0
  35. data/bin/solargraph +29 -0
  36. data/bin/thor +29 -0
  37. data/bin/tilt +29 -0
  38. data/bin/yard +29 -0
  39. data/bin/yardoc +29 -0
  40. data/bin/yri +29 -0
  41. data/easy-jsonapi.gemspec +39 -0
  42. data/lib/easy/jsonapi.rb +12 -0
  43. data/lib/easy/jsonapi/collection.rb +144 -0
  44. data/lib/easy/jsonapi/config_manager.rb +144 -0
  45. data/lib/easy/jsonapi/config_manager/config.rb +49 -0
  46. data/lib/easy/jsonapi/document.rb +71 -0
  47. data/lib/easy/jsonapi/document/error.rb +48 -0
  48. data/lib/easy/jsonapi/document/error/error_member.rb +15 -0
  49. data/lib/easy/jsonapi/document/jsonapi.rb +26 -0
  50. data/lib/easy/jsonapi/document/jsonapi/jsonapi_member.rb +15 -0
  51. data/lib/easy/jsonapi/document/links.rb +36 -0
  52. data/lib/easy/jsonapi/document/links/link.rb +15 -0
  53. data/lib/easy/jsonapi/document/meta.rb +26 -0
  54. data/lib/easy/jsonapi/document/meta/meta_member.rb +14 -0
  55. data/lib/easy/jsonapi/document/resource.rb +56 -0
  56. data/lib/easy/jsonapi/document/resource/attributes.rb +37 -0
  57. data/lib/easy/jsonapi/document/resource/attributes/attribute.rb +29 -0
  58. data/lib/easy/jsonapi/document/resource/relationships.rb +40 -0
  59. data/lib/easy/jsonapi/document/resource/relationships/relationship.rb +50 -0
  60. data/lib/easy/jsonapi/document/resource_id.rb +28 -0
  61. data/lib/easy/jsonapi/exceptions.rb +27 -0
  62. data/lib/easy/jsonapi/exceptions/document_exceptions.rb +619 -0
  63. data/lib/easy/jsonapi/exceptions/headers_exceptions.rb +156 -0
  64. data/lib/easy/jsonapi/exceptions/naming_exceptions.rb +36 -0
  65. data/lib/easy/jsonapi/exceptions/query_params_exceptions.rb +67 -0
  66. data/lib/easy/jsonapi/exceptions/user_defined_exceptions.rb +253 -0
  67. data/lib/easy/jsonapi/field.rb +43 -0
  68. data/lib/easy/jsonapi/header_collection.rb +38 -0
  69. data/lib/easy/jsonapi/header_collection/header.rb +11 -0
  70. data/lib/easy/jsonapi/item.rb +88 -0
  71. data/lib/easy/jsonapi/middleware.rb +158 -0
  72. data/lib/easy/jsonapi/name_value_pair.rb +72 -0
  73. data/lib/easy/jsonapi/name_value_pair_collection.rb +78 -0
  74. data/lib/easy/jsonapi/parser.rb +38 -0
  75. data/lib/easy/jsonapi/parser/document_parser.rb +196 -0
  76. data/lib/easy/jsonapi/parser/headers_parser.rb +33 -0
  77. data/lib/easy/jsonapi/parser/rack_req_params_parser.rb +117 -0
  78. data/lib/easy/jsonapi/request.rb +40 -0
  79. data/lib/easy/jsonapi/request/query_param_collection.rb +56 -0
  80. data/lib/easy/jsonapi/request/query_param_collection/fields_param.rb +32 -0
  81. data/lib/easy/jsonapi/request/query_param_collection/fields_param/fieldset.rb +34 -0
  82. data/lib/easy/jsonapi/request/query_param_collection/filter_param.rb +28 -0
  83. data/lib/easy/jsonapi/request/query_param_collection/filter_param/filter.rb +34 -0
  84. data/lib/easy/jsonapi/request/query_param_collection/include_param.rb +119 -0
  85. data/lib/easy/jsonapi/request/query_param_collection/page_param.rb +55 -0
  86. data/lib/easy/jsonapi/request/query_param_collection/query_param.rb +47 -0
  87. data/lib/easy/jsonapi/request/query_param_collection/sort_param.rb +25 -0
  88. data/lib/easy/jsonapi/response.rb +22 -0
  89. data/lib/easy/jsonapi/utility.rb +158 -0
  90. data/lib/easy/jsonapi/version.rb +8 -0
  91. metadata +248 -0
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/request/query_param_collection/query_param'
4
+
5
+ module JSONAPI
6
+ class Request
7
+ class QueryParamCollection < JSONAPI::NameValuePairCollection
8
+ # Used to create a unique Fieldset JSONAPI::Request::QueryParamCollection::QueryParam
9
+ class FieldsParam < QueryParam
10
+
11
+ # @param fieldset_arr [Array<JSONAPI::Request::QueryParamCollection::FieldsParam::Fieldset>]
12
+ # The array of fieldsets found in the query string. Ex: fields[resource]=res_field1,res_field2
13
+ def initialize(fieldset_arr)
14
+ super('fields', fieldset_arr)
15
+ end
16
+
17
+ # Alias to parent #value method
18
+ # @return [Array<JSONAPI::Request::QueryParamCollection::FieldsParam::Fieldset>]
19
+ def fieldsets
20
+ value
21
+ end
22
+
23
+ # @return The the query string representation of the included fieldsets
24
+ # ex: "#{fieldset1.to_s}&{fieldset2.to_s}&..."
25
+ def to_s
26
+ JSONAPI::Utility.to_string_collection(value, delimiter: '&')
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/name_value_pair_collection'
4
+ require 'easy/jsonapi/request/query_param_collection/fields_param'
5
+ require 'easy/jsonapi/utility'
6
+
7
+ module JSONAPI
8
+ class Request
9
+ class QueryParamCollection < NameValuePairCollection
10
+ class FieldsParam < QueryParam
11
+ # Collection of fields related to specific resource objects
12
+ class Fieldset
13
+
14
+ attr_reader :resource_type, :fields
15
+
16
+ # @param field_arr [Array<JSONAPI::Field>]
17
+ # A fieldset is a collection of Resource Fields
18
+ def initialize(resource_type, field_arr = [])
19
+ @resource_type = resource_type
20
+ @fields = field_arr
21
+ end
22
+
23
+ # Represention of Fieldset as a string where fields
24
+ # are comma separated strings
25
+ def to_s
26
+ pre_string = "fields[#{@resource_type}]="
27
+ JSONAPI::Utility.to_string_collection(@fields, delimiter: ',', pre_string: pre_string)
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/request/query_param_collection/query_param'
4
+ require 'easy/jsonapi/utility'
5
+
6
+ module JSONAPI
7
+ class Request
8
+ class QueryParamCollection < JSONAPI::NameValuePairCollection
9
+
10
+ # Used to create a unique Filter JSONAPI::Request::QueryParamCollection::QueryParam
11
+ class FilterParam < QueryParam
12
+
13
+ # @param filter_arr [Array<JSONAPI::Request::QueryParamCollection::FilterParam::Filter>]
14
+ # The array of filters included in the query string. Ex: filter[articles]=(posted_date == today)
15
+ def initialize(filter_arr)
16
+ super('filters', filter_arr)
17
+ end
18
+
19
+ # Represent each filter separated by a & value
20
+ # Ex: "#{filter1.to_s}&{filter2.to_s}&..."
21
+ def to_s
22
+ JSONAPI::Utility.to_string_collection(value, delimiter: '&')
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/request/query_param_collection/filter_param'
4
+
5
+ module JSONAPI
6
+ class Request
7
+ class QueryParamCollection < NameValuePairCollection
8
+ class FilterParam < QueryParam
9
+ # Represents an individual Filtering scheme for the filter query param(s) used.
10
+ class Filter
11
+
12
+ attr_reader :resource_type, :filter
13
+
14
+ # @param resource_type [String] The type to filter
15
+ # @param filter [String] The filter algorithm
16
+ def initialize(resource_type, filter)
17
+ @resource_type = resource_type
18
+ @filter = filter
19
+ end
20
+
21
+ # @return [String] The value of the filter
22
+ def value
23
+ @filter
24
+ end
25
+
26
+ # Represent filter as an individual filter query param
27
+ def to_s
28
+ "filter[#{@resource_type}]=#{@filter}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/request/query_param_collection/query_param'
4
+ require 'easy/jsonapi/utility'
5
+
6
+ module JSONAPI
7
+ class Request
8
+ class QueryParamCollection < JSONAPI::NameValuePairCollection
9
+ # The include query param
10
+ class IncludeParam < QueryParam
11
+
12
+
13
+ # @param includes_arr [Array<String>] An array with each individual query include
14
+ # Ex: incude=author,people => ['author', 'people']
15
+ def initialize(includes_arr)
16
+ includes_hash_structure = store_includes(includes_arr)
17
+ super('includes', includes_hash_structure)
18
+ end
19
+
20
+ # to string
21
+ def to_s
22
+ "include=#{stringify_includes_hash(value)}"
23
+ end
24
+
25
+ private
26
+
27
+ # Represent include internal hash as query string
28
+ # @param includes_hash [Hash] The internal structure
29
+ def stringify_includes_hash(includes_hash)
30
+ to_return = ''
31
+ first = true
32
+ includes_hash.each do |mem_name, mem_hash|
33
+ if first
34
+ to_return += to_s_mem(mem_name, mem_hash)
35
+ first = false
36
+ else
37
+ to_return += ",#{to_s_mem(mem_name, mem_hash)}"
38
+ end
39
+ end
40
+ to_return
41
+ end
42
+
43
+ # Depending on the delimiter stringify differently.
44
+ # @param mem_name [Symbol] The name of the member to stringify
45
+ # @param mem_hash [Hash] The information about that member
46
+ def to_s_mem(mem_name, mem_hash)
47
+ if mem_hash[:relationships] == {}
48
+ mem_name.to_s
49
+ else
50
+ delimiter = mem_hash[:included] == true ? '.' : '-'
51
+ prefix = "#{mem_name}#{delimiter}"
52
+ to_return = ''
53
+ first = true
54
+ mem_hash[:relationships].each do |m_name, m_hash|
55
+ if first
56
+ to_return += "#{prefix}#{to_s_mem(m_name, m_hash)}"
57
+ first = false
58
+ else
59
+ to_return += ",#{prefix}#{to_s_mem(m_name, m_hash)}"
60
+ end
61
+ end
62
+ to_return
63
+ end
64
+ end
65
+
66
+ # Helper for #initialize
67
+ # @param includes_arr [Array<String>] The array of includes to store
68
+ def store_includes(includes_arr)
69
+ incl_hash = {}
70
+ includes_arr.each do |include_str|
71
+ include_str_arr = include_str.scan(/\w+|-|\./) # split into array (word, -, or .)
72
+ store_include(incl_hash, include_str_arr)
73
+ end
74
+ incl_hash
75
+ end
76
+
77
+ # @param loc_in_h [Hash] The location within the main hash
78
+ # @param i_arr [Array<String>] The array of include strings
79
+ def store_include(loc_in_h, i_arr)
80
+ res_name = i_arr[0].to_sym
81
+ if i_arr.length == 1
82
+ add_member(loc_in_h, res_name, included: true)
83
+ else
84
+ add_member(loc_in_h, res_name, included: res_included?(i_arr))
85
+ store_include(loc_in_h[res_name][:relationships], i_arr[2..])
86
+ end
87
+ end
88
+
89
+ # @param (see #store_include)
90
+ def res_included?(i_arr)
91
+ delim = i_arr[1]
92
+ case delim
93
+ when '.'
94
+ true
95
+ when '-'
96
+ false
97
+ else
98
+ raise 'Syntax Error in include query string query param'
99
+ end
100
+ end
101
+
102
+ # @param loc_in_h [Hash] The location within the main hash
103
+ # @param res_name [Symbol] The name of the resource
104
+ # @param included [TrueClass | FalseClass] Whether or not a resource
105
+ # is being requested or not
106
+ def add_member(loc_in_h, res_name, included:)
107
+ if loc_in_h.key?(res_name)
108
+ loc_in_h[res_name][:included] = included unless included == false
109
+ else
110
+ loc_in_h[res_name] = {
111
+ included: included,
112
+ relationships: {}
113
+ }
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/request/query_param_collection/query_param'
4
+
5
+ module JSONAPI
6
+ class Request
7
+ class QueryParamCollection < JSONAPI::NameValuePairCollection
8
+ # Used to create a unique Page JSONAPI::Request::QueryParamCollection::QueryParam
9
+ class PageParam < QueryParam
10
+
11
+ # @param offset [Integer | String] the page offset
12
+ # @param limit [Integer | String] the # of resources returned on a given page
13
+ def initialize(offset:, limit:)
14
+ super('page', { offset: offset.to_i, limit: limit.to_i })
15
+ end
16
+
17
+ # @raise [RuntimeError] Informs user to use a different method
18
+ def value
19
+ raise 'PageParam does not provide a #value method, try #offset or #limit instead'
20
+ end
21
+
22
+ # @raise [RuntimeError] Informs user to use a different method
23
+ def value=(_)
24
+ raise 'PageParam does not provide a #value= method, try #offset= or #limit= instead'
25
+ end
26
+
27
+ # @return [Integer] The page offset
28
+ def offset
29
+ @item[:value][:offset]
30
+ end
31
+
32
+ # @param new_offset [Integer | String] The new page offset number
33
+ def offset=(new_offset)
34
+ @item[:value][:offset] = new_offset.to_i
35
+ end
36
+
37
+ # @return [Integer] The # of resources returned on a given page
38
+ def limit
39
+ @item[:value][:limit]
40
+ end
41
+
42
+ # @param new_limit [Integer] The new page limit number
43
+ def limit=(new_limit)
44
+ @item[:value][:limit] = new_limit.to_i
45
+ end
46
+
47
+ # Represents the Page class in a string format
48
+ def to_s
49
+ "page[offset]=#{offset}&page[limit]=#{limit}"
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/request/query_param_collection'
4
+ require 'easy/jsonapi/name_value_pair'
5
+ require 'easy/jsonapi/exceptions/query_params_exceptions'
6
+ require 'easy/jsonapi/utility'
7
+
8
+
9
+ module JSONAPI
10
+ class Request
11
+ class QueryParamCollection < JSONAPI::NameValuePairCollection
12
+ # A generic name=value query parameter
13
+ class QueryParam < JSONAPI::NameValuePair
14
+
15
+ # @param name [String] The name of the parameter
16
+ # @param value [String | Array<String>] The value of the parameter
17
+ def initialize(name, value)
18
+ if instance_of?(QueryParam)
19
+ JSONAPI::Exceptions::QueryParamsExceptions.check_param_name(name)
20
+ end
21
+ value = value.split(',') if value.is_a? String
22
+ super(name, value)
23
+ end
24
+
25
+ # Update the query_param value, turning value into an array if it was given as a string
26
+ # @param new_value [String, Array<String>] The new value of the Parameter
27
+ def value=(new_value)
28
+ new_value = new_value.split(',') if new_value.is_a? String
29
+ super(new_value)
30
+ end
31
+
32
+ # Represents a parameter as a string
33
+ def to_s
34
+ "#{name}=#{JSONAPI::Utility.to_string_collection(value, delimiter: ',')}"
35
+ end
36
+
37
+ # @raise RuntimeError Cannot change the name of a QueryParam object
38
+ def name=(_)
39
+ raise 'Cannot change the name of QueryParam Objects'
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ # include=author,comments-likers,comments.likes
47
+ # author comments-likers comments.likes
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/request/query_param_collection/query_param'
4
+
5
+ module JSONAPI
6
+ class Request
7
+ class QueryParamCollection < JSONAPI::NameValuePairCollection
8
+ # Used to create a unique Sort JSONAPI::Request::QueryParamCollection::QueryParam
9
+ class SortParam < QueryParam
10
+
11
+ # @param res_field_arr [Array<JSONAPI::Field] The resource fields
12
+ # to sort the primary resources by.
13
+ def initialize(res_field_arr)
14
+ super('sorts', res_field_arr)
15
+ end
16
+
17
+ # Represent sort as query string
18
+ def to_s
19
+ "sort=#{JSONAPI::Utility.to_string_collection(value, delimiter: ',')}"
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/exceptions'
4
+
5
+ module JSONAPI
6
+ # Used to validate the serialized response before returned to a client
7
+ module Response
8
+ # TODO: Add config_manager options for out bound serialization validation
9
+
10
+ # @param headers [Hash] The hash of response headers.
11
+ # @param body [Hash | String] The ruby hash mimicking jsonapi or
12
+ # a JSON document to check for compliance
13
+ # @return [Nilclass] if no errors are found
14
+ # @raise [InvalidDocument | InvalidHeader] depending on what errors were found
15
+ def self.validate(headers, body)
16
+ # TODO: Spec checks based on collections which can be refered from the location header
17
+ # returned by the server
18
+ JSONAPI::Exceptions::HeadersExceptions.check_compliance(headers.transform_keys(&:to_s).transform_keys(&:upcase))
19
+ JSONAPI::Exceptions::DocumentExceptions.check_compliance(body)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONAPI
4
+
5
+ # A collection of resued logic throughout the gem.
6
+ module Utility
7
+
8
+ class << self
9
+
10
+ # To hash method for a JSONAPI collection (like Attributes)
11
+ # @param collection [Inumerable] The collection to hashify
12
+ def to_h_collection(collection)
13
+ to_return = {}
14
+ collection.each do |mem|
15
+ h_val = JSONAPI::Utility.to_h_value(mem)
16
+ to_return[mem.name.to_sym] = h_val
17
+ end
18
+ to_return
19
+ end
20
+
21
+ # Helper method for #to_h_collection
22
+ # @param val [Any] The value to call to hash on.
23
+ # @return a hash value member
24
+ def to_h_value(val)
25
+ case val
26
+ when String
27
+ val
28
+ when JSONAPI::Collection
29
+ obj_hash = {}
30
+ val.each { |i| obj_hash[i.name.to_sym] = to_h_value(i) }
31
+ obj_hash
32
+ else
33
+ v = val.value
34
+ case v
35
+ when String
36
+ to_h_value(v)
37
+ when Array
38
+ v.map { |e| to_h_value(e) }
39
+ else
40
+ v.to_h
41
+ end
42
+ end
43
+ end
44
+
45
+ # Helper for #to_h for classes that arent collection and have a set number
46
+ # of instance variables to hashify
47
+ # @param hash_to_add_to [Hash] The hash to return
48
+ # @param obj_member [Any] The instance variable to hashify
49
+ # @param obj_member_name [Symbol] The hash key to store the object under
50
+ # @return [Hash] The hash for a specific instance variable
51
+ def to_h_member(hash_to_add_to, obj_member, obj_member_name)
52
+ return if obj_member.nil?
53
+ case obj_member
54
+ when String
55
+ hash_to_add_to[obj_member_name] = obj_member
56
+ when Array
57
+ hash_arr = obj_member.map(&:to_h)
58
+ hash_to_add_to[obj_member_name] = hash_arr
59
+ else
60
+ hash_to_add_to[obj_member_name] = obj_member.to_h
61
+ end
62
+ end
63
+
64
+ # Helper method for #to_s that stringifys a collection
65
+ # @param collection [Inumerable] An array type of collection
66
+ # @param delimiter [String] The delimieter to separate each item string
67
+ # @param pre_string [String] The string to precede the collection string
68
+ # @param post_string [String] The string to follow the collection
69
+ def to_string_collection(collection, delimiter: ', ', pre_string: '', post_string: '')
70
+ to_return = pre_string
71
+ first = true
72
+ collection.each do |item|
73
+ if first
74
+ to_return += item.to_s
75
+ first = false
76
+ else
77
+ to_return += "#{delimiter}#{item}"
78
+ end
79
+ end
80
+ to_return += post_string
81
+ end
82
+
83
+ # Helper for #to_s where collections are hashes and members should not
84
+ # be included if they are nil. It also accounts for arrays.
85
+ # @param str_name [String | Symbol] The name of hash member
86
+ # @param member [Any] The value of the hash member
87
+ # @param first_member [TrueClass | FalseClass] Whether or not this is the
88
+ # first member in the hash.
89
+ def member_to_s(str_name, member, first_member: false)
90
+ return '' if member.nil?
91
+ member = "\"#{member}\"" if member.is_a? String
92
+ if first_member
93
+ "\"#{str_name}\": #{array_to_s(member)}"
94
+ else
95
+ ", \"#{str_name}\": #{array_to_s(member)}"
96
+ end
97
+ end
98
+
99
+ # Returns the proper to_s for members regardless of
100
+ # whether they are stored as an array or member object
101
+ def array_to_s(obj_arr)
102
+ return obj_arr.to_s unless obj_arr.is_a? Array
103
+ to_return = '['
104
+ first = true
105
+ obj_arr.each do |obj|
106
+ if first
107
+ to_return += obj.to_s
108
+ first = false
109
+ else
110
+ to_return += ", #{obj}"
111
+ end
112
+ end
113
+ to_return += ']'
114
+ end
115
+
116
+ # Get resource type from path
117
+ # @param path [String] The url path
118
+ # @return [String] The resource type
119
+ def path_to_res_type(path)
120
+ path_arr = path.split('/')
121
+ if integer?(path_arr[-1]) || uuid?(path_arr[-1])
122
+ path_arr[-2]
123
+ else
124
+ path_arr[-1]
125
+ end
126
+ end
127
+
128
+ # Check if an input is an integer
129
+ # @param str [String | Integer]
130
+ def integer?(str)
131
+ # integers cannot be valid symbols, so assume string or integer input
132
+ return true if str.is_a?(Integer) ||
133
+ str =~ /\A\d+\z/
134
+ false
135
+ end
136
+
137
+ # Use regix to test whether the input is a valid gen 4 uuid.
138
+ # @param uuid [String]
139
+ # @return [TrueClass | FalseClass]
140
+ def uuid?(uuid)
141
+ # uuids cannot be valid symbols, so assume string
142
+ return true if uuid =~ /\A[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\z/i ||
143
+ uuid =~ /\A[\dA-F]{8}-[\dA-F]{4}-[\dA-F]{4}-[\dA-F]{4}-[\dA-F]{12}\z/i
144
+ false
145
+ end
146
+
147
+ # The hash method #dig throws an error if an array appears in the path specified,
148
+ # this method returns false for such a senario.
149
+ # @param hash [Hash] The hash being inspected
150
+ # @param args [Array<Symbol | String>] The hash keys making up the path
151
+ def all_hash_path?(hash, args)
152
+ return false if (args.size.positive? && !hash.is_a?(Hash)) || hash.nil?
153
+ return true if args.size.zero? && !hash.nil?
154
+ all_hash_path?(hash[args.first], args[1..])
155
+ end
156
+ end
157
+ end
158
+ end