Skip to content

Commit b3ee6e4

Browse files
API: added short-cut methods for related records
For example, `authors` association provides the following extras: * `.authored_by` scope to filter records by authors, * `#authored_by?` — to check if the record is authored by provided ones, * `#authored_by!` — to add an author. NOTE: this applies to role-based associations with actor-like models only.
1 parent b6e0ab4 commit b3ee6e4

4 files changed

Lines changed: 81 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
- `referencing`/`referenced_by` scopes to filter by related records.
1414
- `#referencing!`/`#referenced_by!` setters to add related records.
15+
- Short-cut methods for related records.
16+
For example, `authors` association provides the following extras:
17+
- `.authored_by` scope to filter records by authors,
18+
- `#authored_by?` — to check if the record is authored by provided ones,
19+
- `#authored_by!` — to add an author.
1520

1621

1722
## [0.10.0] — 2025-04-23

lib/adjustable_schema/active_record/association.rb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ class Association < Struct.new( # :nodoc:
1515
require_relative 'association/scopes'
1616
require_relative 'association/roleless'
1717
require_relative 'association/hierarchy'
18+
require_relative 'association/referenced'
1819

1920
include Memery
2021

2122
def initialize(...)
2223
super
2324

24-
extend Roleless if roleless?
25-
extend Hierarchy if hierarchy?
25+
extend Roleless if roleless?
26+
extend Hierarchy if hierarchy?
27+
extend Referenced if source? and role and target.actor?
2628
end
2729

2830
def define
@@ -58,13 +60,15 @@ def define_relationships
5860

5961
def define_scopes
6062
scopes
61-
.reject { owner.singleton_class.method_defined? _1 }
63+
.reject { owner.respond_to? _1 }
6264
.each { owner.scope _1, _2 }
6365
end
6466

6567
def define_methods
66-
flags
67-
.transform_keys { :"#{it}?" }
68+
{
69+
**flag_methods,
70+
**setter_methods,
71+
}
6872
.reject { owner.method_defined? _1 }
6973
.each { owner.define_method _1, &_2 }
7074
end
@@ -93,6 +97,14 @@ def flags
9397
}
9498
end
9599

100+
def flag_methods = flags
101+
.transform_keys { :"#{it}?" }
102+
103+
def setters = {}
104+
105+
def setter_methods = setters
106+
.transform_keys { :"#{it}!" }
107+
96108
memoize def options = {
97109
through: define_relationships,
98110
source: direction,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# frozen_string_literal: true
2+
3+
module AdjustableSchema
4+
module ActiveRecord
5+
module Association::Referenced # :nodoc:
6+
private
7+
8+
def scopes
9+
role = self.role.name # save the context
10+
11+
{
12+
**super,
13+
14+
referenced_name => -> { referenced_by it, role: },
15+
}
16+
end
17+
18+
def flags
19+
name = self.name # save the context
20+
21+
{
22+
**super,
23+
24+
referenced_name => -> { it.in? send name },
25+
}
26+
end
27+
28+
def setters
29+
role = self.role.name # save the context
30+
31+
{
32+
**super,
33+
34+
referenced_name => -> { referenced_by! it, role: },
35+
}
36+
end
37+
38+
using Association::Inflections
39+
40+
def referenced_name = :"#{object_name.passivize}_by"
41+
end
42+
end
43+
end

test/lib/active_record/association_test.rb

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,29 @@ def defines_methods(&)
7676
let(:association_name) { 'authors' }
7777
let(:name_for_any) { 'authored' }
7878

79-
defines_scopes
80-
defines_methods
79+
defines_scopes do
80+
_(owner).must_respond_to :authored_by
81+
end
82+
83+
defines_methods do
84+
_(record).must_respond_to :authored_by?
85+
_(record).must_respond_to :authored_by!
86+
end
8187

8288
describe 'without a role' do
8389
let(:role) {}
8490

8591
let(:association_name) { 'users' }
8692
let(:name_for_any) { 'used' }
8793

88-
defines_scopes
89-
defines_methods
94+
defines_scopes do
95+
_(owner).wont_respond_to :used_by
96+
end
97+
98+
defines_methods do
99+
_(record).wont_respond_to :used_by?
100+
_(record).wont_respond_to :used_by!
101+
end
90102
end
91103
end
92104

0 commit comments

Comments
 (0)