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
         
     |