shaf_client 0.5.3 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92a7f2a17ed01c0b131194c0ca4516280d219d5a36a2f3afc38f4ccc314a5f0a
4
- data.tar.gz: f2c91a861378440ded31fa39bd364ad80d4ecf57408f1b51219c746883f254d8
3
+ metadata.gz: 19b9d623cc520b186b862d8604530daabf1723bc5a3b5d159dac3ecca706f1fb
4
+ data.tar.gz: 5ee331304969c4c873429914c521be63aaa849f94e5a09c94f6ba4fec06f9049
5
5
  SHA512:
6
- metadata.gz: 4595ff9d2c6bf0665a0f541f428de36b8f420543a0ad0d6c208dbdc70b7b1d5aa0369dd16e8a5f2cfa254b995aabc7f2151047ab539d24ce32a0002a319d7e21
7
- data.tar.gz: 1933d7180df83cba97c2be59b6bff59c86545151cf9caffab0a7b1e0e036cc152bcd1567c31a1a4f8f3ad0a5719139ff6c01bc09a4e47940a8c3a66925e33432
6
+ metadata.gz: 85305d86a68ad781d0608aca92c595dca9403a7a420900560fc50828af4b6a663a3731fe7de10b73efaab75e2f0430a5f52ffce1003dcb32b511380e85577358
7
+ data.tar.gz: f9fc05a4f34e6dad3f83ee583fa8329ae665bf537522a7a1e950a818e97cc869a4109a73f909000cf4ef49a90e4571f595b54b679ce74cedd15826e775dc6a29
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/lib/shaf_client.rb CHANGED
@@ -6,7 +6,8 @@ require 'json'
6
6
  require 'shaf_client/error'
7
7
  require 'shaf_client/middleware/redirect'
8
8
  require 'shaf_client/resource'
9
- require 'shaf_client/form'
9
+ require 'shaf_client/shaf_form'
10
+ require 'shaf_client/hal_form'
10
11
  require 'shaf_client/api_error'
11
12
  require 'shaf_client/empty_resource'
12
13
  require 'shaf_client/unknown_resource'
@@ -44,7 +45,7 @@ class ShafClient
44
45
  )
45
46
 
46
47
  body = String(response.body)
47
- response.headers['content-type'] = '__shaf_client_emtpy__' if body.empty?
48
+ response.headers['content-type'] = nil if body.empty?
48
49
 
49
50
  Resource.build(self, body, response.status, response.headers)
50
51
  end
@@ -21,5 +21,10 @@ class ShafClient
21
21
  @href = href
22
22
  @templated = !!templated
23
23
  end
24
+
25
+ def resolve_templated(**args)
26
+ args[:rel] &&= args[:rel].to_s.sub(/#{name}:/, '')
27
+ super(**args)
28
+ end
24
29
  end
25
30
  end
@@ -4,7 +4,7 @@ class ShafClient
4
4
  class EmptyResource < Resource
5
5
  attr_reader :http_status, :headers
6
6
 
7
- ResourceMapper.register('__shaf_client_emtpy__', self)
7
+ ResourceMapper.register(nil, self)
8
8
 
9
9
  def initialize(_client, _payload, status = nil, headers = {})
10
10
  @http_status = status
@@ -0,0 +1,75 @@
1
+ require 'json'
2
+
3
+ class ShafClient
4
+ class Field
5
+ attr_reader :name, :prompt, :required, :read_only,
6
+ :hidden, :templated, :type, :value, :regex
7
+
8
+ def initialize(
9
+ name:,
10
+ prompt: nil,
11
+ title: nil,
12
+ required: false,
13
+ read_only: false,
14
+ hidden: false,
15
+ templated: false,
16
+ type: nil,
17
+ value: nil,
18
+ regex: nil
19
+ )
20
+ @name = name.to_sym
21
+ @prompt = prompt || title || name
22
+ @required = required
23
+ @read_only = read_only
24
+ @hidden = hidden
25
+ @templated = templated
26
+ @type = type&.downcase
27
+ @value = value
28
+ @regex = parse_regexp(regex)
29
+ end
30
+
31
+ def valid?(value)
32
+ return false unless validate_required(value)
33
+ return false unless validate_number(value)
34
+ return false unless validate_string(value)
35
+ return false unless validate_regexp(value)
36
+ true
37
+ end
38
+
39
+ private
40
+
41
+ def validate_required(value)
42
+ return true unless required
43
+ return false if String(value).empty?
44
+ true
45
+ end
46
+
47
+ def validate_number(value)
48
+ return true if value.nil?
49
+ return true unless type
50
+ return true unless %w[int integer number].include? type
51
+ value.is_a? Numeric
52
+ end
53
+
54
+ def validate_string(value)
55
+ return true if value.nil?
56
+ return true unless type
57
+ return true unless %w[string text].include? type
58
+ value.is_a? String
59
+ end
60
+
61
+ def validate_regexp(value)
62
+ str = String(value)
63
+ return true if str.empty?
64
+ return true unless regex
65
+ str.match? regex
66
+ end
67
+
68
+ def parse_regexp(str)
69
+ return if String(str).empty?
70
+ Regexp.new(str)
71
+ rescue RegexpError
72
+ nil
73
+ end
74
+ end
75
+ end
@@ -1,42 +1,65 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
4
+ require 'uri'
2
5
  require 'shaf_client/link'
6
+ require 'shaf_client/field'
3
7
 
4
8
  class ShafClient
5
9
  class Form < Resource
6
10
 
7
- profile 'shaf-form'
8
-
9
11
  def values
10
12
  return @values if defined? @values
11
13
 
12
- @values = attribute(:fields).each_with_object({}) do |d, v|
13
- v[d['name'].to_sym] = d['value']
14
+ @values = fields.each_with_object({}) do |field, values|
15
+ values[field.name] = field.value
14
16
  end
15
17
  end
16
18
 
17
19
  def [](key)
18
- values[key.to_sym]
20
+ values.fetch(key.to_sym)
19
21
  end
20
22
 
21
23
  def []=(key, value)
24
+ values.fetch(key.to_sym) # Raise KeyError unless key exist!
22
25
  values[key.to_sym] = value
23
26
  end
24
27
 
28
+ def title
29
+ raise NotImplementedError
30
+ end
31
+
25
32
  def target
26
- attribute(:href)
33
+ raise NotImplementedError
27
34
  end
28
35
 
29
36
  def http_method
30
- attribute(:method).downcase.to_sym
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def content_type
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def fields
45
+ raise NotImplementedError
31
46
  end
32
47
 
33
48
  def submit
34
- client.send(http_method, target, payload: @values)
49
+ client.send(
50
+ http_method,
51
+ target,
52
+ payload: encoded_payload,
53
+ headers: {'Content-Type' => content_type}
54
+ )
35
55
  end
36
56
 
37
57
  def valid?
38
- attribute(:fields).all? do |field|
39
- valid_field? field
58
+ field_value_mapping
59
+
60
+ fields.all? do |field|
61
+ value = values[field.name.to_sym]
62
+ field.valid? value
40
63
  end
41
64
  end
42
65
 
@@ -50,32 +73,24 @@ class ShafClient
50
73
  super
51
74
  end
52
75
 
53
- def valid_field?(field)
54
- key = field['name'].to_sym
55
- return false unless validate_required(field, key)
56
- return true if values[key].nil?
57
- return false unless validate_number(field, key)
58
- return false unless validate_string(field, key)
59
- true
60
- end
61
-
62
76
  private
63
77
 
64
- def validate_required(field, key)
65
- return true unless field['required']
66
- return false if values[key].nil?
67
- return false if values[key].respond_to?(:empty) && values[key].empty?
68
- true
78
+ def encoded_payload
79
+ if content_type&.downcase == 'application/x-www-form-urlencoded'
80
+ URI.encode_www_form(values)
81
+ else
82
+ JSON.generate(values)
83
+ end
69
84
  end
70
85
 
71
- def validate_string(field, key)
72
- return true unless %w[string text].include? field.fetch('type', '').downcase
73
- values[key].is_a? String
86
+ def field_names
87
+ fields.map { |f| f.name.to_sym }
74
88
  end
75
89
 
76
- def validate_number(field, key)
77
- return true unless %w[int integer number].include? field.fetch('type', '').downcase
78
- values.fetch(key, 0).is_a? Numeric
90
+ def field_value_mapping
91
+ field_names.each_with_object({}) do |name, mapping|
92
+ mapping[name.to_sym] = values[name.to_sym]
93
+ end
79
94
  end
80
95
  end
81
96
  end
@@ -0,0 +1,39 @@
1
+ require 'shaf_client/form'
2
+
3
+ class ShafClient
4
+ class HalForm < Form
5
+ attr_accessor :target
6
+
7
+ content_type 'application/prs.hal-forms+json'
8
+
9
+ def title
10
+ template[:title]
11
+ end
12
+
13
+ def name
14
+ attribute(:name)
15
+ end
16
+
17
+ def http_method
18
+ template[:method].downcase.to_sym
19
+ end
20
+
21
+ def content_type
22
+ template[:contentType]
23
+ end
24
+
25
+ def fields
26
+ template[:properties].map do |values|
27
+ Field.new(values.transform_keys(&:to_sym))
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def template
34
+ @template ||= attributes
35
+ .dig(:_templates, 'default')
36
+ .transform_keys(&:to_sym)
37
+ end
38
+ end
39
+ end
@@ -2,8 +2,6 @@
2
2
 
3
3
  class ShafClient
4
4
  class Link
5
- attr_reader :templated
6
-
7
5
  def self.from(data)
8
6
  if data.is_a? Array
9
7
  data.map { |d| new(href: d['href'], templated: d['templated']) }
@@ -17,7 +15,9 @@ class ShafClient
17
15
  @templated = !!templated
18
16
  end
19
17
 
20
- alias templated? templated
18
+ def templated?
19
+ @templated
20
+ end
21
21
 
22
22
  def href
23
23
  @href.dup
@@ -26,10 +26,9 @@ class ShafClient
26
26
  def resolve_templated(**args)
27
27
  return href unless templated?
28
28
 
29
- args.inject(href) do |uri, (key, value)|
30
- value = value.to_s.sub(/.+:/, '')
31
- uri.sub(/{#{key}}/, value)
32
- end
29
+ href
30
+ .then { |href| resolve_required(href, **args) }
31
+ .then { |href| resolve_optional(href, **args) }
33
32
  end
34
33
 
35
34
  def to_h
@@ -38,5 +37,29 @@ class ShafClient
38
37
  templated: templated?
39
38
  }
40
39
  end
40
+
41
+ private
42
+
43
+ def resolve_required(href, **args)
44
+ String(href).gsub(/{(?!\?)([^}]*)}/) do
45
+ key = $1.to_sym
46
+ args.fetch key do
47
+ raise ArgumentError, "missing keyword: :#{key}"
48
+ end
49
+ end
50
+ end
51
+
52
+ def resolve_optional(href, **args)
53
+ String(href).gsub(/{\?([^}]*)}/) do
54
+ values = []
55
+ $1.split(',').each do |key|
56
+ next unless args.key? key.to_sym
57
+ values << "#{key}=#{args[key.to_sym]}"
58
+ end
59
+
60
+ next '' if values.empty?
61
+ "?#{values.join('&')}"
62
+ end
63
+ end
41
64
  end
42
65
  end
@@ -49,6 +49,21 @@ class ShafClient
49
49
  client.get_doc(uri)
50
50
  end
51
51
 
52
+ def get_hal_form(rel)
53
+ href = link(rel).href
54
+ uri = rel.to_s
55
+ if uri.match? %r{:[^/]}
56
+ curie_name, rel = rel.split(':')
57
+ curie = curie(curie_name)
58
+ uri = curie.resolve_templated(rel: rel)
59
+ end
60
+
61
+ headers = {'Accept': 'application/prs.hal-forms+json'}
62
+ client.get(uri, headers: headers).tap do |form|
63
+ form.target = href if form.respond_to? :target=
64
+ end
65
+ end
66
+
52
67
  def reload!
53
68
  self << get(:self, headers: {'Cache-Control': 'no-cache'})
54
69
  end
@@ -3,12 +3,8 @@ require 'shaf_client/error'
3
3
  class ShafClient
4
4
  class ResourceMapper
5
5
  class << self
6
- def all
7
- @all ||= {}
8
- end
9
-
10
6
  def for(content_type)
11
- all[content_type&.to_sym].tap do |clazz|
7
+ mapping[content_type&.to_sym].tap do |clazz|
12
8
  next if clazz
13
9
  raise UnSupportedContentType,
14
10
  "Can't handle Content-Type: #{content_type}"
@@ -16,11 +12,17 @@ class ShafClient
16
12
  end
17
13
 
18
14
  def register(content_type, clazz)
19
- all[content_type&.to_sym] = clazz
15
+ mapping[content_type&.to_sym] = clazz
20
16
  end
21
17
 
22
18
  def default=(clazz)
23
- all.default = clazz
19
+ mapping.default = clazz
20
+ end
21
+
22
+ private
23
+
24
+ def mapping
25
+ @mapping ||= {}
24
26
  end
25
27
  end
26
28
  end
@@ -0,0 +1,34 @@
1
+ require 'shaf_client/form'
2
+
3
+ class ShafClient
4
+ class ShafForm < Form
5
+
6
+ profile 'shaf-form'
7
+
8
+ def title
9
+ attribute(:title)
10
+ end
11
+
12
+ def name
13
+ attribute(:name)
14
+ end
15
+
16
+ def target
17
+ attribute(:href)
18
+ end
19
+
20
+ def http_method
21
+ attribute(:method).downcase.to_sym
22
+ end
23
+
24
+ def content_type
25
+ attribute(:type)
26
+ end
27
+
28
+ def fields
29
+ attribute(:fields).map do |values|
30
+ Field.new(values.transform_keys(&:to_sym))
31
+ end
32
+ end
33
+ end
34
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shaf_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sammy Henningsson
@@ -30,7 +30,7 @@ cert_chain:
30
30
  ZMhjYR7sRczGJx+GxGU2EaR0bjRsPVlC4ywtFxoOfRG3WaJcpWGEoAoMJX6Z0bRv
31
31
  M40=
32
32
  -----END CERTIFICATE-----
33
- date: 2019-09-17 00:00:00.000000000 Z
33
+ date: 2019-09-20 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: faraday
@@ -106,11 +106,14 @@ files:
106
106
  - lib/shaf_client/curie.rb
107
107
  - lib/shaf_client/empty_resource.rb
108
108
  - lib/shaf_client/error.rb
109
+ - lib/shaf_client/field.rb
109
110
  - lib/shaf_client/form.rb
111
+ - lib/shaf_client/hal_form.rb
110
112
  - lib/shaf_client/link.rb
111
113
  - lib/shaf_client/middleware/redirect.rb
112
114
  - lib/shaf_client/resource.rb
113
115
  - lib/shaf_client/resource_mapper.rb
116
+ - lib/shaf_client/shaf_form.rb
114
117
  - lib/shaf_client/unknown_resource.rb
115
118
  homepage: https://github.com/sammyhenningsson/shaf_client
116
119
  licenses:
metadata.gz.sig CHANGED
Binary file