1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
require "active_support/core_ext/class/attribute"
require "active_support/core_ext/string/inflections"
require "active_support/core_ext/module/anonymous"
require "set"
module ActiveModel
class Serializer
module Associations
class Config < Struct.new(:name, :options)
def serializer
options[:serializer]
end
end
class HasMany < Config
def serialize(collection, scope)
collection.map do |item|
serializer.new(item, scope).serializable_hash
end
end
def serialize_ids(collection, scope)
# use named scopes if they are present
return collection.ids if collection.respond_to?(:ids)
collection.map do |item|
item.read_attribute_for_serialization(:id)
end
end
end
class HasOne < Config
def serialize(object, scope)
object && serializer.new(object, scope).serializable_hash
end
def serialize_ids(object, scope)
object && object.read_attribute_for_serialization(:id)
end
end
end
class_attribute :_attributes
self._attributes = Set.new
class_attribute :_associations
self._associations = []
class_attribute :_root
class << self
def attributes(*attrs)
self._attributes += attrs
end
def associate(klass, attrs)
options = attrs.extract_options!
self._associations += attrs.map do |attr|
unless method_defined?(attr)
class_eval "def #{attr}() object.#{attr} end", __FILE__, __LINE__
end
options[:serializer] ||= const_get("#{attr.to_s.camelize}Serializer")
klass.new(attr, options)
end
end
def has_many(*attrs)
associate(Associations::HasMany, attrs)
end
def has_one(*attrs)
associate(Associations::HasOne, attrs)
end
def root(name)
self._root = name
end
def inherited(klass)
return if klass.anonymous?
name = klass.name.demodulize.underscore.sub(/_serializer$/, '')
klass.class_eval do
alias_method name.to_sym, :object
root name.to_sym unless self._root == false
end
end
end
attr_reader :object, :scope
def initialize(object, scope)
@object, @scope = object, scope
end
def as_json(*)
if _root
{ _root => serializable_hash }
else
serializable_hash
end
end
def serializable_hash
attributes.merge(associations)
end
def associations
hash = {}
_associations.each do |association|
associated_object = send(association.name)
hash[association.name] = association.serialize(associated_object, scope)
end
hash
end
def association_ids
hash = {}
_associations.each do |association|
associated_object = send(association.name)
hash[association.name] = association.serialize_ids(associated_object, scope)
end
hash
end
def attributes
hash = {}
_attributes.each do |name|
hash[name] = @object.read_attribute_for_serialization(name)
end
hash
end
end
end
|