ahnnotate 0.2.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +55 -0
- data/LICENSE.txt +21 -0
- data/README.md +118 -0
- data/Rakefile +10 -0
- data/ahnnotate.gemspec +33 -0
- data/bin/console +14 -0
- data/bin/rake +29 -0
- data/bin/setup +8 -0
- data/exe/ahnnotate +14 -0
- data/lib/ahnnotate/cli.rb +116 -0
- data/lib/ahnnotate/column.rb +60 -0
- data/lib/ahnnotate/config.rb +67 -0
- data/lib/ahnnotate/error.rb +8 -0
- data/lib/ahnnotate/facet/models/main.rb +49 -0
- data/lib/ahnnotate/facet/models/module_node.rb +123 -0
- data/lib/ahnnotate/facet/models/processor.rb +91 -0
- data/lib/ahnnotate/facet/models/resolve_active_record_models.rb +42 -0
- data/lib/ahnnotate/facet/models/resolve_class_relationships.rb +67 -0
- data/lib/ahnnotate/facet/models/standin.rb +41 -0
- data/lib/ahnnotate/facet/models.rb +10 -0
- data/lib/ahnnotate/function/format.rb +36 -0
- data/lib/ahnnotate/function/main.rb +32 -0
- data/lib/ahnnotate/function/run.rb +24 -0
- data/lib/ahnnotate/function/tabularize.rb +53 -0
- data/lib/ahnnotate/index.rb +26 -0
- data/lib/ahnnotate/options.rb +48 -0
- data/lib/ahnnotate/rails.rake +44 -0
- data/lib/ahnnotate/railtie.rb +7 -0
- data/lib/ahnnotate/table.rb +44 -0
- data/lib/ahnnotate/tables.rb +55 -0
- data/lib/ahnnotate/version.rb +3 -0
- data/lib/ahnnotate/vfs.rb +69 -0
- data/lib/ahnnotate/vfs_driver/filesystem.rb +68 -0
- data/lib/ahnnotate/vfs_driver/hash.rb +59 -0
- data/lib/ahnnotate/vfs_driver/read_only_filesystem.rb +8 -0
- data/lib/ahnnotate.rb +32 -0
- metadata +202 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Facet
|
3
|
+
module Models
|
4
|
+
# ModuleNode is named as such since `Class.is_a?(Module) == true`.
|
5
|
+
class ModuleNode
|
6
|
+
# By including ClassMethods this way, I'm including the methods as
|
7
|
+
# instance methods. I'm doing this so that I can compute the table name
|
8
|
+
# on per-class basis.
|
9
|
+
#
|
10
|
+
# It's a bit unfortunate that this class will be used to both (1) keep
|
11
|
+
# track of how classes/modules relate to each other and (2) compute the
|
12
|
+
# table name.
|
13
|
+
include ActiveRecord::ModelSchema::ClassMethods
|
14
|
+
|
15
|
+
# Named to fit the ModelSchema interface. This is basically `Class#name`
|
16
|
+
attr_accessor :name
|
17
|
+
# Named to fit the ModelSchema interface. This is the "outer class"
|
18
|
+
attr_accessor :parent
|
19
|
+
# Named to fit the ModelSchema interface. This is the class that the
|
20
|
+
# current class inherits from. This is computed, whereas
|
21
|
+
# `claimed_superclass` is what is parsed from the source
|
22
|
+
attr_accessor :superclass
|
23
|
+
# Named to fit the ModelSchema interface. This is currently unsupported
|
24
|
+
attr_accessor :table_name_prefix
|
25
|
+
|
26
|
+
attr_writer :claimed_superclass
|
27
|
+
attr_writer :abstract_class
|
28
|
+
attr_writer :is_a_kind_of_activerecord_base
|
29
|
+
attr_accessor :explicit_table_name
|
30
|
+
attr_accessor :is_active_record_base
|
31
|
+
attr_accessor :path
|
32
|
+
|
33
|
+
def initialize(name,
|
34
|
+
parent: nil,
|
35
|
+
is_a_kind_of_activerecord_base: false,
|
36
|
+
claimed_superclass: nil,
|
37
|
+
explicit_table_name: nil,
|
38
|
+
abstract_class: nil)
|
39
|
+
self.name = name
|
40
|
+
self.parent = parent
|
41
|
+
self.is_a_kind_of_activerecord_base = is_a_kind_of_activerecord_base
|
42
|
+
self.claimed_superclass = claimed_superclass
|
43
|
+
self.explicit_table_name = explicit_table_name
|
44
|
+
self.abstract_class = abstract_class
|
45
|
+
end
|
46
|
+
|
47
|
+
# Named to fit the ModelSchema interface
|
48
|
+
def pluralize_table_names
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def table_name_suffix
|
53
|
+
end
|
54
|
+
|
55
|
+
def is_a_kind_of_activerecord_base?
|
56
|
+
!!@is_a_kind_of_activerecord_base
|
57
|
+
end
|
58
|
+
|
59
|
+
# Named to fit the ModelSchema interface
|
60
|
+
def abstract_class?
|
61
|
+
!!@abstract_class
|
62
|
+
end
|
63
|
+
|
64
|
+
def claimed_superclass
|
65
|
+
@claimed_superclass.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
# Named to fit the ModelSchema interface. It was originally implemented
|
69
|
+
# in ActiveRecord::Inheritance. I've re-implemented it here via the
|
70
|
+
# documentation.
|
71
|
+
def base_class
|
72
|
+
if superclass.is_active_record_base
|
73
|
+
return self
|
74
|
+
end
|
75
|
+
|
76
|
+
if superclass.abstract_class?
|
77
|
+
return self
|
78
|
+
end
|
79
|
+
|
80
|
+
superclass.base_class
|
81
|
+
end
|
82
|
+
|
83
|
+
# Named to fit the ModelSchema interface. It was originally implemented
|
84
|
+
# in ActiveSupport::Introspection
|
85
|
+
def parents
|
86
|
+
if parent
|
87
|
+
[parent, *parent.parent]
|
88
|
+
else
|
89
|
+
[]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def class_name
|
94
|
+
if @name
|
95
|
+
"#{parent.class_name}::#{@name}"
|
96
|
+
else
|
97
|
+
""
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def table_name
|
102
|
+
if explicit_table_name
|
103
|
+
return @explicit_table_name
|
104
|
+
end
|
105
|
+
|
106
|
+
super
|
107
|
+
end
|
108
|
+
|
109
|
+
def <(other)
|
110
|
+
other == ActiveRecord::Base && is_a_kind_of_activerecord_base?
|
111
|
+
end
|
112
|
+
|
113
|
+
def ==(other)
|
114
|
+
if is_active_record_base && other == ActiveRecord::Base
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
|
118
|
+
super
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Facet
|
3
|
+
module Models
|
4
|
+
class Processor < Parser::AST::Processor
|
5
|
+
def call(content)
|
6
|
+
sexp = Parser::CurrentRuby.parse(content)
|
7
|
+
|
8
|
+
@current_class = ModuleNode.new(nil)
|
9
|
+
@classes = [@current_class]
|
10
|
+
|
11
|
+
process(sexp)
|
12
|
+
|
13
|
+
@classes.reject { |klass| klass.class_name == "" }
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_class(node)
|
17
|
+
@current_class = module_node_create(node, parent: @current_class)
|
18
|
+
@classes.push(@current_class)
|
19
|
+
|
20
|
+
super
|
21
|
+
|
22
|
+
@current_class = @current_class.parent
|
23
|
+
end
|
24
|
+
|
25
|
+
alias on_module on_class
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
receiver, method_name, assigned_node = *node
|
29
|
+
|
30
|
+
if receiver == s(:self) && method_name == :table_name=
|
31
|
+
table_name = assigned_node.children.last
|
32
|
+
@current_class.table_name = table_name
|
33
|
+
end
|
34
|
+
|
35
|
+
if receiver == s(:self) && method_name == :abstract_class=
|
36
|
+
abstract_class =
|
37
|
+
if assigned_node.type == :true
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
@current_class.abstract_class = abstract_class
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# ignore instance method definitions since method definition bodies
|
47
|
+
# can't contain class declarations
|
48
|
+
def on_def(_)
|
49
|
+
end
|
50
|
+
|
51
|
+
# ignore class method definitions since method definition bodies can't
|
52
|
+
# contain class declarations
|
53
|
+
def on_defs(_)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def module_node_create(node, parent:)
|
59
|
+
class_node, superclass_node, _body_node = *node
|
60
|
+
|
61
|
+
if node.type == :module
|
62
|
+
superclass_node = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
class_name = resolve_class_name(class_node)
|
66
|
+
superclass_name = resolve_class_name(superclass_node)
|
67
|
+
|
68
|
+
classlike = ModuleNode.new(class_name.to_s)
|
69
|
+
classlike.claimed_superclass = superclass_name.to_s
|
70
|
+
classlike.parent = parent
|
71
|
+
|
72
|
+
classlike
|
73
|
+
end
|
74
|
+
|
75
|
+
def resolve_class_name(node)
|
76
|
+
outer, name = *node
|
77
|
+
|
78
|
+
if outer.nil?
|
79
|
+
name
|
80
|
+
else
|
81
|
+
"#{resolve_class_name(outer)}::#{name}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def s(type, *children)
|
86
|
+
AST::Node.new(type, children)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Facet
|
3
|
+
module Models
|
4
|
+
class ResolveActiveRecordModels
|
5
|
+
include ProcParty
|
6
|
+
|
7
|
+
def call(object_space)
|
8
|
+
tree = {}
|
9
|
+
object_space.each do |_class_name, extracted_class|
|
10
|
+
superclass = extracted_class.superclass
|
11
|
+
|
12
|
+
tree[superclass] ||= Set.new
|
13
|
+
tree[superclass].add(extracted_class)
|
14
|
+
end
|
15
|
+
|
16
|
+
activerecord_family =
|
17
|
+
gather_family(object_space["::ActiveRecord::Base"], tree)
|
18
|
+
|
19
|
+
activerecord_family.each do |individual|
|
20
|
+
individual.is_a_kind_of_activerecord_base = true
|
21
|
+
end
|
22
|
+
|
23
|
+
object_space.values - [object_space[""], object_space["::ActiveRecord::Base"]]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def gather_family(node, tree)
|
29
|
+
gather_family_helper(node, tree).flatten
|
30
|
+
end
|
31
|
+
|
32
|
+
def gather_family_helper(node, tree)
|
33
|
+
if tree[node].nil?
|
34
|
+
return node
|
35
|
+
end
|
36
|
+
|
37
|
+
[node] + tree[node].map { |child| gather_family_helper(child, tree) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Facet
|
3
|
+
module Models
|
4
|
+
class ResolveClassRelationships
|
5
|
+
include ProcParty
|
6
|
+
|
7
|
+
def call(extracted_classes)
|
8
|
+
object_space =
|
9
|
+
extracted_classes
|
10
|
+
.map(&method(:self_and_outer_class))
|
11
|
+
.flatten
|
12
|
+
.compact
|
13
|
+
.uniq
|
14
|
+
.map { |x| [x.class_name, x] }
|
15
|
+
.to_h
|
16
|
+
|
17
|
+
object_space[""] ||= ModuleNode.new(nil)
|
18
|
+
|
19
|
+
object_space["::ActiveRecord::Base"] =
|
20
|
+
ModuleNode.new(
|
21
|
+
"ActiveRecord::Base",
|
22
|
+
parent: object_space[""],
|
23
|
+
abstract_class: nil
|
24
|
+
)
|
25
|
+
object_space["::ActiveRecord::Base"].is_active_record_base = true
|
26
|
+
|
27
|
+
object_space.each do |class_name, extracted_class|
|
28
|
+
possible_namespace_levels = class_name.split("::")[1..-2] || []
|
29
|
+
|
30
|
+
if extracted_class.claimed_superclass == "" || extracted_class.claimed_superclass == nil
|
31
|
+
next
|
32
|
+
end
|
33
|
+
|
34
|
+
(possible_namespace_levels.size + 1).times do
|
35
|
+
class_uri_parts =
|
36
|
+
possible_namespace_levels + [extracted_class.claimed_superclass]
|
37
|
+
|
38
|
+
class_uri = "::#{class_uri_parts.join("::")}"
|
39
|
+
|
40
|
+
if object_space[class_uri]
|
41
|
+
extracted_class.superclass = object_space[class_uri]
|
42
|
+
break
|
43
|
+
end
|
44
|
+
|
45
|
+
possible_namespace_levels.pop
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
object_space
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def self_and_outer_class(extracted_class)
|
55
|
+
if extracted_class.nil?
|
56
|
+
return nil
|
57
|
+
end
|
58
|
+
|
59
|
+
[
|
60
|
+
extracted_class,
|
61
|
+
self_and_outer_class(extracted_class.parent)
|
62
|
+
]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Facet
|
3
|
+
module Models
|
4
|
+
class Standin
|
5
|
+
# Lol. It's a little easier in this case to deal with instances here.
|
6
|
+
# I'll need to create many instances to recreate the class structure of
|
7
|
+
# the models in order to most correctly compute the table name from the
|
8
|
+
# model name 🥳
|
9
|
+
include ActiveRecord::ModelSchema::ClassMethods
|
10
|
+
|
11
|
+
# boolean, is this an abstract class
|
12
|
+
attr_writer :abstract_class
|
13
|
+
# the class that the current class inherits from
|
14
|
+
attr_accessor :superclass
|
15
|
+
attr_accessor :base_class
|
16
|
+
# the outer class
|
17
|
+
attr_accessor :parent
|
18
|
+
|
19
|
+
def initialize(abstract_class:, superclass:, base_class:, parent:, is_active_record_base: false)
|
20
|
+
@abstract_class = abstract_class
|
21
|
+
@superclass = superclass
|
22
|
+
@base_class = base_class
|
23
|
+
@parent = parent
|
24
|
+
@is_active_record_base = is_active_record_base
|
25
|
+
end
|
26
|
+
|
27
|
+
def abstract_class?
|
28
|
+
!!@abstract_class
|
29
|
+
end
|
30
|
+
|
31
|
+
def ==(other)
|
32
|
+
if @is_active_record_base && other == ActiveRecord::Base
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Function
|
3
|
+
class Format
|
4
|
+
attr_reader :comment
|
5
|
+
|
6
|
+
def initialize(comment:)
|
7
|
+
@comment = comment
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(table, content)
|
11
|
+
table.string(comment: comment) + "\n" + strip_schema(content)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def strip_schema(content)
|
17
|
+
matches = pattern.match(content)
|
18
|
+
|
19
|
+
if matches
|
20
|
+
matches["post"]
|
21
|
+
else
|
22
|
+
content
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def pattern
|
27
|
+
@pattern ||=
|
28
|
+
begin
|
29
|
+
newline = /\r?\n\r?/
|
30
|
+
|
31
|
+
/\A#{comment}\s==\sSchema\sInfo#{newline}?(?:^#{comment}[^\n]*$#{newline})*#{newline}(?<post>.*)/m
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Function
|
3
|
+
class Main
|
4
|
+
def initialize(root, options, config)
|
5
|
+
@root = root
|
6
|
+
@options = options
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
if @config["boot"]
|
12
|
+
eval @config["boot"]
|
13
|
+
end
|
14
|
+
|
15
|
+
vfs = Vfs.new(vfs_driver)
|
16
|
+
|
17
|
+
runner = Run.new(@config, vfs)
|
18
|
+
runner.call
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def vfs_driver
|
24
|
+
if @options.fix?
|
25
|
+
VfsDriver::Filesystem.new(root: @root)
|
26
|
+
else
|
27
|
+
VfsDriver::ReadOnlyFilesystem.new(root: @root)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Function
|
3
|
+
class Run
|
4
|
+
def initialize(config, vfs)
|
5
|
+
@config = config
|
6
|
+
@vfs = vfs
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
Facet::Models.add(@config, tables_hash, @vfs)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def tables_hash
|
16
|
+
@tables_hash = tables.to_h
|
17
|
+
end
|
18
|
+
|
19
|
+
def tables
|
20
|
+
@tables ||= Tables.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
module Function
|
3
|
+
class Tabularize
|
4
|
+
def initialize(prefix:, cell_divider:)
|
5
|
+
@prefix = prefix
|
6
|
+
@cell_divider = cell_divider
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(data, column_names)
|
10
|
+
output = StringIO.new
|
11
|
+
minimum_column_lengths = Hash.new { 0 }
|
12
|
+
|
13
|
+
rows = data.map do |row|
|
14
|
+
row_hash = {}
|
15
|
+
|
16
|
+
column_names.each do |c|
|
17
|
+
value = row.public_send(c).to_s
|
18
|
+
|
19
|
+
row_hash[c] = value
|
20
|
+
|
21
|
+
if value.size > minimum_column_lengths[c]
|
22
|
+
minimum_column_lengths[c] = value.size
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
row_hash
|
27
|
+
end
|
28
|
+
|
29
|
+
rows.each do |row|
|
30
|
+
# Note: minimum_column_lengths shouldn't include any of the columns
|
31
|
+
# with a length of zero since they were never explicitly set (to 0)
|
32
|
+
minimum_column_lengths.each.with_index do |(column_name, column_max_length), index|
|
33
|
+
if index == 0
|
34
|
+
output.print(@prefix)
|
35
|
+
end
|
36
|
+
|
37
|
+
if_rightmost_column = index + 1 == minimum_column_lengths.size
|
38
|
+
|
39
|
+
if if_rightmost_column
|
40
|
+
output.puts "#{row[column_name]}"
|
41
|
+
else
|
42
|
+
column_length = row[column_name].size
|
43
|
+
spaces_length = column_max_length - column_length
|
44
|
+
output.print "#{row[column_name]}#{" " * spaces_length}#{@cell_divider}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
output.string.gsub(/ +$/, "")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
class Index
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :columns
|
5
|
+
attr_accessor :comment
|
6
|
+
attr_accessor :unique
|
7
|
+
|
8
|
+
def initialize(**args)
|
9
|
+
args.each do |key, value|
|
10
|
+
public_send("#{key}=", value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def presentable_columns
|
15
|
+
"(#{columns.join(", ")})"
|
16
|
+
end
|
17
|
+
|
18
|
+
def presentable_unique
|
19
|
+
if unique
|
20
|
+
"UNIQUE"
|
21
|
+
else
|
22
|
+
""
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
class Options
|
3
|
+
def self.attribute_names
|
4
|
+
@attribute_names ||= []
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.attr_writer(*names)
|
8
|
+
attribute_names.push(*names)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.attr_question(*names)
|
13
|
+
names.each do |name|
|
14
|
+
attr_writer(name)
|
15
|
+
|
16
|
+
define_method("#{name}?") do
|
17
|
+
!!instance_variable_get("@#{name}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_question :exit
|
23
|
+
attr_question :fix
|
24
|
+
|
25
|
+
def initialize(**args)
|
26
|
+
args.each do |key, value|
|
27
|
+
public_send("#{key}=", value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
output = StringIO.new
|
33
|
+
|
34
|
+
output.puts "🧐 options:"
|
35
|
+
self.class.attribute_names.each do |attribute_name|
|
36
|
+
output.print "🧐 #{attribute_name}: "
|
37
|
+
|
38
|
+
if instance_variable_defined?("@#{attribute_name}")
|
39
|
+
output.puts "undefined"
|
40
|
+
else
|
41
|
+
output.puts instance_variable_get("@#{attribute_name}").inspect
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
output.string
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
namespace :db do
|
2
|
+
task :migrate do
|
3
|
+
$rake_ahnnotate_config ||= Ahnnotate::Config.load(root: Rails.root)
|
4
|
+
|
5
|
+
if $rake_ahnnotate_config["rake_db_autorun"]
|
6
|
+
Rake::Task["ahnnotate:all"].reenable
|
7
|
+
Rake::Task["ahnnotate:all"].invoke
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
task :rollback do
|
12
|
+
$rake_ahnnotate_config ||= Ahnnotate::Config.load(root: Rails.root)
|
13
|
+
|
14
|
+
if $rake_ahnnotate_config["rake_db_autorun"]
|
15
|
+
Rake::Task["ahnnotate:all"].reenable
|
16
|
+
Rake::Task["ahnnotate:all"].invoke
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
namespace :ahnnotate do
|
22
|
+
desc "Run ahnnotate"
|
23
|
+
task :all do
|
24
|
+
require "ahnnotate/cli"
|
25
|
+
require "shellwords"
|
26
|
+
|
27
|
+
# This should either be `rails` or `rake` (since newer versions of Rails
|
28
|
+
# can call rake tasks with either executable)
|
29
|
+
exe_name = File.basename($0)
|
30
|
+
|
31
|
+
argv = ENV.fetch("COMMENTATE", "--fix")
|
32
|
+
argv = Shellwords.split(argv)
|
33
|
+
|
34
|
+
puts "Commentating models..."
|
35
|
+
|
36
|
+
cli = Ahnnotate::Cli.new(name: "#{exe_name} ahnnotate")
|
37
|
+
cli.run(argv, $rake_ahnnotate_config)
|
38
|
+
|
39
|
+
puts "Done!"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Run rake task `ahnnotate:all`"
|
44
|
+
task ahnnotate: "ahnnotate:all"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Ahnnotate
|
2
|
+
class Table
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :columns
|
5
|
+
attr_accessor :indexes
|
6
|
+
|
7
|
+
def initialize(**args)
|
8
|
+
args.each do |key, value|
|
9
|
+
public_send("#{key}=", value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def string(comment:)
|
14
|
+
tabularizer =
|
15
|
+
Function::Tabularize.new(
|
16
|
+
prefix: "#{comment} ",
|
17
|
+
cell_divider: " "
|
18
|
+
)
|
19
|
+
|
20
|
+
output = StringIO.new
|
21
|
+
output.puts "#{comment} == Schema Info"
|
22
|
+
output.puts comment
|
23
|
+
output.puts "#{comment} Table name: #{@name}"
|
24
|
+
output.puts comment
|
25
|
+
output.print tabularizer.call(columns, [:name, :type, :details])
|
26
|
+
output.puts comment
|
27
|
+
|
28
|
+
if indexes.any?
|
29
|
+
output.puts "#{comment} Indexes:"
|
30
|
+
output.puts comment
|
31
|
+
output.print tabularizer.call(indexes, [:name, :presentable_columns, :presentable_unique, :comment])
|
32
|
+
output.puts comment
|
33
|
+
end
|
34
|
+
|
35
|
+
output.string
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def longest_column_name_length
|
41
|
+
@longest_column_name_length ||= @columns.map(&:name).map(&:size).max
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|