axel 0.0.1
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 +15 -0
- data/.gitignore +21 -0
- data/.octopolo.yml +2 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +271 -0
- data/Rakefile +13 -0
- data/app/models/axel/api_proxy.rb +86 -0
- data/app/models/axel/associations/base.rb +64 -0
- data/app/models/axel/associations/belongs_to.rb +43 -0
- data/app/models/axel/associations/has_many.rb +29 -0
- data/app/models/axel/associations/has_one.rb +30 -0
- data/app/models/axel/payload.rb +4 -0
- data/app/models/axel/payload/base.rb +107 -0
- data/app/models/axel/payload/errors.rb +62 -0
- data/app/models/axel/payload/metadata.rb +57 -0
- data/app/models/axel/querier.rb +166 -0
- data/app/models/axel/router.rb +119 -0
- data/app/models/axel/service_resource.rb +4 -0
- data/app/models/axel/service_resource/associations.rb +80 -0
- data/app/models/axel/service_resource/attributes.rb +23 -0
- data/app/models/axel/service_resource/automatic_resource.rb +23 -0
- data/app/models/axel/service_resource/base.rb +47 -0
- data/app/models/axel/service_resource/builder.rb +40 -0
- data/app/models/axel/service_resource/inspects.rb +17 -0
- data/app/models/axel/service_resource/payload_parser.rb +46 -0
- data/app/models/axel/service_resource/queries.rb +25 -0
- data/app/models/axel/service_resource/requesters.rb +49 -0
- data/app/models/axel/service_resource/routes.rb +19 -0
- data/app/models/axel/service_resource/typhoid_extensions.rb +134 -0
- data/app/views/axel/base/empty.json.erb +0 -0
- data/app/views/axel/base/empty.xml.builder +0 -0
- data/app/views/layouts/axel.json.jbuilder +7 -0
- data/app/views/layouts/axel.xml.builder +12 -0
- data/axel.gemspec +42 -0
- data/lib/axel.rb +56 -0
- data/lib/axel/application_extensions.rb +13 -0
- data/lib/axel/application_helper.rb +27 -0
- data/lib/axel/base_controller.rb +6 -0
- data/lib/axel/cascadable_attribute.rb +33 -0
- data/lib/axel/configurations/resource.rb +29 -0
- data/lib/axel/configurations/service.rb +28 -0
- data/lib/axel/configurator.rb +54 -0
- data/lib/axel/configurators/services.rb +29 -0
- data/lib/axel/controller_base.rb +27 -0
- data/lib/axel/controller_helpers.rb +209 -0
- data/lib/axel/controller_parameters.rb +32 -0
- data/lib/axel/engine.rb +14 -0
- data/lib/axel/inspector.rb +91 -0
- data/lib/axel/payload/remote_error.rb +14 -0
- data/lib/axel/request_options.rb +26 -0
- data/lib/axel/uri.rb +47 -0
- data/lib/axel/version.rb +3 -0
- data/lib/generators/axel/install_generator.rb +16 -0
- data/lib/generators/templates/README.md +22 -0
- data/lib/generators/templates/axel.rb +81 -0
- data/script/rails +5 -0
- data/spec/controllers/pages_controller_spec.rb +217 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/pages_controller.rb +6 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +62 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitignore +1 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/envelope_integration_check.rb +96 -0
- data/spec/helpers/axel/application_helper_spec.rb +31 -0
- data/spec/lib/axel/associations/base_spec.rb +28 -0
- data/spec/lib/axel/associations/belongs_to_spec.rb +62 -0
- data/spec/lib/axel/associations/has_many_spec.rb +23 -0
- data/spec/lib/axel/associations/has_one_spec.rb +23 -0
- data/spec/lib/axel/configurations/resource_spec.rb +15 -0
- data/spec/lib/axel/configurations/service_spec.rb +31 -0
- data/spec/lib/axel/configurator_spec.rb +26 -0
- data/spec/lib/axel/configurators/services_spec.rb +37 -0
- data/spec/lib/axel/controller_base_spec.rb +16 -0
- data/spec/lib/axel/controller_parameters_spec.rb +31 -0
- data/spec/lib/axel/inspector_spec.rb +45 -0
- data/spec/lib/axel/request_options_spec.rb +50 -0
- data/spec/lib/axel/uri_spec.rb +42 -0
- data/spec/lib/axel_spec.rb +64 -0
- data/spec/models/axel/api_proxy_spec.rb +66 -0
- data/spec/models/axel/payload/errors_spec.rb +165 -0
- data/spec/models/axel/payload/metadata_spec.rb +141 -0
- data/spec/models/axel/querier_spec.rb +158 -0
- data/spec/models/axel/router_spec.rb +115 -0
- data/spec/models/axel/service_resource/base_spec.rb +244 -0
- data/spec/models/axel/service_resource/builder_spec.rb +64 -0
- data/spec/models/axel/service_resource/payload_parser_spec.rb +60 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/address.rb +5 -0
- data/spec/support/persona.rb +15 -0
- data/spec/support/user.rb +6 -0
- data/spec/views/axel/base/empty_spec.rb +34 -0
- metadata +508 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Axel
|
|
2
|
+
module Associations
|
|
3
|
+
class Base
|
|
4
|
+
attr_accessor :model
|
|
5
|
+
attr_accessor :relation_name
|
|
6
|
+
attr_accessor :options
|
|
7
|
+
|
|
8
|
+
def initialize(model, relation_name, options)
|
|
9
|
+
self.model = model
|
|
10
|
+
self.relation_name = relation_name.to_s
|
|
11
|
+
self.options = options || {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def handles_method?(method_name)
|
|
15
|
+
matchers.any? { |matcher| method_name.to_s.match(matcher) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run_method(instance, method_name, *args, &block)
|
|
19
|
+
if included?
|
|
20
|
+
included_getter(instance, *args, &block)
|
|
21
|
+
else
|
|
22
|
+
method_chooser(method_name)[instance, *args, &block]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def included?
|
|
29
|
+
!!options[:included]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def build_klass
|
|
33
|
+
options[:class] || relation_name_klass
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def premodule
|
|
37
|
+
model.name.split("::")[0..-2].join("::")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def relation_name_klass
|
|
41
|
+
"#{premodule}::#{relation_name.to_s.singularize.classify}".safe_constantize
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def matcher_map
|
|
45
|
+
{
|
|
46
|
+
/\A#{relation_name}\z/ => :getter
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def matchers
|
|
51
|
+
matcher_map.keys
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def method_chooser(method_name)
|
|
55
|
+
name = matcher_map[matchers.find { |matcher| method_name.to_s.match matcher }]
|
|
56
|
+
if name
|
|
57
|
+
method(name)
|
|
58
|
+
else
|
|
59
|
+
raise NoMethodError, "Could not find an association method for `#{method_name}'"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Axel
|
|
2
|
+
module Associations
|
|
3
|
+
class BelongsTo < Base
|
|
4
|
+
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def included_getter(instance, *args, &block)
|
|
8
|
+
relation_attributes = (instance.try(:attributes) || {})[relation_name]
|
|
9
|
+
build_klass.new(relation_attributes) if relation_attributes
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def find_nested?
|
|
13
|
+
!!options[:find_nested]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def getter(instance, *args, &block)
|
|
17
|
+
request_options = args.extract_options!
|
|
18
|
+
if find_nested?
|
|
19
|
+
build_klass
|
|
20
|
+
.querier
|
|
21
|
+
.without_default_path
|
|
22
|
+
.at_path(route_path(instance))
|
|
23
|
+
.request_options(request_options)
|
|
24
|
+
.first
|
|
25
|
+
else
|
|
26
|
+
build_klass.find(instance.public_send(association_id_method), request_options)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def route_path(instance)
|
|
31
|
+
[URI(instance.request_uri).path, association_path(instance)].join("/")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def association_path(instance) # needs to be belongs_to, some looks has_many
|
|
35
|
+
"#{relation_name.to_s.pluralize}/#{instance.public_send(association_id_method)}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def association_id_method
|
|
39
|
+
options[:id_attribute] || "#{relation_name.to_s.singularize.underscore}_id"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Axel
|
|
2
|
+
module Associations
|
|
3
|
+
class HasMany < Base
|
|
4
|
+
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def included_getter(instance, *args, &block)
|
|
8
|
+
Array(instance.attributes[relation_name]).map { |attributes| build_klass.new(attributes) }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def getter(instance, *args, &block)
|
|
12
|
+
request_options = args.extract_options!
|
|
13
|
+
build_klass
|
|
14
|
+
.querier
|
|
15
|
+
.without_default_path
|
|
16
|
+
.at_path(route_path(instance))
|
|
17
|
+
.request_options request_options
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def route_path(instance)
|
|
21
|
+
[URI(instance.request_uri).path, association_path].join("/")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def association_path # needs to be belongs_to, some looks has_many
|
|
25
|
+
options[:suffix_path].present? && options[:suffix_path] || relation_name.to_s
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Axel
|
|
2
|
+
module Associations
|
|
3
|
+
class HasOne < Base
|
|
4
|
+
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def included_getter(instance, *args, &block)
|
|
8
|
+
Array(instance.attributes[relation_name]).map { |attributes| build_klass.new(attributes) }.first
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def getter(instance, *args, &block)
|
|
12
|
+
request_options = args.extract_options!
|
|
13
|
+
build_klass
|
|
14
|
+
.querier
|
|
15
|
+
.without_default_path
|
|
16
|
+
.at_path(route_path(instance))
|
|
17
|
+
.request_options(request_options)
|
|
18
|
+
.first
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def route_path(instance)
|
|
22
|
+
[URI(instance.request_uri).path, association_path].join("/")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def association_path # needs to be belongs_to, some looks has_many
|
|
26
|
+
options[:suffix_path].present? && options[:suffix_path] || relation_name.to_s
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module Axel
|
|
2
|
+
module Payload
|
|
3
|
+
class Base
|
|
4
|
+
attr_reader :attributes
|
|
5
|
+
|
|
6
|
+
delegate :fetch, to: :attributes
|
|
7
|
+
|
|
8
|
+
def self.root_node(name = nil)
|
|
9
|
+
@root_node = name if name
|
|
10
|
+
@root_node
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.attribute(name, options = {})
|
|
14
|
+
attribute_defaults[name.to_s.to_sym] = options[:default]
|
|
15
|
+
visibility_read = options.has_key?(:read) ? options[:read] : true
|
|
16
|
+
visibility_write = options.has_key?(:write) ? options[:write] : true
|
|
17
|
+
if visibility_read
|
|
18
|
+
define_method name do
|
|
19
|
+
@attributes[name]
|
|
20
|
+
end
|
|
21
|
+
if %w[public private protected].include? visibility_read.to_s
|
|
22
|
+
send visibility_read, name
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if visibility_write
|
|
27
|
+
define_method "#{name}=" do |new_value|
|
|
28
|
+
@attributes[name] = new_value
|
|
29
|
+
end
|
|
30
|
+
if %w[public private protected].include? visibility_write.to_s
|
|
31
|
+
send visibility_write, name
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.attribute_defaults
|
|
37
|
+
@attribute_defaults ||= {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def reset!
|
|
41
|
+
@attributes = {}
|
|
42
|
+
apply_defaults
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def initialize(params = {})
|
|
47
|
+
apply_defaults
|
|
48
|
+
params = params.is_a?(Hash) ? params : {}
|
|
49
|
+
@attributes.merge! params
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def []=(key, value)
|
|
53
|
+
@attributes[key] = decode(value)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def [](key)
|
|
57
|
+
@attributes[key]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def display
|
|
61
|
+
drop? ? {} : @attributes
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def display?
|
|
65
|
+
true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def drop?
|
|
69
|
+
!!@drop
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def drop!
|
|
73
|
+
@drop = true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_json
|
|
77
|
+
display? ? display.to_json : ""
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def to_xml
|
|
81
|
+
display? ? display.to_xml(dasherize: false, skip_instruct: true, root: self.class.root_node) : ""
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def apply_defaults
|
|
85
|
+
@attributes = self.class.attribute_defaults.with_indifferent_access
|
|
86
|
+
end
|
|
87
|
+
private :apply_defaults
|
|
88
|
+
|
|
89
|
+
def decode(input)
|
|
90
|
+
if input.is_a? String
|
|
91
|
+
begin
|
|
92
|
+
ActiveSupport::JSON.decode input
|
|
93
|
+
rescue MultiJson::DecodeError, JSON::ParserError
|
|
94
|
+
begin
|
|
95
|
+
Hash.from_xml input
|
|
96
|
+
rescue REXML::ParseException
|
|
97
|
+
input
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
input
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
private :decode
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Axel
|
|
2
|
+
module Payload
|
|
3
|
+
class Errors < Base
|
|
4
|
+
root_node :error
|
|
5
|
+
attribute :messages, default: []
|
|
6
|
+
attribute :status, default: 200, read: :private
|
|
7
|
+
SUCCESS_CODES = [200, 201, 202, 204]
|
|
8
|
+
|
|
9
|
+
def initialize(params = {})
|
|
10
|
+
super
|
|
11
|
+
@suppress_response_codes = !!@attributes[:suppress_response_codes]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def exception
|
|
15
|
+
RemoteError.new(self) unless success?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def success?
|
|
19
|
+
SUCCESS_CODES.include?(status_code.to_i) && messages.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def messages=(messages)
|
|
23
|
+
@attributes[:messages] = [messages].flatten.compact
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def status=(status)
|
|
27
|
+
@attributes[:status] = status
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def header_status
|
|
31
|
+
suppress_response_codes? ? 200 : status_code
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def <<(message)
|
|
35
|
+
@attributes[:messages] << message
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def display
|
|
39
|
+
drop? ? {} : { status: status_code, messages: messages }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def display?
|
|
43
|
+
!SUCCESS_CODES.include?(status_code.to_i) || messages.present?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def new_error(status, *messages)
|
|
47
|
+
self.status = status if status
|
|
48
|
+
self.messages = self.messages + messages
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def status_code
|
|
52
|
+
status ? Rack::Utils.status_code(status) : 200
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def suppress_response_codes?
|
|
58
|
+
@suppress_response_codes
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Axel
|
|
2
|
+
module Payload
|
|
3
|
+
class Metadata < Base
|
|
4
|
+
root_node :metadata
|
|
5
|
+
|
|
6
|
+
def paged?
|
|
7
|
+
total_pages > 1
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def total_pages
|
|
11
|
+
raw = pagination_settings["total_pages"].to_i
|
|
12
|
+
raw == 0 ? 1 : raw
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def merge!(other_object)
|
|
16
|
+
tap do
|
|
17
|
+
@attributes.merge! mergeable_from(other_object)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def merge(other_object)
|
|
22
|
+
#self.class.new(attributes).tap do |c|
|
|
23
|
+
dup.tap do |c|
|
|
24
|
+
c.merge!(mergeable_from(other_object))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def dup(*args)
|
|
29
|
+
super.tap do |d|
|
|
30
|
+
d.instance_variable_set "@attributes", @attributes.clone
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def clone(*args)
|
|
35
|
+
super.tap do |c|
|
|
36
|
+
c.instance_variable_set "@attributes", @attributes.clone
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def pagination_settings
|
|
41
|
+
fetch("pagination", {})
|
|
42
|
+
end
|
|
43
|
+
private :pagination_settings
|
|
44
|
+
|
|
45
|
+
def mergeable_from(object)
|
|
46
|
+
if object.respond_to?(:attributes) && object.attributes.is_a?(Hash)
|
|
47
|
+
object.attributes
|
|
48
|
+
elsif object.is_a? Hash
|
|
49
|
+
object
|
|
50
|
+
else
|
|
51
|
+
{} # unmergeable, should think about erroring
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
private :mergeable_from
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
module Axel
|
|
2
|
+
class Querier
|
|
3
|
+
private
|
|
4
|
+
attr_writer :klass
|
|
5
|
+
attr_writer :loaded
|
|
6
|
+
attr_reader :records
|
|
7
|
+
|
|
8
|
+
protected
|
|
9
|
+
attr_accessor :where_values
|
|
10
|
+
attr_accessor :manual_uri
|
|
11
|
+
attr_accessor :extra_paths
|
|
12
|
+
attr_accessor :request_option_values
|
|
13
|
+
attr_accessor :paged
|
|
14
|
+
attr_writer :records
|
|
15
|
+
|
|
16
|
+
public
|
|
17
|
+
attr_reader :klass
|
|
18
|
+
attr_reader :loaded
|
|
19
|
+
|
|
20
|
+
alias loaded? loaded
|
|
21
|
+
alias paged? paged
|
|
22
|
+
|
|
23
|
+
def self.query_methods
|
|
24
|
+
[
|
|
25
|
+
:all,
|
|
26
|
+
:none,
|
|
27
|
+
:where,
|
|
28
|
+
:uri,
|
|
29
|
+
:to_a,
|
|
30
|
+
:at_path,
|
|
31
|
+
:request_options,
|
|
32
|
+
:without_default_path,
|
|
33
|
+
:paged
|
|
34
|
+
]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize(klass)
|
|
38
|
+
self.klass = klass
|
|
39
|
+
self.records = []
|
|
40
|
+
self.extra_paths = []
|
|
41
|
+
self.loaded = false
|
|
42
|
+
self.where_values = {}.with_indifferent_access
|
|
43
|
+
self.request_option_values = {}.with_indifferent_access
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def paged
|
|
47
|
+
query = clone
|
|
48
|
+
query.paged = true
|
|
49
|
+
query
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def at_path(*paths)
|
|
53
|
+
return self if paths.blank?
|
|
54
|
+
query = clone
|
|
55
|
+
query.extra_paths += paths
|
|
56
|
+
query
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# "http://user-service.dev"
|
|
60
|
+
def uri(base_url)
|
|
61
|
+
return self if base_url.blank?
|
|
62
|
+
query = clone
|
|
63
|
+
query.manual_uri = base_url.to_s
|
|
64
|
+
query
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def where(params = {})
|
|
68
|
+
return self if params.blank? || !params.is_a?(Hash)
|
|
69
|
+
query = clone
|
|
70
|
+
query.request_option_values.deep_merge! params: params
|
|
71
|
+
query
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def request_options(params = {})
|
|
75
|
+
return self if params.blank? || !params.is_a?(Hash)
|
|
76
|
+
query = clone
|
|
77
|
+
query.request_option_values.deep_merge! params
|
|
78
|
+
query
|
|
79
|
+
end
|
|
80
|
+
alias_method :all, :request_options
|
|
81
|
+
|
|
82
|
+
def none
|
|
83
|
+
query = clone
|
|
84
|
+
query.loaded!
|
|
85
|
+
query.records = []
|
|
86
|
+
query
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def without_default_path
|
|
90
|
+
query = clone
|
|
91
|
+
query.manual_uri = klass.site
|
|
92
|
+
query
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def reload
|
|
96
|
+
reset
|
|
97
|
+
self
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def to_a
|
|
101
|
+
Array(loaded? ? records : self.records = run_requests)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def inspect
|
|
105
|
+
to_a.inspect
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def loaded!
|
|
109
|
+
self.loaded = true
|
|
110
|
+
end
|
|
111
|
+
protected :loaded!
|
|
112
|
+
|
|
113
|
+
def reset
|
|
114
|
+
self.loaded = false
|
|
115
|
+
self.records = []
|
|
116
|
+
self
|
|
117
|
+
end
|
|
118
|
+
private :reset
|
|
119
|
+
|
|
120
|
+
def request_uri
|
|
121
|
+
Uri.new((manual_uri || klass.request_uri), *extra_paths).to_s
|
|
122
|
+
end
|
|
123
|
+
private :request_uri
|
|
124
|
+
|
|
125
|
+
def retrieve_request_options
|
|
126
|
+
klass.retrieve_default_request_options request_option_values
|
|
127
|
+
end
|
|
128
|
+
private :retrieve_request_options
|
|
129
|
+
|
|
130
|
+
def run_requests
|
|
131
|
+
loaded!
|
|
132
|
+
execute_request_on_klass
|
|
133
|
+
end
|
|
134
|
+
private :run_requests
|
|
135
|
+
|
|
136
|
+
def execute_request_on_klass
|
|
137
|
+
initial = klass.request(request_uri, retrieve_request_options)
|
|
138
|
+
return initial unless paged?
|
|
139
|
+
|
|
140
|
+
single = initial.first
|
|
141
|
+
return initial if single.blank? || !single.paged?
|
|
142
|
+
|
|
143
|
+
(2..single.total_pages).reduce(initial) do |memo, page|
|
|
144
|
+
paged_options = retrieve_request_options
|
|
145
|
+
.stringify_keys
|
|
146
|
+
.deep_merge "params" => { "page" => page }
|
|
147
|
+
memo + klass.request(request_uri, paged_options)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
private :execute_request_on_klass
|
|
151
|
+
|
|
152
|
+
def respond_to?(method, include_private = false)
|
|
153
|
+
super || Array.public_method_defined?(method) || klass.routes.has_key?(method)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def method_missing(method, *args, &block)
|
|
157
|
+
if Array.method_defined?(method)
|
|
158
|
+
to_a.public_send(method, *args, &block)
|
|
159
|
+
elsif klass.routes.has_key? method
|
|
160
|
+
klass.public_send method, *args
|
|
161
|
+
else
|
|
162
|
+
super
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|