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,9 @@
1
+ module Traits
2
+ class Model
3
+ module EssayShortcuts
4
+ def features
5
+ model_class.features
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,83 @@
1
+ module Traits
2
+ class Model
3
+ module Naming
4
+ # class User
5
+ # name => user
6
+ #
7
+ # class Assets::Photo
8
+ # name => assets/photo
9
+ #
10
+ def name
11
+ # Rails 4.1 doesn't support nested acronyms
12
+ #
13
+ # Rails 4.1 behaviour:
14
+ # 'Helpers::ViewHelper'.underscore => 'helpers/view_helper' - good
15
+ # 'CIHelper'.underscore => 'ci_helper' - good
16
+ # 'Helpers::CIHelper' => 'helpers/ci_helper' - good
17
+ #
18
+ # 'Helpers::ViewHelper' => 'helpers/view_helper' - good
19
+ # 'CIHelper' => 'cihelper' - good
20
+ # 'Helpers::CIHelper' => 'helpers/ci_helper' - bad!
21
+ #
22
+ # Newer Rails behaviour:
23
+ # 'Helpers::ViewHelper' => 'helpers/view_helper' - good
24
+ # 'CIHelper' => 'cihelper' - good
25
+ # 'Helpers::CIHelper' => 'helpers/cihelper' - good
26
+ #
27
+ @name ||= model_class.name.split('::').map(&:underscore).join('/')
28
+ end
29
+
30
+ # class User
31
+ # plural_name => users
32
+ #
33
+ # class Assets::Photo
34
+ # plural_name => assets/photos
35
+ #
36
+ def plural_name
37
+ @plural_name ||= name.pluralize
38
+ end
39
+
40
+ # class User
41
+ # resource_name => users
42
+ #
43
+ # class Assets::Photo
44
+ # resource_name => assets/photos
45
+ #
46
+ def resource_name
47
+ @resource_name ||= plural_name
48
+ end
49
+
50
+ # class User
51
+ # class_name => User
52
+ #
53
+ # class Assets::Photo
54
+ # class_name => Assets::Photo
55
+ #
56
+ def class_name
57
+ @class_name ||= model_class.name
58
+ end
59
+
60
+ # class User
61
+ # lookup_name => User
62
+ # lookup_name(:underscore) => user
63
+ #
64
+ # class Assets::Photo
65
+ # lookup_name => AssetsPhoto
66
+ # lookup_name(:underscore) => assets_photo
67
+ #
68
+ def lookup_name(namecase = :class)
69
+ namecase == :underscore ? name.gsub('/', '_') : class_name.gsub('::', '')
70
+ end
71
+
72
+ def to_hash
73
+ super.merge!(
74
+ name: name,
75
+ plural_name: plural_name,
76
+ class_name: class_name,
77
+ lookup_name: lookup_name,
78
+ resource_name: resource_name
79
+ )
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,15 @@
1
+ module Traits
2
+ class Model
3
+ module Polymorphism
4
+ def polymorphic_type
5
+ model_class.base_class.name.to_sym
6
+ end
7
+
8
+ def to_hash
9
+ super.merge!(
10
+ polymorphic_type: polymorphic_type
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ module Traits
2
+ class Model
3
+ module Querying
4
+ def primary_key_name
5
+ model_class.primary_key.to_sym
6
+ end
7
+
8
+ def arel
9
+ model_class.arel_table
10
+ end
11
+
12
+ def connection
13
+ model_class.connection
14
+ end
15
+
16
+ def table_name
17
+ model_class.table_name
18
+ end
19
+
20
+ def quoted_table_name
21
+ connection.quote_table_name(model_class.table_name)
22
+ end
23
+
24
+ def to_hash
25
+ super.merge!(
26
+ table_name: table_name,
27
+ quoted_table_name: quoted_table_name,
28
+ polymorphic_type: polymorphic_type,
29
+ primary_key_name: primary_key_name
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ module Traits
2
+ class Model
3
+ module STI
4
+ def uses_sti?
5
+ sti_base? || sti_derived?
6
+ end
7
+
8
+ def sti_base?
9
+ model_class.descends_from_active_record? && model_class.subclasses.any? do |subclass|
10
+ subclass.superclass == model_class
11
+ end
12
+ end
13
+
14
+ def sti_derived?
15
+ !model_class.descends_from_active_record?
16
+ end
17
+
18
+ def sti_attribute_name
19
+ model_class.inheritance_column.to_sym
20
+ end
21
+
22
+ def sti_chain
23
+ model_class = self.model_class
24
+ chain = [model_class]
25
+ until model_class.superclass == ActiveRecord::Base do
26
+ model_class = model_class.superclass
27
+ chain.unshift(model_class)
28
+ end
29
+ chain
30
+ end
31
+
32
+ def to_hash
33
+ super.merge!(
34
+ sti_base: sti_base?,
35
+ sti_derived: sti_derived?,
36
+ sti_attribute_name: sti_attribute_name
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,51 @@
1
+ module Traits
2
+ module DescendantsListing
3
+ def active_record_descendants_loaded?
4
+ !!@ar_descendants
5
+ end
6
+
7
+ def load_active_record_descendants!
8
+ # Railties are not necessary here
9
+ # Source: http://stackoverflow.com/questions/6497834/differences-between-railties-and-engines-in-ruby-on-rails-3
10
+ Rails::Engine.subclasses.map(&:instance).each { |i| i.eager_load! }
11
+ Rails.application.eager_load!
12
+ end
13
+
14
+ def invalidate_loaded_active_record_descendants!
15
+ @ar_descendants = nil
16
+ end
17
+
18
+ def active_record_descendants
19
+ @ar_descendants ||= begin
20
+ load_active_record_descendants!
21
+ descendants = ActiveRecord::Base.descendants
22
+
23
+ descendants.reject! { |ar| excluded_active_record_descendant?(ar) }
24
+ descendants
25
+ end
26
+ end
27
+
28
+ def excluded_active_record_descendant?(model_class)
29
+ rules_for_excluding_active_records.any? do |rule|
30
+ case rule
31
+ when Regexp then model_class.name =~ rule
32
+ when String then model_class.name == rule
33
+ when Class then model_class == rule
34
+ else false
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ extend DescendantsListing
41
+
42
+ # TODO Give a better name
43
+ mattr_accessor :rules_for_excluding_active_records
44
+
45
+ self.rules_for_excluding_active_records = [
46
+ 'Globalize::ActiveRecord::Translation',
47
+ 'ActiveRecord::SchemaMigration',
48
+ /\AHABTM/,
49
+ /::Translation\Z/
50
+ ]
51
+ end
@@ -0,0 +1,13 @@
1
+ module Traits
2
+ class Engine < Rails::Engine
3
+ isolate_namespace Traits
4
+
5
+ config.before_initialize do |app|
6
+ unless app.config.cache_classes
7
+ ActionDispatch::Reloader.to_prepare do
8
+ Traits.invalidate_loaded_active_record_descendants!
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,52 @@
1
+ module Traits
2
+ class List
3
+ include Enumerable
4
+
5
+ def initialize(list = [])
6
+ @list = list
7
+ end
8
+
9
+ def filter(hash)
10
+ select do |attr|
11
+ hash.all? do |method, expected|
12
+ returned = attr.send(method)
13
+ if expected.is_a?(Array)
14
+ expected.include?(returned)
15
+ else
16
+ returned == expected
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def first_where(hash)
23
+ find do |attr|
24
+ hash.all? do |method, expected|
25
+ returned = attr.send(method)
26
+ if expected.is_a?(Array)
27
+ expected.include?(returned)
28
+ else
29
+ returned == expected
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def [](arg)
36
+ by_name(arg)
37
+ end
38
+
39
+ def each(&block)
40
+ @list.each(&block)
41
+ end
42
+
43
+ def by_name(name)
44
+ name = name.to_s.to_sym
45
+ find { |attr| attr.name == name }
46
+ end
47
+
48
+ def to_hash
49
+ each_with_object({}) { |item, memo| memo[item.name] = item.to_hash }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,78 @@
1
+ module Traits
2
+ class Model < Base
3
+ end
4
+ end
5
+
6
+ require 'traits/concerns/model/naming'
7
+ require 'traits/concerns/model/polymorphism'
8
+ require 'traits/concerns/model/sti'
9
+ require 'traits/concerns/model/querying'
10
+ require 'traits/concerns/model/essay_shortcuts'
11
+
12
+ module Traits
13
+ class Model
14
+ include Naming
15
+ include STI
16
+ include Polymorphism
17
+ include Querying
18
+ include EssayShortcuts
19
+
20
+ attr_reader :model_class
21
+
22
+ alias active_record model_class
23
+ alias klass model_class
24
+
25
+ def initialize(model_class)
26
+ @model_class = model_class
27
+ end
28
+
29
+ def attributes
30
+ @attributes ||= inspect_attributes
31
+ end
32
+
33
+ def associations
34
+ @associations ||= inspect_associations
35
+ end
36
+
37
+ def to_s
38
+ class_name
39
+ end
40
+
41
+ def to_hash
42
+ super.merge!(
43
+ attributes: attributes.to_hash,
44
+ associations: associations.to_hash
45
+ )
46
+ end
47
+
48
+ protected
49
+
50
+ # TODO Store, Storage, Virtual attributes
51
+ def inspect_attributes
52
+ columns = model_class.columns_hash.values
53
+
54
+ if features.translates_with_globalize?
55
+ globalize = features.globalize
56
+ tr_class = globalize.model_class_for_translations
57
+ tr_columns_hash = tr_class.columns_hash
58
+
59
+ columns += globalize.translated_attribute_names.map do |el|
60
+ tr_columns_hash[el.to_s]
61
+ end
62
+ end
63
+
64
+ list = columns.map do |column|
65
+ Traits::Attribute.new(model_class, column)
66
+ end
67
+
68
+ Traits::List.new(list)
69
+ end
70
+
71
+ def inspect_associations
72
+ list = model_class.reflections.map do |pair|
73
+ Traits::Association.new(model_class, pair.last)
74
+ end
75
+ Traits::List.new(list)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,37 @@
1
+ module Traits
2
+ class UnidentifiedModelReference < StandardError
3
+ end
4
+
5
+ module Utilities
6
+ def retrieve_model_class(obj)
7
+ if active_record_descendant?(obj)
8
+ obj
9
+
10
+ elsif active_record_instance?(obj)
11
+ obj.class
12
+
13
+ else
14
+ case obj
15
+ when Symbol then obj.to_s.camelize.constantize
16
+ when String then obj.camelize.constantize
17
+ when Model then obj.model_class
18
+ else raise UnidentifiedModelReference, obj
19
+ end
20
+ end
21
+ end
22
+
23
+ def active_record_descendant?(obj)
24
+ obj.kind_of?(Class) && obj < ActiveRecord::Base
25
+ end
26
+
27
+ def active_record_instance?(obj)
28
+ obj.kind_of?(ActiveRecord::Base)
29
+ end
30
+
31
+ def valid_active_record_identifier?(id)
32
+ id.kind_of?(String) || id.kind_of?(Numeric)
33
+ end
34
+ end
35
+
36
+ extend Utilities
37
+ end
@@ -0,0 +1,3 @@
1
+ module Traits
2
+ VERSION = '1.0.0'
3
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-traits
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Yaroslav Konoplov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: essay
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: essay-globalize
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: essay-carrierwave
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.0.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.0.0
83
+ description: Type information of ActiveRecord models, attributes and associations
84
+ email:
85
+ - yaroslav@inbox.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - activerecord-traits.gemspec
96
+ - lib/activerecord-traits.rb
97
+ - lib/traits/association.rb
98
+ - lib/traits/attribute.rb
99
+ - lib/traits/base.rb
100
+ - lib/traits/concerns/association/essay_shortcuts.rb
101
+ - lib/traits/concerns/association/intermediate.rb
102
+ - lib/traits/concerns/association/join.rb
103
+ - lib/traits/concerns/association/macro.rb
104
+ - lib/traits/concerns/association/members.rb
105
+ - lib/traits/concerns/association/naming.rb
106
+ - lib/traits/concerns/association/polymorphism.rb
107
+ - lib/traits/concerns/association/read_only.rb
108
+ - lib/traits/concerns/association/through.rb
109
+ - lib/traits/concerns/attribute/essay_shortcuts.rb
110
+ - lib/traits/concerns/attribute/key.rb
111
+ - lib/traits/concerns/attribute/naming.rb
112
+ - lib/traits/concerns/attribute/polymorphism.rb
113
+ - lib/traits/concerns/attribute/querying.rb
114
+ - lib/traits/concerns/attribute/sti.rb
115
+ - lib/traits/concerns/attribute/type.rb
116
+ - lib/traits/concerns/model/essay_shortcuts.rb
117
+ - lib/traits/concerns/model/naming.rb
118
+ - lib/traits/concerns/model/polymorphism.rb
119
+ - lib/traits/concerns/model/querying.rb
120
+ - lib/traits/concerns/model/sti.rb
121
+ - lib/traits/descendants_listing.rb
122
+ - lib/traits/engine.rb
123
+ - lib/traits/list.rb
124
+ - lib/traits/model.rb
125
+ - lib/traits/utilities.rb
126
+ - lib/traits/version.rb
127
+ homepage: http://github.com/yivo/activerecord-traits
128
+ licenses:
129
+ - MIT
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 2.4.8
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Type information of ActiveRecord models, attributes and associations
151
+ test_files: []