sequencescape-client-api 0.3.10 → 0.4.0.pre.rc1
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 +4 -4
- data/.rubocop.yml +44 -0
- data/.rubocop_todo.yml +338 -0
- data/.ruby-version +1 -0
- data/Gemfile +1 -1
- data/README.markdown +13 -0
- data/lib/sequencescape-api.rb +1 -1
- data/lib/sequencescape-api/actions.rb +9 -8
- data/lib/sequencescape-api/associations.rb +8 -6
- data/lib/sequencescape-api/associations/base.rb +1 -1
- data/lib/sequencescape-api/associations/base/instance_methods.rb +10 -8
- data/lib/sequencescape-api/associations/belongs_to.rb +4 -4
- data/lib/sequencescape-api/associations/has_many.rb +14 -13
- data/lib/sequencescape-api/associations/has_many/json.rb +1 -1
- data/lib/sequencescape-api/associations/has_many/validation.rb +1 -1
- data/lib/sequencescape-api/composition.rb +11 -7
- data/lib/sequencescape-api/connection_factory.rb +6 -5
- data/lib/sequencescape-api/connection_factory/actions.rb +17 -12
- data/lib/sequencescape-api/core.rb +12 -7
- data/lib/sequencescape-api/core_ext/array.rb +1 -1
- data/lib/sequencescape-api/core_ext/hash.rb +3 -3
- data/lib/sequencescape-api/errors.rb +2 -2
- data/lib/sequencescape-api/finder_methods.rb +16 -11
- data/lib/sequencescape-api/rails.rb +8 -7
- data/lib/sequencescape-api/resource/active_model.rb +1 -1
- data/lib/sequencescape-api/resource/attribute_groups.rb +11 -7
- data/lib/sequencescape-api/resource/attributes.rb +8 -8
- data/lib/sequencescape-api/resource/instance_methods.rb +17 -9
- data/lib/sequencescape-api/resource/json.rb +18 -15
- data/lib/sequencescape-api/resource/modifications.rb +20 -11
- data/lib/sequencescape-api/resource_model_proxy.rb +7 -5
- data/lib/sequencescape-api/version.rb +1 -1
- data/lib/sequencescape.rb +1 -2
- data/lib/sequencescape/bait_library.rb +1 -1
- data/lib/sequencescape/bait_library_layout.rb +2 -2
- data/lib/sequencescape/barcoded_asset.rb +1 -1
- data/lib/sequencescape/batch.rb +6 -6
- data/lib/sequencescape/behaviour/qced.rb +3 -4
- data/lib/sequencescape/behaviour/receptacle.rb +3 -3
- data/lib/sequencescape/behaviour/state_driven.rb +4 -4
- data/lib/sequencescape/bulk_transfer.rb +1 -1
- data/lib/sequencescape/comment.rb +0 -2
- data/lib/sequencescape/extraction_attribute.rb +1 -1
- data/lib/sequencescape/library_event.rb +1 -1
- data/lib/sequencescape/library_tube.rb +1 -1
- data/lib/sequencescape/lot.rb +0 -1
- data/lib/sequencescape/lot_type.rb +1 -2
- data/lib/sequencescape/order_template.rb +1 -2
- data/lib/sequencescape/pipeline.rb +1 -1
- data/lib/sequencescape/plate.rb +19 -15
- data/lib/sequencescape/plate/pooling.rb +9 -3
- data/lib/sequencescape/plate/well_structure.rb +6 -6
- data/lib/sequencescape/plate_conversion.rb +3 -3
- data/lib/sequencescape/plate_creation.rb +3 -3
- data/lib/sequencescape/plate_purpose.rb +3 -3
- data/lib/sequencescape/plate_template.rb +0 -2
- data/lib/sequencescape/pooled_plate_creation.rb +3 -3
- data/lib/sequencescape/project.rb +2 -1
- data/lib/sequencescape/qc_decision.rb +0 -1
- data/lib/sequencescape/qc_file.rb +0 -3
- data/lib/sequencescape/qcable.rb +0 -3
- data/lib/sequencescape/qcable_creator.rb +0 -3
- data/lib/sequencescape/request.rb +3 -3
- data/lib/sequencescape/search.rb +10 -8
- data/lib/sequencescape/specific_tube_creation.rb +2 -2
- data/lib/sequencescape/stamp.rb +0 -1
- data/lib/sequencescape/state_change.rb +2 -2
- data/lib/sequencescape/study.rb +1 -1
- data/lib/sequencescape/submission.rb +1 -2
- data/lib/sequencescape/tag2_layout.rb +3 -3
- data/lib/sequencescape/tag2_layout_template.rb +2 -2
- data/lib/sequencescape/tag_group.rb +0 -1
- data/lib/sequencescape/tag_layout.rb +3 -3
- data/lib/sequencescape/tag_layout_template.rb +4 -4
- data/lib/sequencescape/transfer.rb +2 -2
- data/lib/sequencescape/transfer_request.rb +4 -5
- data/lib/sequencescape/transfer_template.rb +2 -2
- data/lib/sequencescape/tube.rb +3 -3
- data/lib/sequencescape/tube_creation.rb +3 -3
- data/lib/sequencescape/tube_from_tube_creation.rb +3 -3
- data/lib/sequencescape/tube_purpose.rb +3 -3
- data/lib/sequencescape/user.rb +2 -2
- data/lib/sequencescape/volume_update.rb +1 -1
- data/lib/sequencescape/well.rb +0 -2
- data/lib/sequencescape/work_completion.rb +1 -1
- data/sequencescape-api.gemspec +18 -17
- data/spec/sequencescape-api/associations_spec.rb +4 -2
- data/spec/sequencescape-api/finding_methods_spec.rb +3 -1
- data/spec/sequencescape-api/modifications_spec.rb +17 -16
- data/spec/sequencescape-api/root_spec.rb +11 -6
- data/spec/spec_helper.rb +3 -1
- data/spec/support/contract_helper.rb +18 -10
- data/spec/support/namespaces.rb +9 -9
- data/spec/support/shared_examples.rb +2 -0
- metadata +53 -35
- data/.rvmrc +0 -52
|
@@ -9,12 +9,12 @@ module Sequencescape::Api::Associations
|
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def association_methods(association, type, proxy)
|
|
13
|
-
proxy_class_name = [
|
|
12
|
+
def association_methods(association, type, proxy) # rubocop:todo Metrics/MethodLength
|
|
13
|
+
proxy_class_name = [association, type, 'proxy'].join('_').classify
|
|
14
14
|
const_set(proxy_class_name.to_sym, proxy)
|
|
15
15
|
|
|
16
16
|
line = __LINE__ + 1
|
|
17
|
-
class_eval(%
|
|
17
|
+
class_eval(%{
|
|
18
18
|
def #{association}(reload = false)
|
|
19
19
|
associations[#{association.inspect}] = nil if !!reload
|
|
20
20
|
associations[#{association.inspect}] ||= #{proxy_class_name}.new(self)
|
|
@@ -40,7 +40,8 @@ module Sequencescape::Api::Associations
|
|
|
40
40
|
|
|
41
41
|
module InstanceMethods
|
|
42
42
|
def initialize(*args, &block)
|
|
43
|
-
@associations
|
|
43
|
+
@associations = {}
|
|
44
|
+
@errors = nil
|
|
44
45
|
super
|
|
45
46
|
end
|
|
46
47
|
|
|
@@ -61,12 +62,13 @@ module Sequencescape::Api::Associations
|
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
def attributes_from_path(path, default_value_if_missing = nil)
|
|
64
|
-
path.to_s.split('.').inject(attributes) { |k,v| k.try(:[], v) } || default_value_if_missing
|
|
65
|
+
path.to_s.split('.').inject(attributes) { |k, v| k.try(:[], v) } || default_value_if_missing
|
|
65
66
|
end
|
|
66
67
|
private :attributes_from_path
|
|
67
68
|
|
|
68
69
|
def run_validations!
|
|
69
|
-
our_result
|
|
70
|
+
our_result = super
|
|
71
|
+
their_result = @associations.values.all?(&:run_validations!)
|
|
70
72
|
our_result and their_result
|
|
71
73
|
end
|
|
72
74
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
module Sequencescape::Api::Associations::Base::InstanceMethods
|
|
2
|
-
def self.included(base)
|
|
2
|
+
def self.included(base) # rubocop:todo Metrics/MethodLength
|
|
3
3
|
base.class_eval do
|
|
4
4
|
class_attribute :association, :options
|
|
5
|
-
class_attribute :default_attributes_if_missing, :
|
|
5
|
+
class_attribute :default_attributes_if_missing, instance_writer: false
|
|
6
6
|
|
|
7
7
|
attr_reader :model
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
delegate :read_timeout, to: :@owner
|
|
9
10
|
private :model
|
|
10
11
|
|
|
11
12
|
def api(*args, &block)
|
|
@@ -17,9 +18,9 @@ module Sequencescape::Api::Associations::Base::InstanceMethods
|
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def initialize(owner, json = nil)
|
|
20
|
-
@owner
|
|
21
|
-
@
|
|
22
|
-
@model
|
|
21
|
+
@owner = owner
|
|
22
|
+
@_attributes_ = json.nil? ? owner.attributes_for(association, default_attributes_if_missing) : attributes_from(json)
|
|
23
|
+
@model = api.model(options[:class_name] || association)
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def new(*args, &block)
|
|
@@ -38,8 +39,9 @@ module Sequencescape::Api::Associations::Base::InstanceMethods
|
|
|
38
39
|
when json.is_a?(String) then { uuid: json, uuid_only: true }
|
|
39
40
|
when json.is_a?(Hash) then json
|
|
40
41
|
when json.respond_to?(:map) then json.map(&method(:attributes_from))
|
|
41
|
-
when json.is_a?(Sequencescape::Api::Resource) then json.as_json(:
|
|
42
|
-
|
|
42
|
+
when json.is_a?(Sequencescape::Api::Resource) then json.as_json(force: true, action: :update,
|
|
43
|
+
root: false, uuid: true)
|
|
44
|
+
when json.is_a?(Sequencescape::Api::Associations::Base) then json.as_json(force: true, action: :update)
|
|
43
45
|
else raise json.inspect
|
|
44
46
|
end
|
|
45
47
|
end
|
|
@@ -11,7 +11,7 @@ module Sequencescape::Api::Associations::BelongsTo
|
|
|
11
11
|
|
|
12
12
|
def initialize(*args, &block)
|
|
13
13
|
super
|
|
14
|
-
@object = new(@
|
|
14
|
+
@object = new(@_attributes_, false)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def update_from_json(json)
|
|
@@ -50,7 +50,7 @@ module Sequencescape::Api::Associations::BelongsTo
|
|
|
50
50
|
@owner = owner
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
delegate :loaded, :
|
|
53
|
+
delegate :loaded, to: :@owner
|
|
54
54
|
private :loaded
|
|
55
55
|
|
|
56
56
|
def new(*args, &block)
|
|
@@ -71,7 +71,7 @@ module Sequencescape::Api::Associations::BelongsTo
|
|
|
71
71
|
private :object
|
|
72
72
|
|
|
73
73
|
def as_json(options = nil)
|
|
74
|
-
@object.as_json({ :
|
|
74
|
+
@object.as_json({ root: false, uuid: false }.reverse_merge(options || {}))
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
@@ -109,7 +109,7 @@ module Sequencescape::Api::Associations::BelongsTo
|
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
-
def belongs_to(association, options = {}, &block)
|
|
112
|
+
def belongs_to(association, options = {}, &block) # rubocop:todo Metrics/MethodLength
|
|
113
113
|
association = association.to_sym
|
|
114
114
|
|
|
115
115
|
proxy = Class.new(
|
|
@@ -15,12 +15,14 @@ module Sequencescape::Api::Associations::HasMany
|
|
|
15
15
|
include Enumerable
|
|
16
16
|
|
|
17
17
|
def size
|
|
18
|
-
return @
|
|
18
|
+
return @_attributes_['size'] if api.capabilities.size_in_pages?
|
|
19
|
+
|
|
19
20
|
all.size
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def empty?
|
|
23
|
-
return @
|
|
24
|
+
return @_attributes_['size'].zero? if api.capabilities.size_in_pages?
|
|
25
|
+
|
|
24
26
|
all.empty?
|
|
25
27
|
end
|
|
26
28
|
|
|
@@ -30,16 +32,15 @@ module Sequencescape::Api::Associations::HasMany
|
|
|
30
32
|
|
|
31
33
|
def initialize(owner, json = nil)
|
|
32
34
|
super
|
|
33
|
-
@cached_all = case
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
end
|
|
35
|
+
@cached_all = case json
|
|
36
|
+
when Array then json.map { |js| new_from(js) }
|
|
37
|
+
end
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
def new_from(json)
|
|
40
|
-
case
|
|
41
|
-
when
|
|
42
|
-
when
|
|
41
|
+
case json
|
|
42
|
+
when String then new(uuid: json) # We've recieved an array of strings, prob. uuids
|
|
43
|
+
when Hash then new(json)
|
|
43
44
|
else json
|
|
44
45
|
end
|
|
45
46
|
end
|
|
@@ -60,8 +61,8 @@ module Sequencescape::Api::Associations::HasMany
|
|
|
60
61
|
super
|
|
61
62
|
@objects =
|
|
62
63
|
case
|
|
63
|
-
when @
|
|
64
|
-
when @
|
|
64
|
+
when @_attributes_.is_a?(Array) then @_attributes_.map(&method(:new))
|
|
65
|
+
when @_attributes_.is_a?(Hash) then @_attributes_.map { |uuid, json| new(json.merge('uuid' => uuid)) }
|
|
65
66
|
else raise StandardError, "Cannot handle has_many JSON: #{json.inspect}"
|
|
66
67
|
end
|
|
67
68
|
end
|
|
@@ -90,7 +91,7 @@ module Sequencescape::Api::Associations::HasMany
|
|
|
90
91
|
@objects
|
|
91
92
|
end
|
|
92
93
|
|
|
93
|
-
def each_page
|
|
94
|
+
def each_page
|
|
94
95
|
yield(@objects)
|
|
95
96
|
end
|
|
96
97
|
|
|
@@ -105,7 +106,7 @@ module Sequencescape::Api::Associations::HasMany
|
|
|
105
106
|
end
|
|
106
107
|
end
|
|
107
108
|
|
|
108
|
-
def has_many(association, options = {}, &block)
|
|
109
|
+
def has_many(association, options = {}, &block) # rubocop:todo Metrics/MethodLength
|
|
109
110
|
association = association.to_sym
|
|
110
111
|
|
|
111
112
|
proxy = Class.new(
|
|
@@ -4,7 +4,7 @@ module Sequencescape::Api::Associations::HasMany::Json
|
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
def as_json(options = nil)
|
|
7
|
-
options = { :
|
|
7
|
+
options = { root: false, uuid: true }.reverse_merge(options || {})
|
|
8
8
|
all.map { |o| o.as_json(options) }.compact
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -11,26 +11,30 @@ module Sequencescape::Api::Composition
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def initialize(owner, attributes)
|
|
14
|
-
@owner
|
|
15
|
-
|
|
14
|
+
@owner = owner
|
|
15
|
+
@_attributes_ = attributes
|
|
16
|
+
attributes.each { |k, v| send(:"#{k}=", v) }
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def attributes
|
|
22
|
+
@_attributes_
|
|
23
|
+
end
|
|
20
24
|
end
|
|
21
25
|
|
|
22
|
-
def composed_of(name, options = {})
|
|
26
|
+
def composed_of(name, options = {}) # rubocop:todo Metrics/MethodLength
|
|
23
27
|
composed_class_name = options[:class_name] || name
|
|
24
28
|
|
|
25
29
|
line = __LINE__ + 1
|
|
26
|
-
class_eval(%
|
|
30
|
+
class_eval(%{
|
|
27
31
|
def #{name}
|
|
28
32
|
return nil unless attributes_for?(#{name.to_s.inspect})
|
|
29
33
|
api.model(#{composed_class_name.inspect}).new(self, attributes_for(#{name.to_s.inspect}))
|
|
30
34
|
end
|
|
31
35
|
|
|
32
36
|
def #{name}=(attributes)
|
|
33
|
-
@
|
|
37
|
+
@_attributes_[#{name.to_s.inspect}] = attributes
|
|
34
38
|
end
|
|
35
39
|
}, __FILE__, line)
|
|
36
40
|
end
|
|
@@ -8,14 +8,14 @@ class Sequencescape::Api::ConnectionFactory
|
|
|
8
8
|
def self.create(options)
|
|
9
9
|
required_options = []
|
|
10
10
|
required_options.push(:cookie) if options[:authorisation].blank?
|
|
11
|
-
required_options.push(:url) if
|
|
11
|
+
required_options.push(:url) if default_url.blank?
|
|
12
12
|
|
|
13
|
-
required_options.push(:
|
|
13
|
+
required_options.push(allow_blank: false)
|
|
14
14
|
options.required!(*required_options) do |missing|
|
|
15
15
|
raise ::Sequencescape::Api::Error, "No #{missing.or_sentence} set"
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
options[:url] ||=
|
|
18
|
+
options[:url] ||= default_url
|
|
19
19
|
new(options)
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -23,7 +23,9 @@ class Sequencescape::Api::ConnectionFactory
|
|
|
23
23
|
private :url, :cookie, :read_timeout
|
|
24
24
|
|
|
25
25
|
def initialize(options)
|
|
26
|
-
@url
|
|
26
|
+
@url = options[:url]
|
|
27
|
+
@cookie = options[:cookie]
|
|
28
|
+
@authorisation = options[:authorisation]
|
|
27
29
|
@read_timeout = options[:read_timeout] || 120
|
|
28
30
|
end
|
|
29
31
|
private_class_method :initialize
|
|
@@ -37,4 +39,3 @@ class Sequencescape::Api::ConnectionFactory
|
|
|
37
39
|
|
|
38
40
|
include Actions
|
|
39
41
|
end
|
|
40
|
-
|
|
@@ -8,7 +8,7 @@ BEGIN {
|
|
|
8
8
|
Net::HTTP.module_eval do
|
|
9
9
|
alias_method '__initialize__', 'initialize'
|
|
10
10
|
|
|
11
|
-
def initialize(*args
|
|
11
|
+
def initialize(*args, &block)
|
|
12
12
|
__initialize__(*args, &block)
|
|
13
13
|
ensure
|
|
14
14
|
@debug_output = $stderr if ENV['HTTP_DEBUG']
|
|
@@ -47,8 +47,9 @@ module Sequencescape::Api::ConnectionFactory::Actions
|
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
# rubocop:todo Metrics/MethodLength
|
|
51
|
+
def create(url, body, handler) # rubocop:todo Metrics/CyclomaticComplexity
|
|
52
|
+
perform(:post, url, jsonify(body, action: :create)) do |response|
|
|
52
53
|
case response
|
|
53
54
|
when Net::HTTPCreated then handler.success(parse_json_from(response))
|
|
54
55
|
when Net::HTTPSuccess then handler.success(parse_json_from(response)) # TODO: should be error!
|
|
@@ -60,8 +61,10 @@ module Sequencescape::Api::ConnectionFactory::Actions
|
|
|
60
61
|
end
|
|
61
62
|
end
|
|
62
63
|
end
|
|
64
|
+
# rubocop:enable Metrics/MethodLength
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
# rubocop:todo Metrics/MethodLength
|
|
67
|
+
def create_from_file(url, file, filename, content_type, handler) # rubocop:todo Metrics/CyclomaticComplexity
|
|
65
68
|
perform_for_file(:post, url, file, filename, content_type) do |response|
|
|
66
69
|
case response
|
|
67
70
|
when Net::HTTPCreated then handler.success(parse_json_from(response))
|
|
@@ -74,9 +77,10 @@ module Sequencescape::Api::ConnectionFactory::Actions
|
|
|
74
77
|
end
|
|
75
78
|
end
|
|
76
79
|
end
|
|
80
|
+
# rubocop:enable Metrics/MethodLength
|
|
77
81
|
|
|
78
82
|
def update(url, body, handler)
|
|
79
|
-
perform(:put, url, jsonify(body, :
|
|
83
|
+
perform(:put, url, jsonify(body, action: :update)) do |response|
|
|
80
84
|
case response
|
|
81
85
|
when Net::HTTPSuccess then handler.success(parse_json_from(response))
|
|
82
86
|
when Net::HTTPUnauthorized then handler.unauthenticated(parse_json_from(response))
|
|
@@ -95,7 +99,7 @@ module Sequencescape::Api::ConnectionFactory::Actions
|
|
|
95
99
|
end
|
|
96
100
|
private :handle_redirect
|
|
97
101
|
|
|
98
|
-
def perform(http_verb, url, body = nil, accepts = nil
|
|
102
|
+
def perform(http_verb, url, body = nil, accepts = nil) # rubocop:todo Metrics/MethodLength
|
|
99
103
|
begin
|
|
100
104
|
uri = URI.parse(url)
|
|
101
105
|
rescue URI::InvalidURIError => e
|
|
@@ -103,12 +107,12 @@ module Sequencescape::Api::ConnectionFactory::Actions
|
|
|
103
107
|
end
|
|
104
108
|
Net::HTTP.start(uri.host, uri.port) do |connection|
|
|
105
109
|
connection.read_timeout = read_timeout
|
|
106
|
-
request_headers =
|
|
110
|
+
request_headers = headers
|
|
107
111
|
request_headers.merge!('Accept' => accepts) unless accepts.nil?
|
|
108
112
|
request = Net::HTTP.const_get(http_verb.to_s.classify).new(uri.request_uri, request_headers)
|
|
109
113
|
unless body.nil?
|
|
110
114
|
request.content_type = 'application/json'
|
|
111
|
-
#request.body = body.to_json
|
|
115
|
+
# request.body = body.to_json
|
|
112
116
|
request.body = Yajl::Encoder.encode(body)
|
|
113
117
|
end
|
|
114
118
|
yield(connection.request(request))
|
|
@@ -116,14 +120,14 @@ module Sequencescape::Api::ConnectionFactory::Actions
|
|
|
116
120
|
end
|
|
117
121
|
private :perform
|
|
118
122
|
|
|
119
|
-
def perform_for_file(http_verb, url, file, filename, content_type
|
|
123
|
+
def perform_for_file(http_verb, url, file, filename, content_type)
|
|
120
124
|
uri = URI.parse(url)
|
|
121
125
|
Net::HTTP.start(uri.host, uri.port) do |connection|
|
|
122
126
|
connection.read_timeout = read_timeout
|
|
123
|
-
file_headers = headers.merge!({'Content-Disposition'=> "form-data; filename=\"#{filename}\""})
|
|
127
|
+
file_headers = headers.merge!({ 'Content-Disposition' => "form-data; filename=\"#{filename}\"" })
|
|
124
128
|
request = Net::HTTP.const_get(http_verb.to_s.classify).new(uri.request_uri, file_headers)
|
|
125
129
|
request.content_type = content_type
|
|
126
|
-
#request.body = body.to_json
|
|
130
|
+
# request.body = body.to_json
|
|
127
131
|
request.body = file.read
|
|
128
132
|
yield(connection.request(request))
|
|
129
133
|
end
|
|
@@ -134,13 +138,14 @@ module Sequencescape::Api::ConnectionFactory::Actions
|
|
|
134
138
|
case
|
|
135
139
|
when body.nil? then {}
|
|
136
140
|
when body.is_a?(Hash) then body
|
|
137
|
-
else body.as_json(options.merge(:
|
|
141
|
+
else body.as_json(options.merge(root: true))
|
|
138
142
|
end
|
|
139
143
|
end
|
|
140
144
|
private :jsonify
|
|
141
145
|
|
|
142
146
|
def parse_json_from(response)
|
|
143
147
|
raise ServerError, 'server returned non-JSON content' unless response.content_type == 'application/json'
|
|
148
|
+
|
|
144
149
|
Yajl::Parser.parse(StringIO.new(response.body))
|
|
145
150
|
end
|
|
146
151
|
private :parse_json_from
|
|
@@ -10,7 +10,8 @@ class Sequencescape::Api
|
|
|
10
10
|
extend Sequencescape::Api::ConnectionFactory::Helpers
|
|
11
11
|
|
|
12
12
|
def initialize(options = {})
|
|
13
|
-
@models
|
|
13
|
+
@models = {}
|
|
14
|
+
@model_namespace = options.delete(:namespace) || Sequencescape
|
|
14
15
|
@model_namespace = @model_namespace.constantize if @model_namespace.is_a?(String)
|
|
15
16
|
@connection = self.class.connection_factory.create(options).tap do |connection|
|
|
16
17
|
connection.root(self)
|
|
@@ -18,7 +19,8 @@ class Sequencescape::Api
|
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
attr_reader :capabilities
|
|
21
|
-
|
|
22
|
+
|
|
23
|
+
delegate :read, :create, :create_from_file, :update, :retrieve, to: :@connection
|
|
22
24
|
|
|
23
25
|
def read_uuid(uuid, handler)
|
|
24
26
|
read(@connection.url_for_uuid(uuid), handler)
|
|
@@ -30,22 +32,25 @@ class Sequencescape::Api
|
|
|
30
32
|
|
|
31
33
|
def method_missing(name, *args, &block)
|
|
32
34
|
return super unless @models.keys.include?(name.to_s)
|
|
35
|
+
|
|
33
36
|
ResourceModelProxy.new(self, model(name), @models[name.to_s])
|
|
34
37
|
end
|
|
35
38
|
protected :method_missing
|
|
36
39
|
|
|
37
|
-
def model(name)
|
|
40
|
+
def model(name) # rubocop:todo Metrics/MethodLength
|
|
38
41
|
parts = name.to_s.split('::').map(&:classify)
|
|
39
42
|
raise StandardError, "#{name.inspect} is rooted and that is not supported" if parts.first.blank?
|
|
43
|
+
|
|
40
44
|
parts.inject(@model_namespace) { |context, part| context.const_get(part) }
|
|
41
|
-
rescue NameError =>
|
|
45
|
+
rescue NameError => e
|
|
42
46
|
raise if @model_namespace == ::Sequencescape
|
|
43
|
-
|
|
47
|
+
|
|
48
|
+
parts.inject([::Sequencescape, @model_namespace]) do |(source, dest), part|
|
|
44
49
|
const_from_source = source.const_get(part)
|
|
45
50
|
if dest.const_defined?(part)
|
|
46
|
-
[
|
|
51
|
+
[const_from_source, dest.const_get(part)]
|
|
47
52
|
else
|
|
48
|
-
[
|
|
53
|
+
[const_from_source, dest.const_set(part, const_from_source)]
|
|
49
54
|
end
|
|
50
55
|
end.last
|
|
51
56
|
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
class Hash
|
|
2
2
|
# Yields all of the missing keys if there are any so that you can do what you like, like
|
|
3
3
|
# error maybe?
|
|
4
|
-
def required!(*keys
|
|
4
|
+
def required!(*keys)
|
|
5
5
|
options = keys.extract_options!
|
|
6
6
|
return if keys.empty?
|
|
7
7
|
|
|
8
|
-
allowance_method =
|
|
8
|
+
allowance_method = options[:allow_blank] == false ? :blank? : :nil?
|
|
9
9
|
|
|
10
10
|
missing = keys.inject([]) do |missing, next_key|
|
|
11
|
-
missing.tap do
|
|
11
|
+
missing.tap do
|
|
12
12
|
value = self[next_key]
|
|
13
13
|
missing << next_key if value.send(allowance_method)
|
|
14
14
|
end
|