society 0.13.2 → 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.
- checksums.yaml +4 -4
- data/.bowerrc +3 -0
- data/.gitignore +2 -14
- data/README.md +18 -17
- data/bower.json +6 -0
- data/lib/society.rb +16 -6
- data/lib/society/association_processor.rb +206 -0
- data/lib/society/cli.rb +12 -9
- data/lib/society/edge.rb +15 -0
- data/lib/society/formatter/graph/json.rb +44 -0
- data/lib/society/formatter/report/html.rb +73 -0
- data/lib/society/formatter/report/json.rb +39 -0
- data/lib/society/formatter/report/templates/components/.gitignore +7 -0
- data/lib/society/formatter/report/templates/components/d3/d3.min.js +5 -0
- data/lib/society/formatter/report/templates/components/society-assets/society.css +70 -0
- data/lib/society/formatter/report/templates/components/society-assets/society.js +420 -0
- data/lib/society/formatter/report/templates/index.htm.haml +48 -0
- data/lib/society/object_graph.rb +3 -2
- data/lib/society/parser.rb +49 -32
- data/lib/society/reference_processor.rb +60 -0
- data/lib/society/version.rb +1 -1
- data/society.gemspec +4 -1
- data/society_graph.json +1 -0
- data/spec/association_processor_spec.rb +174 -0
- data/spec/cli_spec.rb +25 -0
- data/spec/fixtures/foo.rb +6 -18
- data/spec/formatter/graph/json_spec.rb +52 -0
- data/spec/formatter/report/html_spec.rb +75 -0
- data/spec/formatter/report/json_spec.rb +70 -0
- data/spec/object_graph_spec.rb +30 -0
- data/spec/parser_spec.rb +61 -0
- data/spec/reference_processor_spec.rb +18 -0
- data/spec/spec_helper.rb +3 -0
- metadata +77 -13
- data/lib/society/matrix.rb +0 -36
- data/lib/society/node.rb +0 -19
- data/spec/fixtures/bar.rb +0 -6
- data/spec/matrix_spec.rb +0 -27
- data/spec/node_spec.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e981e4f78cf210bc9a370f895b47f44b66f48a1
|
4
|
+
data.tar.gz: 164a11a1e55e5921ca3dc32c58afbf984f09427f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb27a9fe4c863f8acd5ff810ab0d5c37be1f22082f1f1e412a7349c58ed4e616631a8852f60eb7f6fc8a96ac254266e56a509aa43e51274fc22a162cea206a88
|
7
|
+
data.tar.gz: f5859b87ee2efd9e3456c488298f0e2fe6d67ed25bfa497e56e42ea7b6a0710925584289c62d62d0621f7cac873c1549c833d7388391678348176bd79a0cb116
|
data/.bowerrc
ADDED
data/.gitignore
CHANGED
@@ -1,23 +1,11 @@
|
|
1
|
-
*.gem
|
2
|
-
*.rbc
|
3
1
|
.bundle
|
4
2
|
.config
|
5
|
-
.yardoc
|
6
3
|
Gemfile.lock
|
7
|
-
InstalledFiles
|
8
|
-
_yardoc
|
9
4
|
coverage
|
10
|
-
doc/
|
11
|
-
lib/bundler/man
|
12
5
|
pkg
|
13
6
|
rdoc
|
14
7
|
spec/reports
|
15
|
-
test/tmp
|
16
|
-
test/version_tmp
|
17
8
|
tmp
|
18
|
-
*.bundle
|
19
|
-
*.so
|
20
|
-
*.o
|
21
|
-
*.a
|
22
|
-
mkmf.log
|
23
9
|
.console_history
|
10
|
+
doc/society
|
11
|
+
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Society
|
2
2
|
|
3
|
-
Society analyzes and presents a social graph of relationships between classes or
|
3
|
+
Society analyzes and presents a social graph of relationships between classes or
|
4
|
+
methods.
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -18,31 +19,31 @@ Or install it yourself as:
|
|
18
19
|
|
19
20
|
## Usage
|
20
21
|
|
21
|
-
|
22
|
+
From your terminal:
|
22
23
|
|
23
|
-
|
24
|
+
society from path/to/models
|
24
25
|
|
25
|
-
|
26
|
+
and then open `doc/society/index.htm` in your browser.
|
26
27
|
|
27
|
-
|
28
|
+
The default format is HTML; you can skip the HTML interface and just get the
|
29
|
+
JSON by passing `--format json`
|
28
30
|
|
29
|
-
|
31
|
+
Note that all JSON data is timestamped (regardless of output format) to store
|
32
|
+
snapshots of your project over time.
|
30
33
|
|
31
|
-
|
34
|
+
## Updating assets
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
* Add fukuzatsu as a dependency
|
40
|
-
|
41
|
-
* Wrap fukuzatsu parsing and remove duplicate classes
|
36
|
+
All JavaScript and CSS dependencies are checked into the repo, so any given
|
37
|
+
commit should have everything it needs. However, if you're developing the gem
|
38
|
+
and need to pull in updates from the
|
39
|
+
[society-assets](https://github.com/CoralineAda/society-assets) package, you
|
40
|
+
can do so on the command line with `$ bower update`.
|
42
41
|
|
43
42
|
## Contributing
|
44
43
|
|
45
|
-
Please note that this project is released with a [Contributor Code of Conduct]
|
44
|
+
Please note that this project is released with a [Contributor Code of Conduct]
|
45
|
+
(http://contributor-covenant.org/version/1/0/0/).
|
46
|
+
By participating in this project you agree to abide by its terms.
|
46
47
|
|
47
48
|
|
48
49
|
1. Fork it ( https://github.com/[my-github-username]/society/fork )
|
data/bower.json
ADDED
data/lib/society.rb
CHANGED
@@ -1,12 +1,22 @@
|
|
1
|
+
require "analyst"
|
1
2
|
require "fileutils"
|
3
|
+
require "active_support/core_ext/string/inflections"
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
require_relative "society/
|
7
|
-
require_relative "society/
|
8
|
-
require_relative "society/
|
5
|
+
require_relative "society/association_processor"
|
6
|
+
require_relative "society/reference_processor"
|
7
|
+
require_relative "society/edge"
|
8
|
+
require_relative "society/formatter/graph/json"
|
9
|
+
require_relative "society/formatter/report/html"
|
10
|
+
require_relative "society/formatter/report/json"
|
9
11
|
require_relative "society/object_graph"
|
10
12
|
require_relative "society/parser"
|
11
13
|
require_relative "society/version"
|
12
14
|
|
15
|
+
module Society
|
16
|
+
|
17
|
+
def self.new(path_to_files)
|
18
|
+
Society::Parser.for_files(path_to_files)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module Society
|
2
|
+
|
3
|
+
class AssociationProcessor
|
4
|
+
|
5
|
+
ACTIVE_MODEL_ASSOCIATIONS = %w[belongs_to has_one has_many has_and_belongs_to_many]
|
6
|
+
|
7
|
+
attr_reader :classes, :associations, :unresolved_associations
|
8
|
+
|
9
|
+
def initialize(classes)
|
10
|
+
@classes = classes
|
11
|
+
process
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def process
|
17
|
+
default_resolver = DefaultResolver.new(classes)
|
18
|
+
through_resolver = ThroughResolver.new(classes)
|
19
|
+
poly_resolver = PolymorphicResolver.new(default_resolver)
|
20
|
+
|
21
|
+
classes.each do |klass|
|
22
|
+
klass.macros.select do |macro|
|
23
|
+
ACTIVE_MODEL_ASSOCIATIONS.include?(macro.name)
|
24
|
+
# TODO: make sure macro.target is 'self' (it pretty much always will be)
|
25
|
+
end.each do |association|
|
26
|
+
through_resolver.process(association, klass) or
|
27
|
+
poly_resolver.process(association, klass) or
|
28
|
+
default_resolver.process(association, klass)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
@associations = default_resolver.associations
|
33
|
+
@unresolved_associations = default_resolver.unresolved_associations
|
34
|
+
|
35
|
+
@associations.concat(poly_resolver.associations).concat(through_resolver.associations)
|
36
|
+
@unresolved_associations.concat(through_resolver.unresolved_associations)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class ThroughResolver
|
41
|
+
|
42
|
+
attr_reader :associations, :unresolved_associations
|
43
|
+
|
44
|
+
def initialize(classes)
|
45
|
+
@classes = classes
|
46
|
+
@my_own_default_resolver = DefaultResolver.new(classes)
|
47
|
+
@associations = []
|
48
|
+
@unresolved_associations = []
|
49
|
+
end
|
50
|
+
|
51
|
+
def process(association, klass)
|
52
|
+
return false unless thru = through_of(association)
|
53
|
+
|
54
|
+
if source_type = association.arguments.last.to_hash[:source_type]
|
55
|
+
target = @classes.detect { |cls| cls.full_name == source_type }
|
56
|
+
else
|
57
|
+
joiner = klass.macros.detect do |macro|
|
58
|
+
ACTIVE_MODEL_ASSOCIATIONS.include?(macro.name) && macro.arguments.first.value == thru
|
59
|
+
end
|
60
|
+
joining_class_entities = @my_own_default_resolver.all_classes_for(joiner)
|
61
|
+
|
62
|
+
source_str = source_of(association).to_s
|
63
|
+
|
64
|
+
target = joining_class_entities.reduce(nil) do |_,cls|
|
65
|
+
assoc = find_association_in(cls, source_str)
|
66
|
+
break @my_own_default_resolver.process(assoc, cls) if assoc
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if target
|
71
|
+
@associations << Edge.new(from: klass,
|
72
|
+
to: target,
|
73
|
+
meta: {
|
74
|
+
type: :through_association,
|
75
|
+
macro: association })
|
76
|
+
else
|
77
|
+
@unresolved_associations << { class: klass,
|
78
|
+
macro: association,
|
79
|
+
thru_macro: joiner,
|
80
|
+
joining_classes: joining_class_entities }
|
81
|
+
end
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def find_association_in(klass, name)
|
88
|
+
klass.macros.detect do |macro|
|
89
|
+
ACTIVE_MODEL_ASSOCIATIONS.include?(macro.name) &&
|
90
|
+
macro.arguments.first.value.to_s.singularize == name.singularize
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def through_of(association)
|
95
|
+
last_arg = association.arguments.last
|
96
|
+
last_arg.is_a?(Analyst::Entities::Hash) && last_arg.to_hash[:through]
|
97
|
+
end
|
98
|
+
|
99
|
+
def source_of(association)
|
100
|
+
association.arguments.last.to_hash[:source] || association.arguments.first.value
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
class DefaultResolver
|
107
|
+
|
108
|
+
attr_reader :associations, :unresolved_associations
|
109
|
+
|
110
|
+
def initialize(classes)
|
111
|
+
@classes = classes
|
112
|
+
@associations = []
|
113
|
+
@unresolved_associations = []
|
114
|
+
end
|
115
|
+
|
116
|
+
def process(association, klass)
|
117
|
+
if target = all_classes_for(association).first
|
118
|
+
@associations << Edge.new(from: klass,
|
119
|
+
to: target,
|
120
|
+
meta: {
|
121
|
+
type: :association,
|
122
|
+
macro: association })
|
123
|
+
else
|
124
|
+
@unresolved_associations << { class: klass,
|
125
|
+
target_name: target_class_name_for(association),
|
126
|
+
macro: association }
|
127
|
+
end
|
128
|
+
target
|
129
|
+
end
|
130
|
+
|
131
|
+
def all_classes_for(association)
|
132
|
+
target_name = target_class_name_for(association)
|
133
|
+
classes.select { |klass| klass.full_name == target_name }
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
attr_reader :classes
|
139
|
+
|
140
|
+
def target_class_name_for(association)
|
141
|
+
last_arg = association.arguments.last
|
142
|
+
if last_arg.is_a? Analyst::Entities::Hash
|
143
|
+
target_name = last_arg.to_hash[:class_name]
|
144
|
+
end
|
145
|
+
target_name ||= association.arguments.first.value.to_s.pluralize.classify
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
class PolymorphicResolver
|
152
|
+
|
153
|
+
def initialize(default_resolver)
|
154
|
+
@default_resolver = default_resolver
|
155
|
+
@polymorphics = []
|
156
|
+
@ases = []
|
157
|
+
end
|
158
|
+
|
159
|
+
# stores off any `polymorphic: true` or `as: :something` associations for
|
160
|
+
# later processing. also does default processing on `as: :something`
|
161
|
+
# associations. returns true iff association requires no further processing.
|
162
|
+
def process(association, klass)
|
163
|
+
last_arg = association.arguments.last
|
164
|
+
return false unless last_arg.is_a?(Analyst::Entities::Hash)
|
165
|
+
|
166
|
+
opts = last_arg.to_hash
|
167
|
+
if opts.key? :polymorphic
|
168
|
+
@polymorphics << { class: klass,
|
169
|
+
key: association.arguments.first.value,
|
170
|
+
macro: association }
|
171
|
+
return true
|
172
|
+
|
173
|
+
elsif opts.key? :as
|
174
|
+
if target = @default_resolver.process(association, klass)
|
175
|
+
@ases << { class: klass, target: target,
|
176
|
+
macro: association, key: opts[:as] }
|
177
|
+
end
|
178
|
+
return true
|
179
|
+
end
|
180
|
+
|
181
|
+
false
|
182
|
+
end
|
183
|
+
|
184
|
+
def associations
|
185
|
+
associations = []
|
186
|
+
@polymorphics.each do |poly|
|
187
|
+
matching = @ases.select do |as|
|
188
|
+
as[:key] == poly[:key] && as[:target] == poly[:class]
|
189
|
+
end
|
190
|
+
matching.each do |as|
|
191
|
+
associations << Edge.new(from: poly[:class],
|
192
|
+
to: as[:class],
|
193
|
+
meta: {
|
194
|
+
type: :polymorphic_association,
|
195
|
+
macro: poly[:macro],
|
196
|
+
complement: as[:macro] })
|
197
|
+
end
|
198
|
+
end
|
199
|
+
associations
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
data/lib/society/cli.rb
CHANGED
@@ -1,26 +1,29 @@
|
|
1
1
|
require "thor"
|
2
|
+
require "society"
|
2
3
|
|
3
4
|
module Society
|
4
5
|
|
5
6
|
class CLI < Thor
|
6
7
|
|
7
|
-
desc_text = ""
|
8
|
+
desc_text = "Formats are html (default) and json."
|
9
|
+
desc_text << "Example: society from foo/ -f json -o ./society_data.json"
|
8
10
|
|
9
|
-
desc "from PATH_TO_FILE", desc_text
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
desc "from PATH_TO_FILE [-f FORMAT] [-o OUTPUT_PATH]", desc_text
|
12
|
+
method_option :format, :type => :string, :default => 'html', :aliases => "-f"
|
13
|
+
method_option :output, :type => :string, :aliases => "-o"
|
14
|
+
|
15
|
+
def from(path)
|
16
|
+
Society.new(path).report(format, options['output'])
|
14
17
|
end
|
15
18
|
|
16
19
|
default_task :from
|
17
20
|
|
18
21
|
private
|
19
22
|
|
20
|
-
def
|
21
|
-
|
23
|
+
def format
|
24
|
+
options['format'] && options['format'].to_sym
|
22
25
|
end
|
23
26
|
|
24
27
|
end
|
25
28
|
|
26
|
-
end
|
29
|
+
end
|
data/lib/society/edge.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Society
|
4
|
+
module Formatter
|
5
|
+
module Graph
|
6
|
+
class JSON
|
7
|
+
|
8
|
+
def initialize(graph)
|
9
|
+
@nodes = graph.nodes
|
10
|
+
@edges = graph.edges
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_json
|
14
|
+
to_hash.to_json
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_hash
|
18
|
+
{
|
19
|
+
nodes: node_names.map { |name| { name: name } },
|
20
|
+
edges: named_edges.map do |edge|
|
21
|
+
{
|
22
|
+
from: node_names.index(edge.from),
|
23
|
+
to: node_names.index(edge.to)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :nodes, :edges
|
32
|
+
|
33
|
+
def node_names
|
34
|
+
@node_names ||= nodes.map(&:full_name).uniq
|
35
|
+
end
|
36
|
+
|
37
|
+
def named_edges
|
38
|
+
@named_edges ||= edges.map { |edge| Edge.new(from: edge.from.full_name, to: edge.to.full_name) }
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|