lycra 0.0.7 → 5.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.
- checksums.yaml +5 -5
- data/lib/awesome_print/ext/lycra_attribute.rb +25 -0
- data/lib/awesome_print/ext/lycra_attributes.rb +25 -0
- data/lib/awesome_print/formatters/lycra_attribute_formatter.rb +58 -0
- data/lib/awesome_print/formatters/lycra_attributes_formatter.rb +26 -0
- data/lib/elasticsearch/model/adapters/lycra.rb +2 -0
- data/lib/elasticsearch/model/adapters/lycra/document.rb +120 -0
- data/lib/elasticsearch/model/adapters/lycra/multiple.rb +70 -0
- data/lib/lycra.rb +20 -3
- data/lib/lycra/attributes.rb +146 -0
- data/lib/lycra/attributes/attribute.rb +143 -0
- data/lib/lycra/attributes/collection.rb +38 -0
- data/lib/lycra/awesome_print.rb +6 -0
- data/lib/lycra/decorator.rb +12 -0
- data/lib/lycra/decorator/model.rb +33 -0
- data/lib/lycra/document.rb +11 -0
- data/lib/lycra/document/model.rb +37 -0
- data/lib/lycra/document/proxy.rb +485 -0
- data/lib/lycra/document/registry.rb +38 -0
- data/lib/lycra/engine.rb +4 -1
- data/lib/lycra/errors.rb +18 -3
- data/lib/lycra/import.rb +116 -0
- data/lib/lycra/inheritance.rb +17 -0
- data/lib/lycra/monkeypatches.rb +7 -37
- data/lib/lycra/multidoc.rb +22 -0
- data/lib/lycra/search.rb +2 -2
- data/lib/lycra/serializer.rb +19 -0
- data/lib/lycra/serializer/model.rb +35 -0
- data/lib/lycra/types.rb +165 -0
- data/lib/lycra/version.rb +2 -2
- data/spec/spec_helper.rb +13 -8
- data/spec/support/foo.rb +120 -0
- metadata +53 -31
- data/app/documents/lycra/document.rb +0 -191
- data/lib/lycra/model.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5fc3ae92178f1c1b65aef5866d78ba48eaac5ac5a1f26874f9b9de27171af7c5
|
4
|
+
data.tar.gz: 550692ddd9612fa6b13e1899743c40f080efeb3e5f2f99153a1bc2bd4e600bd7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1425e10b35c3ca78ae95f513535ef8c2568c1f7704045b479be24dcc699277f6ab912023b06c7c882000aa67dc298361dd9216b647cf7f6f3220640b146c0e02
|
7
|
+
data.tar.gz: 156654656586332872962fee77c87272a2cac14342d7a8dbae90e5f9f57088a94189b827536ce4e419d5d3635c32f59bb6ff2ba5954d1e59d174291b76df3826
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AwesomePrint
|
2
|
+
module LycraAttribute
|
3
|
+
def self.included(base)
|
4
|
+
base.send :alias_method, :cast_without_lycra_attribute, :cast
|
5
|
+
base.send :alias_method, :cast, :cast_with_lycra_attribute
|
6
|
+
end
|
7
|
+
|
8
|
+
def cast_with_lycra_attribute(object, type)
|
9
|
+
cast = cast_without_lycra_attribute(object, type)
|
10
|
+
return cast unless defined?(::Lycra::Attributes::Attribute)
|
11
|
+
|
12
|
+
if object.is_a?(::Lycra::Attributes::Attribute)
|
13
|
+
cast = :lycra_attribute
|
14
|
+
end
|
15
|
+
|
16
|
+
cast
|
17
|
+
end
|
18
|
+
|
19
|
+
def awesome_lycra_attribute(object)
|
20
|
+
Formatters::LycraAttributeFormatter.new(object, @inspector).format
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
AwesomePrint::Formatter.send(:include, AwesomePrint::LycraAttribute)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AwesomePrint
|
2
|
+
module LycraAttributes
|
3
|
+
def self.included(base)
|
4
|
+
base.send :alias_method, :cast_without_lycra_attributes, :cast
|
5
|
+
base.send :alias_method, :cast, :cast_with_lycra_attributes
|
6
|
+
end
|
7
|
+
|
8
|
+
def cast_with_lycra_attributes(object, type)
|
9
|
+
cast = cast_without_lycra_attributes(object, type)
|
10
|
+
return cast unless defined?(::Lycra::Attributes::Collection)
|
11
|
+
|
12
|
+
if object.is_a?(::Lycra::Attributes::Collection)
|
13
|
+
cast = :lycra_attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
cast
|
17
|
+
end
|
18
|
+
|
19
|
+
def awesome_lycra_attributes(object)
|
20
|
+
Formatters::LycraAttributesFormatter.new(object, @inspector).format
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
AwesomePrint::Formatter.send(:include, AwesomePrint::LycraAttributes)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module AwesomePrint
|
2
|
+
module Formatters
|
3
|
+
class LycraAttributeFormatter < BaseFormatter
|
4
|
+
attr_reader :attribute, :inspector, :options, :padding
|
5
|
+
# TODO mappings (maybe refactor attribute(s) to accept the document and
|
6
|
+
# behave differently if they're a document attribute or not)
|
7
|
+
|
8
|
+
def initialize(attribute, inspector, padding: 0)
|
9
|
+
@attribute = attribute
|
10
|
+
@inspector = inspector
|
11
|
+
@options = inspector.options
|
12
|
+
@padding = padding
|
13
|
+
end
|
14
|
+
|
15
|
+
def format
|
16
|
+
if options[:multiline]
|
17
|
+
[
|
18
|
+
name_and_type,
|
19
|
+
indent + (' ' * (padding + 1)) + description,
|
20
|
+
#indent + (' ' * (padding + 1)) + mappings
|
21
|
+
].join("\n")
|
22
|
+
else
|
23
|
+
"#{name_and_type} #{description}" #{mappings}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def name_and_type
|
30
|
+
name = align(attribute.name.to_s, indentation + padding)
|
31
|
+
name = attribute.required? ? name.green : name.cyan
|
32
|
+
type = attribute.type.type
|
33
|
+
type = attribute.nested? ? "[#{type}]" : "#{type}"
|
34
|
+
"#{name} #{type.blue} #{resolver}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def description
|
38
|
+
attribute.description || "No description"
|
39
|
+
end
|
40
|
+
|
41
|
+
def resolver
|
42
|
+
if attribute.resolver.is_a?(Symbol)
|
43
|
+
if attribute.klass.instance_methods.include?(attribute.resolver)
|
44
|
+
(attribute.klass.name.yellowish + "##{attribute.resolver.to_s}".purpleish)
|
45
|
+
else
|
46
|
+
(attribute.klass.subject_type.name.yellowish + "##{attribute.resolver.to_s}".purpleish)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
attribute.resolver.to_s.yellowish
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def mappings
|
54
|
+
Inspector.new(options.merge({multiline: false})).awesome(attribute.mappings)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module AwesomePrint
|
2
|
+
module Formatters
|
3
|
+
class LycraAttributesFormatter < HashFormatter
|
4
|
+
attr_reader :attributes, :inspector, :options
|
5
|
+
|
6
|
+
def initialize(attributes, inspector)
|
7
|
+
@attributes = attributes
|
8
|
+
@inspector = inspector
|
9
|
+
@options = inspector.options
|
10
|
+
end
|
11
|
+
|
12
|
+
def format
|
13
|
+
padding = attributes.keys.map { |k| k.to_s.length }.max
|
14
|
+
attrs = attributes.values.map do |attr|
|
15
|
+
LycraAttributeFormatter.new(attr, inspector, padding: padding).format
|
16
|
+
end
|
17
|
+
|
18
|
+
if options[:multiline]
|
19
|
+
"#{attributes.to_s} {\n#{attrs.join("\n")}\n#{outdent}}"
|
20
|
+
else
|
21
|
+
"#{attributes.to_s} { #{attrs.join(", ")} }"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Model
|
3
|
+
module Adapter
|
4
|
+
module Lycra
|
5
|
+
# An adapter for Lycra::Document-based models
|
6
|
+
module Document
|
7
|
+
|
8
|
+
Adapter.register self,
|
9
|
+
lambda { |klass| !!defined?(::Lycra::Document::Proxy) && klass.respond_to?(:ancestors) && klass.ancestors.include?(::Lycra::Document::Proxy) }
|
10
|
+
|
11
|
+
module Records
|
12
|
+
attr_writer :options
|
13
|
+
|
14
|
+
def options
|
15
|
+
@options ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns an `ActiveRecord::Relation` instance
|
19
|
+
#
|
20
|
+
def records
|
21
|
+
sql_records = klass.subject_type.where(klass.subject_type.primary_key => ids)
|
22
|
+
sql_records = sql_records.includes(self.options[:includes]) if self.options[:includes]
|
23
|
+
|
24
|
+
# Re-order records based on the order from Elasticsearch hits
|
25
|
+
# by redefining `to_a`, unless the user has called `order()`
|
26
|
+
#
|
27
|
+
sql_records.instance_exec(response.response['hits']['hits']) do |hits|
|
28
|
+
ar_records_method_name = :to_a
|
29
|
+
ar_records_method_name = :records if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 5
|
30
|
+
|
31
|
+
define_singleton_method(ar_records_method_name) do
|
32
|
+
if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 4
|
33
|
+
self.load
|
34
|
+
else
|
35
|
+
self.__send__(:exec_queries)
|
36
|
+
end
|
37
|
+
@records.sort_by { |record| hits.index { |hit| hit['_id'].to_s == record.id.to_s } }
|
38
|
+
end if self
|
39
|
+
end
|
40
|
+
|
41
|
+
sql_records
|
42
|
+
end
|
43
|
+
|
44
|
+
# Prevent clash with `ActiveSupport::Dependencies::Loadable`
|
45
|
+
#
|
46
|
+
def load
|
47
|
+
records.__send__(:load)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Intercept call to the `order` method, so we can ignore the order from Elasticsearch
|
51
|
+
#
|
52
|
+
def order(*args)
|
53
|
+
sql_records = records.__send__ :order, *args
|
54
|
+
|
55
|
+
# Redefine the `to_a` method to the original one
|
56
|
+
#
|
57
|
+
sql_records.instance_exec do
|
58
|
+
define_singleton_method(:to_a) do
|
59
|
+
if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 4
|
60
|
+
self.load
|
61
|
+
else
|
62
|
+
self.__send__(:exec_queries)
|
63
|
+
end
|
64
|
+
@records
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
sql_records
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module Callbacks
|
73
|
+
# Lycra does not use callbacks directly on the documents
|
74
|
+
end
|
75
|
+
|
76
|
+
module Importing
|
77
|
+
|
78
|
+
# Fetch batches of records from the database (used by the import method)
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# @see http://api.rubyonrails.org/classes/ActiveRecord/Batches.html ActiveRecord::Batches.find_in_batches
|
82
|
+
#
|
83
|
+
def __find_in_batches(options={}, &block)
|
84
|
+
query = options.delete(:query)
|
85
|
+
named_scope = options.delete(:scope)
|
86
|
+
preprocess = options.delete(:preprocess)
|
87
|
+
|
88
|
+
scope = subject_type
|
89
|
+
scope = scope.__send__(named_scope) if named_scope
|
90
|
+
scope = scope.instance_exec(&query) if query
|
91
|
+
|
92
|
+
scope.find_in_batches(options) do |batch|
|
93
|
+
yield (preprocess ? self.__send__(preprocess, batch) : batch)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def __transform
|
98
|
+
lambda do |model|
|
99
|
+
json_data = begin
|
100
|
+
if model.respond_to?(:model_document)
|
101
|
+
# TODO model_document.as_indexed_json(model)
|
102
|
+
else
|
103
|
+
modoc = begin
|
104
|
+
"#{model.class.name}Document".constantize
|
105
|
+
rescue NameError => e
|
106
|
+
raise "Unable to locate a document class for the #{model.class.name} model"
|
107
|
+
end
|
108
|
+
modoc.as_indexed_json(model)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
{ index: { _id: model.id, data: json_data } }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Model
|
3
|
+
module Adapter
|
4
|
+
module Lycra
|
5
|
+
|
6
|
+
module Multiple
|
7
|
+
Adapter.register self, lambda { |klass| klass.is_a? ::Lycra::Multidoc }
|
8
|
+
|
9
|
+
module Records
|
10
|
+
#def documents
|
11
|
+
# documents_by_type = __documents_by_type
|
12
|
+
|
13
|
+
# documents = response.response["hits"]["hits"].map do |hit|
|
14
|
+
# documents_by_type[ __type_for_hit(hit) ][ hit[:_id] ]
|
15
|
+
# end
|
16
|
+
|
17
|
+
# documents.compact
|
18
|
+
#end
|
19
|
+
|
20
|
+
def records
|
21
|
+
documents_by_type = __documents_by_type
|
22
|
+
|
23
|
+
records = response.response["hits"]["hits"].map do |hit|
|
24
|
+
documents_by_type[ __type_for_hit(hit) ][ hit[:_id] ]#.subject
|
25
|
+
end
|
26
|
+
|
27
|
+
records.compact
|
28
|
+
end
|
29
|
+
|
30
|
+
def __documents_by_type
|
31
|
+
result = __ids_by_type.map do |klass, ids|
|
32
|
+
documents = __documents_for_klass(klass, ids)
|
33
|
+
ids = documents.map(&:id).map(&:to_s)
|
34
|
+
mapped = [ klass, Hash[ids.zip(documents)] ]
|
35
|
+
mapped
|
36
|
+
end
|
37
|
+
|
38
|
+
Hash[result]
|
39
|
+
end
|
40
|
+
|
41
|
+
def __documents_for_klass(klass, ids)
|
42
|
+
return klass.subject_type.where(klass.primary_key => ids)
|
43
|
+
end
|
44
|
+
|
45
|
+
def __ids_by_type
|
46
|
+
ids_by_type = {}
|
47
|
+
|
48
|
+
response.response["hits"]["hits"].each do |hit|
|
49
|
+
type = __type_for_hit(hit)
|
50
|
+
ids_by_type[type] ||= []
|
51
|
+
ids_by_type[type] << hit[:_id]
|
52
|
+
end
|
53
|
+
ids_by_type
|
54
|
+
end
|
55
|
+
|
56
|
+
def __type_for_hit(hit)
|
57
|
+
@@__types ||= {}
|
58
|
+
|
59
|
+
@@__types[ "#{hit[:_index]}::#{hit[:_type]}" ] ||= begin
|
60
|
+
::Lycra::Document::Registry.all.detect do |document|
|
61
|
+
hit[:_index] =~ /\A#{document.index_name}(-[a-zA-Z0-9]+)?\Z/ && document.document_type == hit[:_type]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/lycra.rb
CHANGED
@@ -1,12 +1,23 @@
|
|
1
1
|
require 'logger'
|
2
2
|
require 'canfig'
|
3
|
-
require 'elasticsearch/persistence'
|
4
3
|
require 'elasticsearch/model'
|
4
|
+
require 'elasticsearch/model/adapters/lycra'
|
5
5
|
require 'lycra/monkeypatches'
|
6
6
|
require 'lycra/errors'
|
7
|
-
require 'lycra/
|
7
|
+
require 'lycra/types'
|
8
|
+
require 'lycra/attributes'
|
9
|
+
require 'lycra/serializer'
|
10
|
+
require 'lycra/serializer/model'
|
11
|
+
require 'lycra/document'
|
12
|
+
require 'lycra/document/model'
|
13
|
+
require 'lycra/import'
|
14
|
+
require 'lycra/decorator'
|
15
|
+
require 'lycra/decorator/model'
|
16
|
+
require 'lycra/inheritance'
|
8
17
|
require 'lycra/search'
|
18
|
+
require 'lycra/multidoc'
|
9
19
|
require 'lycra/engine' if defined?(Rails)
|
20
|
+
require 'lycra/awesome_print' if defined?(AwesomePrint)
|
10
21
|
|
11
22
|
module Lycra
|
12
23
|
include Canfig::Module
|
@@ -32,7 +43,7 @@ module Lycra
|
|
32
43
|
end
|
33
44
|
|
34
45
|
def self.client
|
35
|
-
@client ||= Elasticsearch::Client.new(
|
46
|
+
@client ||= ::Elasticsearch::Client.new(
|
36
47
|
host: configuration.elasticsearch_url,
|
37
48
|
log: configuration.log,
|
38
49
|
logger: configuration.logger,
|
@@ -40,4 +51,10 @@ module Lycra
|
|
40
51
|
tracer: configuration.tracer
|
41
52
|
)
|
42
53
|
end
|
54
|
+
|
55
|
+
def self.search(query_or_payload, models=[], options={})
|
56
|
+
models = Multidoc.new(models)
|
57
|
+
request = Elasticsearch::Model::Searching::SearchRequest.new(models, query_or_payload, options)
|
58
|
+
Elasticsearch::Model::Response::Response.new(models, request)
|
59
|
+
end
|
43
60
|
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'lycra/attributes/collection'
|
2
|
+
require 'lycra/attributes/attribute'
|
3
|
+
|
4
|
+
module Lycra
|
5
|
+
module Attributes
|
6
|
+
def self.included(base)
|
7
|
+
base.send :attr_reader, :resolved
|
8
|
+
base.send :attr_accessor, :subject
|
9
|
+
base.send :extend, ClassMethods
|
10
|
+
base.send :include, InstanceMethods
|
11
|
+
|
12
|
+
base.class_eval do
|
13
|
+
# TODO only for activerecord models??
|
14
|
+
attribute! :id, types.integer
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def inherited(child)
|
20
|
+
super if defined?(super)
|
21
|
+
|
22
|
+
# This clones parent attribues down to inheriting child classes
|
23
|
+
child.send :instance_variable_set,
|
24
|
+
:@_lycra_attributes,
|
25
|
+
self.attributes.dup(child)
|
26
|
+
end
|
27
|
+
|
28
|
+
def types
|
29
|
+
Lycra::Types
|
30
|
+
end
|
31
|
+
|
32
|
+
def attribute(name=nil, type=nil, *args, **opts, &block)
|
33
|
+
opts = {cache: cache}.merge(opts)
|
34
|
+
attr = Attribute.new(name, type, *args, **opts.merge({klass: self}), &block)
|
35
|
+
attributes[attr.name] = attr
|
36
|
+
end
|
37
|
+
|
38
|
+
def attribute!(name=nil, type=nil, *args, **opts, &block)
|
39
|
+
attribute(name, type, *args, **opts.merge({required: true}), &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def attributes
|
43
|
+
@_lycra_attributes ||= Collection.new(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def subject_type(klass=nil)
|
47
|
+
@_lycra_subject_type = klass if klass
|
48
|
+
@_lycra_subject_type ||= (name.gsub(/(Decorator|Document|Serializer)\Z/, '').constantize rescue nil)
|
49
|
+
end
|
50
|
+
|
51
|
+
def subject_type=(klass)
|
52
|
+
subject_type klass
|
53
|
+
end
|
54
|
+
|
55
|
+
def cache(cache=nil)
|
56
|
+
@_lycra_cache = cache unless cache.nil?
|
57
|
+
@_lycra_cache
|
58
|
+
end
|
59
|
+
|
60
|
+
def resolve!(subj, *args, **context)
|
61
|
+
if subj.is_a?(subject_type)
|
62
|
+
return new(subj).resolve!(*args, **context)
|
63
|
+
elsif subj.is_a?(Enumerable) && subj.first.is_a?(subject_type)
|
64
|
+
return subj.map { |s| resolve!(s, *args, **context) }
|
65
|
+
end
|
66
|
+
|
67
|
+
raise "Invalid subject: #{subj}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def method_missing(meth, *args, &block)
|
71
|
+
if subject_type && subject_type.respond_to?(meth)
|
72
|
+
result = subject_type.send(meth, *args, &block)
|
73
|
+
|
74
|
+
return result.try(:document) || new(result) if result.is_a?(subject_type)
|
75
|
+
return result if result.is_a?(ActiveRecord::Relation)
|
76
|
+
return result.map { |r| r.try(:document) || new(r) } if result.is_a?(Enumerable) && result.first.is_a?(subject_type)
|
77
|
+
|
78
|
+
return result
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def respond_to_missing?(meth, priv=false)
|
85
|
+
(subject_type && subject_type.respond_to?(meth, priv)) || super
|
86
|
+
end
|
87
|
+
|
88
|
+
def inspect
|
89
|
+
"#{name}(subject: #{subject_type}, #{attributes.map { |key,attr| "#{attr.name}: #{attr.nested? ? "[#{attr.type.type}]" : attr.type.type}"}.join(', ')})"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module InstanceMethods
|
94
|
+
delegate :subject_type, to: :class
|
95
|
+
|
96
|
+
def initialize(subject)
|
97
|
+
@subject = subject
|
98
|
+
end
|
99
|
+
|
100
|
+
def attributes
|
101
|
+
@attributes ||= self.class.attributes.dup
|
102
|
+
end
|
103
|
+
|
104
|
+
def resolve!(*args, **options)
|
105
|
+
raise Lycra::MissingSubjectError.new(self) if subject.nil?
|
106
|
+
context = options.slice!(:only, :except)
|
107
|
+
|
108
|
+
@resolved = attributes.map do |key,attr|
|
109
|
+
next if (options.key?(:only) && ![options[:only]].flatten.include?(key)) ||
|
110
|
+
(options.key?(:except) && [options[:except]].flatten.include?(key))
|
111
|
+
[ key, attr.dup.resolve!(self, args, context) ]
|
112
|
+
end.compact.to_h
|
113
|
+
end
|
114
|
+
|
115
|
+
def resolved?
|
116
|
+
!!@resolved
|
117
|
+
end
|
118
|
+
|
119
|
+
def reload
|
120
|
+
@resolved = nil
|
121
|
+
attributes.values.each(&:reload)
|
122
|
+
subject.send(:reload) if subject.respond_to?(:reload)
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def method_missing(meth, *args, &block)
|
127
|
+
return subject if meth == subject_type.to_s.underscore.to_sym
|
128
|
+
return attributes[meth].resolve!(self, *args, &block) if attributes.key?(meth)
|
129
|
+
return subject.send(meth, *args, &block) if subject && subject.respond_to?(meth)
|
130
|
+
super
|
131
|
+
end
|
132
|
+
|
133
|
+
def respond_to_missing?(meth, priv=false)
|
134
|
+
meth == subject_type.to_s.underscore.to_sym || attributes.key?(meth) || (subject && subject.respond_to?(meth, priv)) || super
|
135
|
+
end
|
136
|
+
|
137
|
+
def inspect
|
138
|
+
if resolved?
|
139
|
+
"#<#{self.class.name} subject: #{subject.class}, #{attributes.map { |key,attr| "#{key}: #{resolved[key].try(:to_json) || (attr.nested? ? "[#{attr.type.type}]" : attr.type.type)}"}.join(', ')}>"
|
140
|
+
else
|
141
|
+
"#<#{self.class.name} subject: #{subject.class}, #{attributes.map { |key,attr| "#{attr.name}: #{attr.nested? ? "[#{attr.type.type}]" : attr.type.type}"}.join(', ')}>"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|