society 0.13.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|