adjustable_schema 0.6.0 → 0.7.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9fab61b2df51275e0e3c9ebf681dc48a30378a48248b1f02c7bad89afc47075
4
- data.tar.gz: 266abb846dba3f7119dd424f855e77942d3ffc98b012b44d2210902cff32a604
3
+ metadata.gz: 5a26450a562fbcde4b87abd3a84e85f577d3c1a7c9f8ecfdf22b79728875f1e6
4
+ data.tar.gz: b2cda5f212ae129a97b92a80440f8e053af2c44d5c54676e2358da3230cb987a
5
5
  SHA512:
6
- metadata.gz: 9f2b270be59abff4ada4cfc1d9146f00bcbf43c8473588e23f411b71a5daf4f1928e1c075da4b3d4112e1883245249a5a6f42c5bbff148663ceca5fed8492bbe
7
- data.tar.gz: f9ea77c562420ed47c701f963d2fcc9d7835ead91411e7e150944d9d9a9ba49a09b6bdc5ea44c4bcf7bf73e98e9767b8eb383b493f8d5f5aaae1821a4f10cf6c
6
+ metadata.gz: 1bf5d1bcef25e17cdbfc7de6d1f4f0278571169e9416d96fa4542ae654ea236184b39fa162938870d085a5afc496beac98e8a6a48ad311e69a277a97eb2f01d8
7
+ data.tar.gz: '099e2973293293635d6a1a1cda5a3105363e5c42dedc14cc30f07fc967870b6586da29dd4443e2ab37ee7b93059e063e538a8f3c39bc98308354342f5398a3bb'
data/README.md CHANGED
@@ -56,10 +56,12 @@ book.editor_people
56
56
 
57
57
  #### Special cases
58
58
 
59
- In case you have set up relations with a `User` model you'll get a slightly different naming:
59
+ ##### "Actor-like" models
60
+
61
+ In case you have set up relationships with `User` model you'll get a slightly different naming:
60
62
 
61
63
  ``` ruby
62
- AdjustableSchema::Relationship.seed! User => Book, %w[author editor]
64
+ AdjustableSchema::Relationship.seed! User => Book, roles: %w[author editor]
63
65
  ```
64
66
 
65
67
  ``` ruby
@@ -71,11 +73,42 @@ book.editors
71
73
  The list of models to be handled this way can be set with `actor_model_names` configuration parameter.
72
74
  It includes `User` by default.
73
75
 
74
- ###### TODO
76
+ ##### Self-referencing models
77
+
78
+ You may want to set up self-targeted relationships:
79
+
80
+ ``` ruby
81
+ AdjustableSchema::Relationship.seed! Person, roles: %w[friend]
82
+ ```
83
+
84
+ In this case you'll get these associations:
85
+
86
+ ``` ruby
87
+ person.parents
88
+ person.children # for all the children
89
+ person.people # for "roleless" children, not friends
90
+ person.friends
91
+ person.friended_people
92
+ ```
93
+
94
+ If you prefer a different naming over `parents` & `children`, you can configure it like this:
75
95
 
76
- * Describe self-referential associations.
96
+ ```ruby
97
+ AdjustableSchema::Engine.configure do
98
+ config.names[:associations][:source][:self] = :effect
99
+ config.names[:associations][:target][:self] = :cause
100
+ end
101
+ ```
102
+
103
+ Thus, for the self-referenced `Event`s, you'll get:
104
+
105
+ ``` ruby
106
+ event.causes
107
+ event.effects
108
+ ```
77
109
 
78
110
  ## Installation
111
+
79
112
  Add this line to your application's Gemfile:
80
113
 
81
114
  ```ruby
@@ -83,13 +116,15 @@ gem "adjustable_schema"
83
116
  ```
84
117
 
85
118
  And then execute:
119
+
86
120
  ```bash
87
- $ bundle
121
+ bundle
88
122
  ```
89
123
 
90
124
  Or install it yourself as:
125
+
91
126
  ```bash
92
- $ gem install adjustable_schema
127
+ gem install adjustable_schema
93
128
  ```
94
129
 
95
130
  ## Contributing
@@ -101,4 +136,5 @@ $ gem install adjustable_schema
101
136
  5. Create new Pull Request
102
137
 
103
138
  ## License
139
+
104
140
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -4,7 +4,7 @@ class CreateAdjustableSchemaRelationshipTables < ActiveRecord::Migration[7.1]
4
4
  primary_key_type, foreign_key_type = primary_and_foreign_key_types
5
5
 
6
6
  create_table :adjustable_schema_relationship_roles do |t|
7
- t.string :name, index: { unique: true }
7
+ t.string :name, null: false, index: { unique: true }
8
8
 
9
9
  t.timestamps
10
10
  end
@@ -17,13 +17,20 @@ class CreateAdjustableSchemaRelationshipTables < ActiveRecord::Migration[7.1]
17
17
 
18
18
  t.timestamps
19
19
 
20
- t.index %i[
20
+ %i[
21
21
  source_id source_type
22
22
  target_id target_type
23
23
  role_id
24
24
  ].tap { |columns|
25
- columns.reject! { _1.ends_with? '_type' } if foreign_key_type == :uuid # OPTIMIZATION: IDs are unique
26
- }, unique: true, name: :index_adjustable_schema_relationships_uniqueness
25
+ columns.reject! { _1.ends_with? '_type' } if foreign_key_type == :uuid # OPTIMIZATION: IDs are unique
26
+
27
+ # NULLS are DISTINCT by default.
28
+ # One can use `ADD CONSTRAINT … UNIQUE NULLS NOT DISTINCT (…)` instead
29
+ t.index columns,
30
+ unique: true, where: 'role_id IS NOT NULL', name: :index_adjustable_schema_relationships_uniqueness_with_role
31
+ t.index columns.excluding(:role_id),
32
+ unique: true, where: 'role_id IS NULL', name: :index_adjustable_schema_relationships_uniqueness_without_role
33
+ }
27
34
  end
28
35
  end
29
36
 
@@ -9,7 +9,9 @@ module AdjustableSchema
9
9
  module Inflections
10
10
  refine String do
11
11
  def passivize
12
- sub(/(e?d?|ing|[eo]r|ant|(t)ion)$/, '\\2ed')
12
+ self
13
+ .sub(/(author)$/, '\\2ed')
14
+ .sub(/(e*|ed|ing|[eo]r|ant|(t)ion)$/, '\\2ed')
13
15
  end
14
16
  end
15
17
  end
@@ -36,7 +38,7 @@ module AdjustableSchema
36
38
  private
37
39
 
38
40
  memoize def name_with_role
39
- if self_targeted?
41
+ if recursive?
40
42
  {
41
43
  source: role.name,
42
44
  target: "#{role.name.passivize}_#{target_name}",
@@ -50,13 +52,16 @@ module AdjustableSchema
50
52
  end
51
53
 
52
54
  memoize def name_without_role
53
- if self_targeted?
55
+ if recursive?
54
56
  Config.association_directions
55
- .self_related[direction]
57
+ .self[direction]
56
58
  else
57
59
  target_name
58
60
  end
59
61
  end
62
+
63
+ def name_for_any = :"#{name.to_s.singularize.passivize}"
64
+ def name_for_none = :"#{name.to_s.singularize}less"
60
65
  end
61
66
  end
62
67
  end
@@ -15,19 +15,24 @@ module AdjustableSchema
15
15
  class_name: target.name
16
16
  }) do
17
17
  include Scopes
18
- include Scopes::Recursive if association.self_targeted?
18
+ include Scopes::Recursive if association.recursive?
19
19
  end
20
20
 
21
+ define_scopes
22
+ define_methods
23
+
21
24
  unless role
22
- has_many target_name.tableize.to_sym, -> { roleless }, **options if
23
- self_targeted?
25
+ # HACK: using `try` to overcome a Rails bug
26
+ # (see https://github.com/rails/rails/issues/40109)
27
+ has_many target_name.tableize.to_sym, -> { try :roleless }, **options if
28
+ recursive?
24
29
 
25
30
  define_role_methods
26
31
  end
27
32
  end
28
33
  end
29
34
 
30
- def self_targeted? = target == owner
35
+ def recursive? = target == owner
31
36
 
32
37
  private
33
38
 
@@ -40,6 +45,29 @@ module AdjustableSchema
40
45
  end
41
46
  end
42
47
 
48
+ def define_scopes
49
+ name = relationships_name
50
+
51
+ {
52
+ name_for_any => -> { where.associated name },
53
+ name_for_none => -> { where.missing name },
54
+ }
55
+ .reject { owner.singleton_class.method_defined? _1 }
56
+ .each { owner.scope _1, _2 }
57
+ end
58
+
59
+ def define_methods
60
+ name = self.name
61
+
62
+ {
63
+ name_for_any => -> { send(name).any? },
64
+ name_for_none => -> { send(name).none? },
65
+ }
66
+ .transform_keys {"#{_1}?" }
67
+ .reject { owner.method_defined? _1 }
68
+ .each { owner.define_method _1, &_2 }
69
+ end
70
+
43
71
  def define_role_methods
44
72
  name = self.name
45
73
 
@@ -19,11 +19,11 @@ module AdjustableSchema
19
19
  end
20
20
  end
21
21
 
22
- def self_related = config :self_related
22
+ def self = config :self
23
23
 
24
24
  def recursive
25
25
  config.values.to_h do
26
- [ _1[:self_related].to_s.pluralize.to_sym, _1[:recursive].to_sym ]
26
+ [ _1[:self].to_s.pluralize.to_sym, _1[:recursive].to_sym ]
27
27
  end
28
28
  end
29
29
 
@@ -7,16 +7,19 @@ module AdjustableSchema
7
7
  config.names = {
8
8
  associations: {
9
9
  source: {
10
- shortcut: :of,
11
- self_related: :child,
12
- recursive: :descendants,
10
+ shortcut: :of,
11
+ self: :child,
12
+ recursive: :descendants,
13
13
  },
14
14
  target: {
15
- shortcut: :to,
16
- self_related: :parent,
17
- recursive: :ancestors,
15
+ shortcut: :to,
16
+ self: :parent,
17
+ recursive: :ancestors,
18
18
  },
19
19
  },
20
20
  }
21
+
22
+ config.active_record
23
+ .automatically_invert_plural_associations = true
21
24
  end
22
25
  end
@@ -1,3 +1,3 @@
1
1
  module AdjustableSchema
2
- VERSION = '0.6.0'
2
+ VERSION = '0.7.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adjustable_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Senko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-20 00:00:00.000000000 Z
11
+ date: 2024-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -111,7 +111,7 @@ licenses:
111
111
  metadata:
112
112
  homepage_uri: https://github.com/Alexander-Senko/adjustable_schema
113
113
  source_code_uri: https://github.com/Alexander-Senko/adjustable_schema
114
- changelog_uri: https://github.com/Alexander-Senko/adjustable_schema/blob/v0.6.0/CHANGELOG.md
114
+ changelog_uri: https://github.com/Alexander-Senko/adjustable_schema/blob/v0.7.1/CHANGELOG.md
115
115
  post_install_message:
116
116
  rdoc_options: []
117
117
  require_paths:
@@ -127,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  - !ruby/object:Gem::Version
128
128
  version: '0'
129
129
  requirements: []
130
- rubygems_version: 3.5.5
130
+ rubygems_version: 3.5.6
131
131
  signing_key:
132
132
  specification_version: 4
133
133
  summary: Adjustable data schemas for Rails