/
class_interface.rb
134 lines (118 loc) · 3.73 KB
/
class_interface.rb
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
require 'rom/repository/relation_reader'
module ROM
class Repository
# Class-level APIs for repositories
#
# @api public
module ClassInterface
# Create a root-repository class and set its root relation
#
# @example
# # where :users is the relation name in your rom container
# class UserRepo < ROM::Repository[:users]
# end
#
# @param name [Symbol] The relation `register_as` value
#
# @return [Class] descendant of ROM::Repository::Root
#
# @api public
def [](name)
klass = Class.new(self < Repository::Root ? self : Repository::Root)
klass.root(name)
klass
end
# Initialize a new repository object
#
# @param [ROM::Container] container Finalized rom container
#
# @param [Hash] options Repository options
# @option options [Module] :struct_namespace Custom struct namespace
# @option options [Boolean] :auto_struct Enable/Disable auto-struct mapping
#
# @api public
def new(container, options = EMPTY_HASH)
unless relation_reader
relation_reader(RelationReader.new(self, container.relations.elements.keys))
include(relation_reader)
end
super
end
# Inherits configured relations and commands
#
# @api private
def inherited(klass)
super
return if self === Repository
klass.commands(*commands)
end
# Defines command methods on a root repository
#
# @example
# class UserRepo < ROM::Repository[:users]
# commands :create, update: :by_pk, delete: :by_pk
# end
#
# # with custom command plugin
# class UserRepo < ROM::Repository[:users]
# commands :create, plugin: :my_command_plugin
# end
#
# # with custom mapper
# class UserRepo < ROM::Repository[:users]
# commands :create, mapper: :my_custom_mapper
# end
#
# @param [Array<Symbol>] names A list of command names
# @option :mapper [Symbol] An optional mapper identifier
# @option :use [Symbol] An optional command plugin identifier
#
# @return [Array<Symbol>] A list of defined command names
#
# @api public
def commands(*names, mapper: nil, use: nil, **opts)
if names.any? || opts.any?
@commands = names + opts.to_a
@commands.each do |spec|
type, *view = Array(spec).flatten
if view.size > 0
define_restricted_command_method(type, view, mapper: mapper, use: use)
else
define_command_method(type, mapper: mapper, use: use)
end
end
else
@commands ||= []
end
end
private
# @api private
def define_command_method(type, **opts)
define_method(type) do |*input|
if input.size == 1 && input[0].respond_to?(:commit)
input[0].commit
else
root.command(type, **opts).call(*input)
end
end
end
# @api private
def define_restricted_command_method(type, views, **opts)
views.each do |view_name|
meth_name = views.size > 1 ? :"#{type}_#{view_name}" : type
define_method(meth_name) do |*args|
arity = root.__send__(view_name).arity
view_args = args[0..arity-1]
input = args[arity..args.size-1]
changeset = input.first
if changeset.respond_to?(:commit)
changeset.commit
else
root.command(type, **opts).public_send(view_name, *view_args).call(*input)
end
end
end
end
end
end
end