adjustable_schema 0.6.0 → 0.7.1

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