who_am_i 0.0.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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rubocop.yml +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +5 -0
  6. data/Appraisals +31 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +58 -0
  10. data/Rakefile +11 -0
  11. data/bin/appraisal +18 -0
  12. data/bin/console +14 -0
  13. data/bin/m +18 -0
  14. data/bin/rake +18 -0
  15. data/bin/rubocop +18 -0
  16. data/bin/setup +8 -0
  17. data/gemfiles/rails32.gemfile +8 -0
  18. data/gemfiles/rails32.gemfile.lock +89 -0
  19. data/gemfiles/rails40.gemfile +8 -0
  20. data/gemfiles/rails40.gemfile.lock +94 -0
  21. data/gemfiles/rails41.gemfile +8 -0
  22. data/gemfiles/rails41.gemfile.lock +93 -0
  23. data/gemfiles/rails42.gemfile +8 -0
  24. data/gemfiles/rails42.gemfile.lock +91 -0
  25. data/gemfiles/rails50.gemfile +8 -0
  26. data/gemfiles/rails50.gemfile.lock +90 -0
  27. data/gemfiles/rails51.gemfile +8 -0
  28. data/gemfiles/rails51.gemfile.lock +90 -0
  29. data/lib/who_am_i/comment.rb +56 -0
  30. data/lib/who_am_i/error.rb +4 -0
  31. data/lib/who_am_i/extracted_class.rb +50 -0
  32. data/lib/who_am_i/function/annotate_models.rb +35 -0
  33. data/lib/who_am_i/function/compute_comment.rb +13 -0
  34. data/lib/who_am_i/function/compute_content.rb +15 -0
  35. data/lib/who_am_i/function/extract_model_data.rb +14 -0
  36. data/lib/who_am_i/function/get_tables.rb +11 -0
  37. data/lib/who_am_i/function/load_config.rb +45 -0
  38. data/lib/who_am_i/function/ls.rb +13 -0
  39. data/lib/who_am_i/function/main.rb +16 -0
  40. data/lib/who_am_i/function/parse_model.rb +14 -0
  41. data/lib/who_am_i/function/remove_annotation.rb +27 -0
  42. data/lib/who_am_i/function/resolve_active_record.rb +40 -0
  43. data/lib/who_am_i/function/resolve_class_relationships.rb +63 -0
  44. data/lib/who_am_i/function/resolve_tables.rb +25 -0
  45. data/lib/who_am_i/function/write_model.rb +16 -0
  46. data/lib/who_am_i/rake.rb +12 -0
  47. data/lib/who_am_i/refinement/yield_self.rb +17 -0
  48. data/lib/who_am_i/table_column_info.rb +50 -0
  49. data/lib/who_am_i/table_info.rb +31 -0
  50. data/lib/who_am_i/text_table.rb +49 -0
  51. data/lib/who_am_i/version.rb +3 -0
  52. data/lib/who_am_i/walker.rb +113 -0
  53. data/lib/who_am_i.rb +29 -0
  54. data/who_am_i.gemspec +37 -0
  55. metadata +285 -0
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ who_am_i (0.0.0)
5
+ activerecord (>= 3.2, < 5.2)
6
+ parser
7
+ proc_party
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activemodel (5.0.4)
13
+ activesupport (= 5.0.4)
14
+ activerecord (5.0.4)
15
+ activemodel (= 5.0.4)
16
+ activesupport (= 5.0.4)
17
+ arel (~> 7.0)
18
+ activesupport (5.0.4)
19
+ concurrent-ruby (~> 1.0, >= 1.0.2)
20
+ i18n (~> 0.7)
21
+ minitest (~> 5.1)
22
+ tzinfo (~> 1.1)
23
+ appraisal (2.2.0)
24
+ bundler
25
+ rake
26
+ thor (>= 0.14.0)
27
+ arel (7.1.4)
28
+ ast (2.3.0)
29
+ byebug (9.0.6)
30
+ coderay (1.1.1)
31
+ concurrent-ruby (1.0.5)
32
+ foreigner (1.7.4)
33
+ activerecord (>= 3.0.0)
34
+ i18n (0.8.6)
35
+ m (1.5.1)
36
+ method_source (>= 0.6.7)
37
+ rake (>= 0.9.2.2)
38
+ method_source (0.8.2)
39
+ minitest (5.10.2)
40
+ parallel (1.11.2)
41
+ parser (2.4.0.0)
42
+ ast (~> 2.2)
43
+ powerpack (0.1.1)
44
+ proc_party (0.2.0)
45
+ pry (0.10.4)
46
+ coderay (~> 1.1.0)
47
+ method_source (~> 0.8.1)
48
+ slop (~> 3.4)
49
+ pry-byebug (3.4.2)
50
+ byebug (~> 9.0)
51
+ pry (~> 0.10)
52
+ rainbow (2.2.2)
53
+ rake
54
+ rake (10.5.0)
55
+ rubocop (0.49.1)
56
+ parallel (~> 1.10)
57
+ parser (>= 2.3.3.1, < 3.0)
58
+ powerpack (~> 0.1)
59
+ rainbow (>= 1.99.1, < 3.0)
60
+ ruby-progressbar (~> 1.7)
61
+ unicode-display_width (~> 1.0, >= 1.0.1)
62
+ ruby-progressbar (1.8.1)
63
+ slop (3.6.0)
64
+ sqlite3 (1.3.13)
65
+ the_bath_of_zahn (0.0.2)
66
+ thor (0.19.4)
67
+ thread_safe (0.3.6)
68
+ tzinfo (1.2.3)
69
+ thread_safe (~> 0.1)
70
+ unicode-display_width (1.3.0)
71
+
72
+ PLATFORMS
73
+ ruby
74
+
75
+ DEPENDENCIES
76
+ activerecord (= 5.0.4)
77
+ appraisal
78
+ bundler (~> 1.15)
79
+ foreigner
80
+ m (~> 1.5)
81
+ minitest (~> 5.10)
82
+ pry-byebug
83
+ rake (~> 10.0)
84
+ rubocop
85
+ sqlite3
86
+ the_bath_of_zahn
87
+ who_am_i!
88
+
89
+ BUNDLED WITH
90
+ 1.15.4
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "= 5.1.2"
6
+ gem "minitest", "~> 5.10"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ who_am_i (0.0.0)
5
+ activerecord (>= 3.2, < 5.2)
6
+ parser
7
+ proc_party
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activemodel (5.1.2)
13
+ activesupport (= 5.1.2)
14
+ activerecord (5.1.2)
15
+ activemodel (= 5.1.2)
16
+ activesupport (= 5.1.2)
17
+ arel (~> 8.0)
18
+ activesupport (5.1.2)
19
+ concurrent-ruby (~> 1.0, >= 1.0.2)
20
+ i18n (~> 0.7)
21
+ minitest (~> 5.1)
22
+ tzinfo (~> 1.1)
23
+ appraisal (2.2.0)
24
+ bundler
25
+ rake
26
+ thor (>= 0.14.0)
27
+ arel (8.0.0)
28
+ ast (2.3.0)
29
+ byebug (9.0.6)
30
+ coderay (1.1.1)
31
+ concurrent-ruby (1.0.5)
32
+ foreigner (1.7.4)
33
+ activerecord (>= 3.0.0)
34
+ i18n (0.8.6)
35
+ m (1.5.1)
36
+ method_source (>= 0.6.7)
37
+ rake (>= 0.9.2.2)
38
+ method_source (0.8.2)
39
+ minitest (5.10.2)
40
+ parallel (1.11.2)
41
+ parser (2.4.0.0)
42
+ ast (~> 2.2)
43
+ powerpack (0.1.1)
44
+ proc_party (0.2.0)
45
+ pry (0.10.4)
46
+ coderay (~> 1.1.0)
47
+ method_source (~> 0.8.1)
48
+ slop (~> 3.4)
49
+ pry-byebug (3.4.2)
50
+ byebug (~> 9.0)
51
+ pry (~> 0.10)
52
+ rainbow (2.2.2)
53
+ rake
54
+ rake (10.5.0)
55
+ rubocop (0.49.1)
56
+ parallel (~> 1.10)
57
+ parser (>= 2.3.3.1, < 3.0)
58
+ powerpack (~> 0.1)
59
+ rainbow (>= 1.99.1, < 3.0)
60
+ ruby-progressbar (~> 1.7)
61
+ unicode-display_width (~> 1.0, >= 1.0.1)
62
+ ruby-progressbar (1.8.1)
63
+ slop (3.6.0)
64
+ sqlite3 (1.3.13)
65
+ the_bath_of_zahn (0.0.2)
66
+ thor (0.19.4)
67
+ thread_safe (0.3.6)
68
+ tzinfo (1.2.3)
69
+ thread_safe (~> 0.1)
70
+ unicode-display_width (1.3.0)
71
+
72
+ PLATFORMS
73
+ ruby
74
+
75
+ DEPENDENCIES
76
+ activerecord (= 5.1.2)
77
+ appraisal
78
+ bundler (~> 1.15)
79
+ foreigner
80
+ m (~> 1.5)
81
+ minitest (~> 5.10)
82
+ pry-byebug
83
+ rake (~> 10.0)
84
+ rubocop
85
+ sqlite3
86
+ the_bath_of_zahn
87
+ who_am_i!
88
+
89
+ BUNDLED WITH
90
+ 1.15.4
@@ -0,0 +1,56 @@
1
+ module WhoAmI
2
+ class Comment
3
+ def initialize(table_name:)
4
+ @model_info = TableInfo.new(table_name)
5
+ end
6
+
7
+ def output
8
+ output_header + output_schema + output_indices
9
+ end
10
+
11
+ private
12
+
13
+ def output_header
14
+ "# == Schema Info\n" \
15
+ "#\n" \
16
+ "# Table name: #{@model_info.table_name}\n" \
17
+ "#\n"
18
+ end
19
+
20
+ def output_schema
21
+ if @model_info.columns.none?
22
+ return ""
23
+ end
24
+
25
+ tt = TextTable.new(join: " ", prefix: "# ")
26
+
27
+ @model_info.columns.each do |column_info|
28
+ tt.push([
29
+ column_info.name,
30
+ column_info.type,
31
+ column_info.attributes.join(", "),
32
+ ])
33
+ end
34
+
35
+ tt.to_s + "#\n"
36
+ end
37
+
38
+ def output_indices
39
+ if @model_info.indices.none?
40
+ return ""
41
+ end
42
+
43
+ tt = TextTable.new(join: " ", prefix: "# ")
44
+
45
+ @model_info.indices.each do |index|
46
+ tt.push([index.name, "(#{index.columns.join(", ")})"])
47
+ end
48
+
49
+ header =
50
+ "# Indices:\n" \
51
+ "#\n"
52
+
53
+ header + tt.to_s + "#\n"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,4 @@
1
+ module WhoAmI
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,50 @@
1
+ module WhoAmI
2
+ class ExtractedClass
3
+ attr_writer :name
4
+ attr_accessor :outerclass
5
+ attr_accessor :activerecord
6
+ attr_writer :claimed_superclass
7
+ attr_accessor :table_name
8
+ attr_accessor :model_filepath
9
+ attr_accessor :abstract_class
10
+ attr_accessor :resolved_superclass
11
+ attr_accessor :computed_header
12
+ attr_accessor :computed_content
13
+
14
+ def initialize(name,
15
+ outerclass: nil,
16
+ activerecord: false,
17
+ claimed_superclass: nil,
18
+ table_name: nil,
19
+ abstract_class: nil)
20
+ self.name = name
21
+ self.outerclass = outerclass
22
+ self.activerecord = activerecord
23
+ self.claimed_superclass = claimed_superclass
24
+ self.table_name = table_name
25
+ self.abstract_class = abstract_class
26
+ end
27
+
28
+ def activerecord?
29
+ !!@activerecord
30
+ end
31
+
32
+ def abstract_class?
33
+ !!@abstract_class
34
+ end
35
+
36
+ def claimed_superclass
37
+ @claimed_superclass.to_s
38
+ end
39
+
40
+ def class_name
41
+ if @name
42
+ "#{outerclass.class_name}::#{@name}"
43
+ else
44
+ ""
45
+ end
46
+ end
47
+
48
+ alias_method :to_s, :class_name
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ module WhoAmI
2
+ module Function
3
+ class AnnotateModels
4
+ using Refinement::YieldSelf
5
+ include ProcParty
6
+
7
+ def initialize(config, tables)
8
+ @config = config
9
+ @tables = tables
10
+ end
11
+
12
+ def call
13
+ paths
14
+ .flat_map(&Ls.new)
15
+ .flat_map(&ParseModel.new)
16
+ .tap(&ExtractModelData.new)
17
+ .yield_self(&ResolveTables.new)
18
+ .reject(&:abstract_class?)
19
+ .each(&ComputeComment.new)
20
+ .each(&ComputeContent.new)
21
+ .each(&WriteModel.new)
22
+ end
23
+
24
+ private
25
+
26
+ def paths
27
+ if @config.nil?
28
+ []
29
+ else
30
+ @config[:paths]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ module WhoAmI
2
+ module Function
3
+ class ComputeComment
4
+ include ProcParty
5
+
6
+ def call(extracted_class)
7
+ commenter =
8
+ WhoAmI::Comment.new(table_name: extracted_class.table_name)
9
+ extracted_class.computed_header = commenter.output
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module WhoAmI
2
+ module Function
3
+ class ComputeContent
4
+ include ProcParty
5
+
6
+ PATTERN = %r{\A(?:(?:^#.*?$)\n)*\n*(.*)}m
7
+
8
+ def call(extracted_class)
9
+ original_content = File.read(extracted_class.model_filepath)
10
+ content = PATTERN.match(original_content)
11
+ extracted_class.computed_content = content
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module WhoAmI
2
+ module Function
3
+ class ExtractModelData
4
+ using Refinement::YieldSelf
5
+ include ProcParty
6
+
7
+ def call(extracted_classes)
8
+ extracted_classes
9
+ .yield_self(&ResolveClassRelationships.new)
10
+ .yield_self(&ResolveActiveRecord.new)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module WhoAmI
2
+ module Function
3
+ class GetTables
4
+ include ProcParty
5
+
6
+ def call
7
+ ActiveRecord::Base.connection.tables
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ module WhoAmI
2
+ module Function
3
+ class LoadConfig
4
+ include ProcParty
5
+
6
+ def initialize(root)
7
+ @root = root
8
+ end
9
+
10
+ def call
11
+ hash = YAML.load_file(config_path)
12
+ deep_symbolize_hash(hash)
13
+ end
14
+
15
+ private
16
+
17
+ def config_path
18
+ @config_path ||=
19
+ if File.exist?(dotfile_path)
20
+ dotfile_path
21
+ elsif File.exist?(initializer_path)
22
+ initializer_path
23
+ else
24
+ raise WhoAmI::Error, "Configuration not found"
25
+ end
26
+ end
27
+
28
+ def dotfile_path
29
+ File.join(@root, ".who_am_i.yml")
30
+ end
31
+
32
+ def initializer_path
33
+ File.join(@root, "config", "initializers", "who_am_i.yml")
34
+ end
35
+
36
+ def deep_symbolize_hash(obj)
37
+ if obj.is_a?(Hash)
38
+ obj.map { |k, v| [k.to_sym, deep_symbolize_hash(v)] }.to_h
39
+ else
40
+ obj
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ module WhoAmI
2
+ module Function
3
+ class Ls
4
+ include ProcParty
5
+
6
+ def call(path)
7
+ glob = File.expand_path(path)
8
+
9
+ Dir[glob].select { |path| File.file?(path) }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module WhoAmI
2
+ module Function
3
+ class Main
4
+ def initialize(root)
5
+ @root = root
6
+ end
7
+
8
+ def call
9
+ config = LoadConfig.new(@root).call
10
+ tables = GetTables.new.call
11
+
12
+ AnnotateModels.new(config[:enabled][:models], tables).call
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module WhoAmI
2
+ module Function
3
+ class ParseModel
4
+ include ProcParty
5
+
6
+ def call(path)
7
+ walker = WhoAmI::Walker.new
8
+ extracted_classes = walker.read(path)
9
+
10
+ extracted_classes
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module WhoAmI
2
+ module Function
3
+ class RemoveAnnotation
4
+ def call(file_contents:)
5
+ if file_contents !~ /\A# == Schema Info/
6
+ return file_contents
7
+ end
8
+
9
+ keep_the_rest = false
10
+
11
+ kept_lines =
12
+ file_contents.lines.keep_if do |line|
13
+ if keep_the_rest
14
+ next true
15
+ elsif line[0] == "#"
16
+ next false
17
+ else
18
+ keep_the_rest = true
19
+ next false
20
+ end
21
+ end
22
+
23
+ kept_lines.join("")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module WhoAmI
2
+ module Function
3
+ class ResolveActiveRecord
4
+ include ProcParty
5
+
6
+ def call(object_space)
7
+ tree = {}
8
+ object_space.each do |_class_name, extracted_class|
9
+ superclass = extracted_class.resolved_superclass
10
+
11
+ tree[superclass] ||= Set.new
12
+ tree[superclass].add(extracted_class)
13
+ end
14
+
15
+ activerecord_family =
16
+ gather_family(object_space["::ActiveRecord::Base"], tree)
17
+
18
+ activerecord_family.each do |individual|
19
+ individual.activerecord = true
20
+ end
21
+
22
+ object_space.values - [object_space[""], object_space["::ActiveRecord::Base"]]
23
+ end
24
+
25
+ private
26
+
27
+ def gather_family(node, tree)
28
+ gather_family_helper(node, tree).flatten
29
+ end
30
+
31
+ def gather_family_helper(node, tree)
32
+ if tree[node].nil?
33
+ return node
34
+ end
35
+
36
+ [node] + tree[node].map { |child| gather_family_helper(child, tree) }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,63 @@
1
+ module WhoAmI
2
+ module Function
3
+ class ResolveClassRelationships
4
+ include ProcParty
5
+
6
+ def call(extracted_classes)
7
+ object_space =
8
+ extracted_classes
9
+ .map(&method(:self_and_outer_class))
10
+ .flatten
11
+ .compact
12
+ .uniq
13
+ .map { |x| [x.class_name, x] }
14
+ .to_h
15
+
16
+ object_space[""] ||= ExtractedClass.new(nil)
17
+
18
+ object_space["::ActiveRecord::Base"] =
19
+ ExtractedClass.new(
20
+ "ActiveRecord::Base",
21
+ outerclass: object_space[""]
22
+ )
23
+
24
+ object_space.each do |class_name, extracted_class|
25
+ possible_namespace_levels = class_name.split("::")[1..-2] || []
26
+
27
+ if extracted_class.claimed_superclass == "" || extracted_class.claimed_superclass == nil
28
+ next
29
+ end
30
+
31
+ (possible_namespace_levels.size + 1).times do
32
+ class_uri_parts =
33
+ possible_namespace_levels + [extracted_class.claimed_superclass]
34
+
35
+ class_uri = "::#{class_uri_parts.join("::")}"
36
+
37
+ if object_space[class_uri]
38
+ extracted_class.resolved_superclass = object_space[class_uri]
39
+ break
40
+ end
41
+
42
+ possible_namespace_levels.pop
43
+ end
44
+ end
45
+
46
+ object_space
47
+ end
48
+
49
+ private
50
+
51
+ def self_and_outer_class(extracted_class)
52
+ if extracted_class.nil?
53
+ return nil
54
+ end
55
+
56
+ [
57
+ extracted_class,
58
+ self_and_outer_class(extracted_class.outerclass)
59
+ ]
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ module WhoAmI
2
+ module Function
3
+ class ResolveTables
4
+ include ProcParty
5
+
6
+ def call(extracted_classes)
7
+ extracted_classes.each do |extracted_class|
8
+ if !extracted_class.table_name.nil?
9
+ next
10
+ end
11
+
12
+ extracted_class.table_name =
13
+ if Kernel.const_defined?(extracted_class.class_name)
14
+ Kernel.const_get(extracted_class.class_name).table_name
15
+ else
16
+ extracted_class.class_name
17
+ .underscore
18
+ .pluralize
19
+ .sub(%r{\A/}, "")
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module WhoAmI
2
+ module Function
3
+ class WriteModel
4
+ include ProcParty
5
+
6
+ def call(extracted_class)
7
+ content =
8
+ "#{extracted_class.computed_header}" \
9
+ "\n" \
10
+ "#{extracted_class.computed_content}"
11
+
12
+ File.write(extracted_class.model_filepath, content)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ namespace :who_am_i do
2
+ desc "Annotate models"
3
+ task :main do
4
+ require "who_am_i"
5
+
6
+ main = WhoAmI::Function::Main.new(File.expand_path("."))
7
+ main.call
8
+ end
9
+ end
10
+
11
+ desc "Annotate"
12
+ task who_am_i: "who_am_i:main"
@@ -0,0 +1,17 @@
1
+ module WhoAmI
2
+ module Refinement
3
+ module YieldSelf
4
+ refine Object do
5
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5.0")
6
+ def yield_self
7
+ if block_given?
8
+ yield(self)
9
+ else
10
+ enum_for(:yield_self)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end