lycra 0.0.7 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|