activerecord-traits 1.0.0

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