activerecord-traits 1.0.0

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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +8 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +62 -0
  6. data/Rakefile +1 -0
  7. data/activerecord-traits.gemspec +24 -0
  8. data/lib/activerecord-traits.rb +44 -0
  9. data/lib/traits/association.rb +39 -0
  10. data/lib/traits/attribute.rb +52 -0
  11. data/lib/traits/base.rb +7 -0
  12. data/lib/traits/concerns/association/essay_shortcuts.rb +9 -0
  13. data/lib/traits/concerns/association/intermediate.rb +55 -0
  14. data/lib/traits/concerns/association/join.rb +89 -0
  15. data/lib/traits/concerns/association/macro.rb +43 -0
  16. data/lib/traits/concerns/association/members.rb +35 -0
  17. data/lib/traits/concerns/association/naming.rb +15 -0
  18. data/lib/traits/concerns/association/polymorphism.rb +118 -0
  19. data/lib/traits/concerns/association/read_only.rb +17 -0
  20. data/lib/traits/concerns/association/through.rb +72 -0
  21. data/lib/traits/concerns/attribute/essay_shortcuts.rb +9 -0
  22. data/lib/traits/concerns/attribute/key.rb +38 -0
  23. data/lib/traits/concerns/attribute/naming.rb +20 -0
  24. data/lib/traits/concerns/attribute/polymorphism.rb +28 -0
  25. data/lib/traits/concerns/attribute/querying.rb +14 -0
  26. data/lib/traits/concerns/attribute/sti.rb +15 -0
  27. data/lib/traits/concerns/attribute/type.rb +59 -0
  28. data/lib/traits/concerns/model/essay_shortcuts.rb +9 -0
  29. data/lib/traits/concerns/model/naming.rb +83 -0
  30. data/lib/traits/concerns/model/polymorphism.rb +15 -0
  31. data/lib/traits/concerns/model/querying.rb +34 -0
  32. data/lib/traits/concerns/model/sti.rb +41 -0
  33. data/lib/traits/descendants_listing.rb +51 -0
  34. data/lib/traits/engine.rb +13 -0
  35. data/lib/traits/list.rb +52 -0
  36. data/lib/traits/model.rb +78 -0
  37. data/lib/traits/utilities.rb +37 -0
  38. data/lib/traits/version.rb +3 -0
  39. metadata +151 -0
@@ -0,0 +1,43 @@
1
+ module Traits
2
+ class Association
3
+ module Macro
4
+ delegate :macro, to: :reflection
5
+
6
+ def short_macro
7
+ habtm? ? :habtm : macro
8
+ end
9
+
10
+ def has_and_belongs_to_many?
11
+ macro == :has_and_belongs_to_many
12
+ end
13
+
14
+ alias habtm? has_and_belongs_to_many?
15
+
16
+ def has_many?
17
+ macro == :has_many
18
+ end
19
+
20
+ def has_one?
21
+ macro == :has_one
22
+ end
23
+
24
+ def belongs_to?
25
+ macro == :belongs_to
26
+ end
27
+
28
+ def to_many?
29
+ has_and_belongs_to_many? || has_many?
30
+ end
31
+
32
+ alias collection? to_many?
33
+
34
+ def to_one?
35
+ belongs_to? || has_one?
36
+ end
37
+
38
+ def to_hash
39
+ super.merge!(macro: macro, collection: collection?)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,35 @@
1
+ module Traits
2
+ class Association
3
+ module Members
4
+ def from
5
+ @from_class.traits
6
+ end
7
+
8
+ # Returns the actual association establisher class
9
+ def from_class
10
+ @from_class
11
+ end
12
+
13
+ # Returns the actual associated class
14
+ def to
15
+ reflection.klass.traits unless polymorphic?
16
+ end
17
+
18
+ def to_class
19
+ reflection.klass unless polymorphic?
20
+ end
21
+
22
+ def self_to_self?
23
+ from_class == to_class
24
+ end
25
+
26
+ def to_hash
27
+ super.merge!(
28
+ from: from.name,
29
+ to: to.try(:name),
30
+ self_to_self: self_to_self?
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ module Traits
2
+ class Association
3
+ module Naming
4
+ delegate :name, to: :reflection
5
+
6
+ def plural_name
7
+ reflection.plural_name.to_sym
8
+ end
9
+
10
+ def to_hash
11
+ super.merge!(name: name, plural_name: plural_name)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,118 @@
1
+ module Traits
2
+ class Association
3
+ module Polymorphism
4
+ # class Picture
5
+ # belongs_to :imageable, polymorphic: true
6
+ #
7
+ # class Toy
8
+ # has_one :picture, as: :imageable
9
+ #
10
+ # picture_traits.associations[:imageable].polymorphic? => true
11
+ # toy_traits.associations[:picture].polymorphic? => false
12
+ #
13
+ def polymorphic?
14
+ belongs_to? && reflection.options[:polymorphic] == true
15
+ end
16
+
17
+ alias accepts_various_classes? polymorphic?
18
+
19
+ # Example 1:
20
+ # class Picture
21
+ # belongs_to :imageable, polymorphic: true
22
+ #
23
+ # class Toy
24
+ # has_one :picture, as: :imageable
25
+ #
26
+ # picture_traits.associations[:imageable].accepted_classes_through_polymorphism
27
+ # => [Toy]
28
+ #
29
+ # toy_traits.associations[:picture].accepted_classes_through_polymorphism
30
+ # => nil
31
+ #
32
+ # Example 2:
33
+ # class Picture
34
+ # belongs_to :imageable, polymorphic: true
35
+ #
36
+ # class Present
37
+ # has_one :picture, as: :imageable
38
+ #
39
+ # class Toy < Present
40
+ # class VideoGame < Present
41
+ # class Car < Present
42
+ #
43
+ # picture_traits.associations[:imageable].accepted_classes_through_polymorphism
44
+ # => [Car, Present, Toy, VideoGame]
45
+ #
46
+ # Note that items in list are sorted by class name
47
+ #
48
+ def accepted_classes_through_polymorphism
49
+ if polymorphic?
50
+ classes = []
51
+ attr_name = attribute_name_for_polymorphic_type
52
+ this_class = from_class
53
+
54
+ Traits.active_record_descendants.each do |model_class|
55
+ # Skip current model and models which are STI derived
56
+ next if model_class <= this_class # Means is or derived from current model
57
+
58
+ model_class.traits.associations.each do |assoc|
59
+ if assoc.attribute_name_for_polymorphic_type == attr_name
60
+ classes << assoc.from_class
61
+ end
62
+ end
63
+ end
64
+ classes.uniq.sort! { |l, r| l.to_s <=> r.to_s }
65
+ end
66
+ end
67
+
68
+ # class Picture
69
+ # belongs_to :imageable, polymorphic: true
70
+ #
71
+ # class Toy
72
+ # has_one :picture, as: :imageable
73
+ #
74
+ # picture_traits.associations[:imageable].paired_through_polymorphism? => false
75
+ # toy_traits.associations[:picture].paired_through_polymorphism? => true
76
+ #
77
+ def paired_through_polymorphism?
78
+ reflection.type.present?
79
+ end
80
+
81
+ # class Picture
82
+ # belongs_to :imageable, polymorphic: true
83
+ #
84
+ # class Toy
85
+ # has_one :picture, as: :imageable
86
+ #
87
+ # picture_traits.associations[:imageable].polymorphic_type_attribute_name => :imageable_type
88
+ # toy_traits.associations[:picture].polymorphic_type_attribute_name => :imageable_type
89
+ #
90
+ def attribute_name_for_polymorphic_type
91
+ if polymorphic?
92
+ reflection.foreign_type.to_sym
93
+ elsif paired_through_polymorphism?
94
+ reflection.type.to_sym
95
+ end
96
+ end
97
+
98
+ def attribute_for_polymorphic_type
99
+ if polymorphic?
100
+ from.attributes[attribute_for_polymorphic_type]
101
+ elsif paired_through_polymorphism?
102
+ to.attributes[reflection.foreign_type]
103
+ end
104
+ end
105
+
106
+ def to_hash
107
+ accepted_classes = accepted_classes_through_polymorphism
108
+ super.merge!(
109
+ polymorphic: polymorphic?,
110
+ paired_through_polymorphism: paired_through_polymorphism?,
111
+
112
+ attribute_name_for_polymorphic_type: attribute_name_for_polymorphic_type,
113
+ accepted_classes_through_polymorphism: accepted_classes.try(:map) { |el| el.traits.name }
114
+ )
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,17 @@
1
+ module Traits
2
+ class Association
3
+ module ReadOnly
4
+ def mutable?
5
+ !through? || source_association.macro == :belongs_to
6
+ end
7
+
8
+ def readonly?
9
+ not mutable?
10
+ end
11
+
12
+ def to_hash
13
+ super.merge!(mutable: mutable?)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,72 @@
1
+ module Traits
2
+ class Association
3
+ module Through
4
+ def through?
5
+ reflection.through_reflection.present?
6
+ end
7
+
8
+ def through
9
+ if through?
10
+ reflection.through_reflection.klass.traits
11
+ end
12
+ end
13
+
14
+ def through_class
15
+ if through?
16
+ through.active_record
17
+ end
18
+ end
19
+
20
+ def through_association
21
+ if through?
22
+ from.associations[reflection.through_reflection.name]
23
+ end
24
+ end
25
+
26
+ def source_association
27
+ if through?
28
+ through.associations[reflection.source_reflection.name]
29
+ end
30
+ end
31
+
32
+ def source_association_name
33
+ source_association.try(:name)
34
+ end
35
+
36
+ def through_association_name
37
+ if through?
38
+ through_association.name
39
+ end
40
+ end
41
+
42
+ def through_table_name
43
+ if through?
44
+ through_association.to_table_name
45
+ end
46
+ end
47
+
48
+ def through_to_key_name
49
+ if through?
50
+ through_association.to_key_name
51
+ end
52
+ end
53
+
54
+ def through_from_key_name
55
+ if through?
56
+ source_association.from_key_name
57
+ end
58
+ end
59
+
60
+ def to_hash
61
+ super.merge!(
62
+ through: through.try(:name),
63
+ through_association: through_association_name,
64
+ source_association: source_association_name,
65
+ through_table_name: through_table_name,
66
+ through_to_key_name: through_to_key_name,
67
+ through_from_key_name: through_from_key_name
68
+ )
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,9 @@
1
+ module Traits
2
+ class Attribute
3
+ module EssayShortcuts
4
+ def features
5
+ model_class.attribute_features[name]
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ module Traits
2
+ class Attribute
3
+ module Key
4
+ def primary_key?
5
+ column_definition.try(:primary) ||
6
+ model_class.primary_key == column_definition.name ||
7
+ type == :primary_key
8
+ end
9
+
10
+ def foreign_key?
11
+ attr_name = name
12
+ attr_translates = model_class.attribute_features[attr_name].translates_with_globalize?
13
+
14
+ model.associations.any? do |assoc|
15
+ if assoc.belongs_to?
16
+ if attr_translates && assoc.features.translates_with_globalize?
17
+ assoc.features.globalize_translatable.translation_from_key_name == attr_name
18
+ else
19
+ assoc.from_key_name == attr_name
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def key?
26
+ primary_key? || foreign_key? || polymorphic_key?
27
+ end
28
+
29
+ def to_hash
30
+ super.merge!(
31
+ key: key?,
32
+ primary_key: primary_key?,
33
+ foreign_key: foreign_key?
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,20 @@
1
+ module Traits
2
+ class Attribute
3
+ module Naming
4
+ def name
5
+ column_definition.name.to_sym
6
+ end
7
+
8
+ def quoted_name
9
+ model_class.connection.quote_column_name(name)
10
+ end
11
+
12
+ def to_hash
13
+ super.merge!(
14
+ name: name,
15
+ quoted_name: quoted_name
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ module Traits
2
+ class Attribute
3
+ module Polymorphism
4
+ def polymorphic_key?
5
+ model.associations
6
+ .first_where(
7
+ polymorphic?: true,
8
+ from_key_name: name
9
+ ).present?
10
+ end
11
+
12
+ def polymorphic_type?
13
+ model.associations
14
+ .first_where(
15
+ polymorphic?: true,
16
+ attribute_name_for_polymorphic_type: name
17
+ ).present?
18
+ end
19
+
20
+ def to_hash
21
+ super.merge!(
22
+ polymorphic_key: polymorphic_key?,
23
+ polymorphic_type: polymorphic_type?
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ module Traits
2
+ class Attribute
3
+ module Querying
4
+ def arel
5
+ table = if features.translates_with_globalize?
6
+ model_class.features.globalize.translation_model_class.arel_table
7
+ else
8
+ model.arel
9
+ end
10
+ table[name]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module Traits
2
+ class Attribute
3
+ module STI
4
+ def sti_type?
5
+ (model.sti_base? || model.sti_derived?) and name == model.sti_attribute_name
6
+ end
7
+
8
+ def to_hash
9
+ super.merge!(
10
+ sti_type: sti_type?
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,59 @@
1
+ module Traits
2
+ class Attribute
3
+ module Type
4
+ def type
5
+ column_definition.type.to_sym
6
+ end
7
+
8
+ def string?
9
+ type == :string
10
+ end
11
+
12
+ def text?
13
+ type == :text
14
+ end
15
+
16
+ def binary?
17
+ type == :binary
18
+ end
19
+
20
+ def big?
21
+ text? || binary?
22
+ end
23
+
24
+ def integer?
25
+ type == :integer
26
+ end
27
+
28
+ def float?
29
+ type == :float
30
+ end
31
+
32
+ def decimal?
33
+ type == :decimal
34
+ end
35
+
36
+ def real?
37
+ float? || decimal?
38
+ end
39
+
40
+ def number?
41
+ integer? || float? || decimal?
42
+ end
43
+
44
+ def datetime?
45
+ type == :datetime
46
+ end
47
+
48
+ def active_record_timestamp?
49
+ datetime? && (name == :created_at || name == :updated_at || name == :deleted_at)
50
+ end
51
+
52
+ def to_hash
53
+ super.merge!(
54
+ type: type
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end