society 0.13.2 → 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 +4 -4
  2. data/.bowerrc +3 -0
  3. data/.gitignore +2 -14
  4. data/README.md +18 -17
  5. data/bower.json +6 -0
  6. data/lib/society.rb +16 -6
  7. data/lib/society/association_processor.rb +206 -0
  8. data/lib/society/cli.rb +12 -9
  9. data/lib/society/edge.rb +15 -0
  10. data/lib/society/formatter/graph/json.rb +44 -0
  11. data/lib/society/formatter/report/html.rb +73 -0
  12. data/lib/society/formatter/report/json.rb +39 -0
  13. data/lib/society/formatter/report/templates/components/.gitignore +7 -0
  14. data/lib/society/formatter/report/templates/components/d3/d3.min.js +5 -0
  15. data/lib/society/formatter/report/templates/components/society-assets/society.css +70 -0
  16. data/lib/society/formatter/report/templates/components/society-assets/society.js +420 -0
  17. data/lib/society/formatter/report/templates/index.htm.haml +48 -0
  18. data/lib/society/object_graph.rb +3 -2
  19. data/lib/society/parser.rb +49 -32
  20. data/lib/society/reference_processor.rb +60 -0
  21. data/lib/society/version.rb +1 -1
  22. data/society.gemspec +4 -1
  23. data/society_graph.json +1 -0
  24. data/spec/association_processor_spec.rb +174 -0
  25. data/spec/cli_spec.rb +25 -0
  26. data/spec/fixtures/foo.rb +6 -18
  27. data/spec/formatter/graph/json_spec.rb +52 -0
  28. data/spec/formatter/report/html_spec.rb +75 -0
  29. data/spec/formatter/report/json_spec.rb +70 -0
  30. data/spec/object_graph_spec.rb +30 -0
  31. data/spec/parser_spec.rb +61 -0
  32. data/spec/reference_processor_spec.rb +18 -0
  33. data/spec/spec_helper.rb +3 -0
  34. metadata +77 -13
  35. data/lib/society/matrix.rb +0 -36
  36. data/lib/society/node.rb +0 -19
  37. data/spec/fixtures/bar.rb +0 -6
  38. data/spec/matrix_spec.rb +0 -27
  39. data/spec/node_spec.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f65f593c46ae32062a78ec4c22aaf53f55f7436
4
- data.tar.gz: 7f39c3d8b353ff9ad94568abcc690c90a5b878b7
3
+ metadata.gz: 6e981e4f78cf210bc9a370f895b47f44b66f48a1
4
+ data.tar.gz: 164a11a1e55e5921ca3dc32c58afbf984f09427f
5
5
  SHA512:
6
- metadata.gz: 109d6b030877005b7246c2702312cbac6213d9687b06d2e573ddfcb617cc5ec528f40db2799773800ba365666a965c0b8c3a60e49a2a23bfd8cf8c1af1001385
7
- data.tar.gz: 46b91d5ee61b7056f8709d03b1d0dc607482b7d53ac6d758b2b3f8813b4035cf66aebfa9a65debcbf0d37283b02759c8f35ac7efb08c47d23e635d3e9f11b538
6
+ metadata.gz: fb27a9fe4c863f8acd5ff810ab0d5c37be1f22082f1f1e412a7349c58ed4e616631a8852f60eb7f6fc8a96ac254266e56a509aa43e51274fc22a162cea206a88
7
+ data.tar.gz: f5859b87ee2efd9e3456c488298f0e2fe6d67ed25bfa497e56e42ea7b6a0710925584289c62d62d0621f7cac873c1549c833d7388391678348176bd79a0cb116
data/.bowerrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "directory": "lib/society/formatter/report/templates/components"
3
+ }
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 methods.
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
- Initialize a parser with source files:
22
+ From your terminal:
22
23
 
23
- parser = Society::Parser.new("path/to/models")
24
+ society from path/to/models
24
25
 
25
- Generate an object dependency graph:
26
+ and then open `doc/society/index.htm` in your browser.
26
27
 
27
- graph = parser.class_graph
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
- Generate a method dependency graph:
31
+ Note that all JSON data is timestamped (regardless of output format) to store
32
+ snapshots of your project over time.
30
33
 
31
- graph = parser.method_graph
34
+ ## Updating assets
32
35
 
33
- Generate JSON dependency matrix for export to d3:
34
-
35
- parser.matrix.to_jsno
36
-
37
- ## TODO
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](https://github.com/Bantik/society/blob/master/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
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
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "society",
3
+ "dependencies": {
4
+ "society-assets": "1.1.1"
5
+ }
6
+ }
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
- module Society
4
- end
5
-
6
- require_relative "society/cli"
7
- require_relative "society/matrix"
8
- require_relative "society/node"
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
- def from(path="./")
11
- parser = Society::Parser.new(path)
12
- parser.parse_files
13
- parser.report
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 formatter
21
- Formatters::Text
23
+ def format
24
+ options['format'] && options['format'].to_sym
22
25
  end
23
26
 
24
27
  end
25
28
 
26
- end
29
+ end
@@ -0,0 +1,15 @@
1
+ module Society
2
+ class Edge
3
+
4
+ attr_reader :from, :to
5
+ attr_accessor :meta
6
+
7
+ def initialize(from:, to:, meta:nil)
8
+ @from = from
9
+ @to = to
10
+ @meta = meta
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -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