render 0.0.1
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.
- data/.gitignore +20 -0
 - data/.rspec +1 -0
 - data/.ruby-gemset +1 -0
 - data/.ruby-version +1 -0
 - data/Gemfile +4 -0
 - data/Gemfile.lock +49 -0
 - data/LICENSE.txt +22 -0
 - data/extensions/boolean.rb +3 -0
 - data/extensions/dottable_hash.rb +113 -0
 - data/extensions/enumerable.rb +36 -0
 - data/extensions/hash.rb +52 -0
 - data/initialize.rb +7 -0
 - data/lib/render/attribute.rb +99 -0
 - data/lib/render/errors.rb +64 -0
 - data/lib/render/generator.rb +14 -0
 - data/lib/render/graph.rb +106 -0
 - data/lib/render/schema.rb +114 -0
 - data/lib/render/version.rb +3 -0
 - data/lib/render.rb +42 -0
 - data/rakefile.rb +11 -0
 - data/readme.md +90 -0
 - data/render.gemspec +30 -0
 - data/spec/functional/representation/attribute_spec.rb +29 -0
 - data/spec/functional/representation/nested_schemas_spec.rb +103 -0
 - data/spec/functional/representation/schema_spec.rb +86 -0
 - data/spec/integration/nested_graph_spec.rb +77 -0
 - data/spec/integration/single_graph_spec.rb +75 -0
 - data/spec/schemas/film.json +8 -0
 - data/spec/schemas/films.json +13 -0
 - data/spec/support/helpers.rb +3 -0
 - data/spec/support.rb +7 -0
 - data/spec/unit/extensions/boolean_spec.rb +7 -0
 - data/spec/unit/render/attribute_spec.rb +128 -0
 - data/spec/unit/render/generator_spec.rb +39 -0
 - data/spec/unit/render/graph_spec.rb +205 -0
 - data/spec/unit/render/schema_spec.rb +250 -0
 - data/spec/unit/render_spec.rb +81 -0
 - metadata +217 -0
 
    
        data/.gitignore
    ADDED
    
    
    
        data/.rspec
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --color -I spec -I . -r support -r initialize
         
     | 
    
        data/.ruby-gemset
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            render
         
     | 
    
        data/.ruby-version
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ruby-1.9.3
         
     | 
    
        data/Gemfile
    ADDED
    
    
    
        data/Gemfile.lock
    ADDED
    
    | 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            PATH
         
     | 
| 
      
 2 
     | 
    
         
            +
              remote: .
         
     | 
| 
      
 3 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 4 
     | 
    
         
            +
                render (0.0.1)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  uuid (= 2.3.7)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            GEM
         
     | 
| 
      
 8 
     | 
    
         
            +
              remote: http://rubygems.org/
         
     | 
| 
      
 9 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 10 
     | 
    
         
            +
                addressable (2.3.5)
         
     | 
| 
      
 11 
     | 
    
         
            +
                columnize (0.3.6)
         
     | 
| 
      
 12 
     | 
    
         
            +
                crack (0.4.1)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  safe_yaml (~> 0.9.0)
         
     | 
| 
      
 14 
     | 
    
         
            +
                debugger (1.5.0)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  columnize (>= 0.3.1)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  debugger-linecache (~> 1.2.0)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  debugger-ruby_core_source (~> 1.2.0)
         
     | 
| 
      
 18 
     | 
    
         
            +
                debugger-linecache (1.2.0)
         
     | 
| 
      
 19 
     | 
    
         
            +
                debugger-ruby_core_source (1.2.3)
         
     | 
| 
      
 20 
     | 
    
         
            +
                diff-lcs (1.2.4)
         
     | 
| 
      
 21 
     | 
    
         
            +
                macaddr (1.6.1)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  systemu (~> 2.5.0)
         
     | 
| 
      
 23 
     | 
    
         
            +
                rake (10.1.0)
         
     | 
| 
      
 24 
     | 
    
         
            +
                rspec (2.14.1)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  rspec-core (~> 2.14.0)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  rspec-expectations (~> 2.14.0)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  rspec-mocks (~> 2.14.0)
         
     | 
| 
      
 28 
     | 
    
         
            +
                rspec-core (2.14.4)
         
     | 
| 
      
 29 
     | 
    
         
            +
                rspec-expectations (2.14.0)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  diff-lcs (>= 1.1.3, < 2.0)
         
     | 
| 
      
 31 
     | 
    
         
            +
                rspec-mocks (2.14.1)
         
     | 
| 
      
 32 
     | 
    
         
            +
                safe_yaml (0.9.4)
         
     | 
| 
      
 33 
     | 
    
         
            +
                systemu (2.5.2)
         
     | 
| 
      
 34 
     | 
    
         
            +
                uuid (2.3.7)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  macaddr (~> 1.0)
         
     | 
| 
      
 36 
     | 
    
         
            +
                webmock (1.13.0)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  addressable (>= 2.2.7)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  crack (>= 0.3.2)
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            PLATFORMS
         
     | 
| 
      
 41 
     | 
    
         
            +
              ruby
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            DEPENDENCIES
         
     | 
| 
      
 44 
     | 
    
         
            +
              bundler (~> 1.3)
         
     | 
| 
      
 45 
     | 
    
         
            +
              debugger
         
     | 
| 
      
 46 
     | 
    
         
            +
              rake
         
     | 
| 
      
 47 
     | 
    
         
            +
              render!
         
     | 
| 
      
 48 
     | 
    
         
            +
              rspec
         
     | 
| 
      
 49 
     | 
    
         
            +
              webmock
         
     | 
    
        data/LICENSE.txt
    ADDED
    
    | 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Copyright (c) 2013 Steve Weber
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            MIT License
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 6 
     | 
    
         
            +
            a copy of this software and associated documentation files (the
         
     | 
| 
      
 7 
     | 
    
         
            +
            "Software"), to deal in the Software without restriction, including
         
     | 
| 
      
 8 
     | 
    
         
            +
            without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 9 
     | 
    
         
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 10 
     | 
    
         
            +
            permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 11 
     | 
    
         
            +
            the following conditions:
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be
         
     | 
| 
      
 14 
     | 
    
         
            +
            included in all copies or substantial portions of the Software.
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 17 
     | 
    
         
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 18 
     | 
    
         
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
      
 19 
     | 
    
         
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
      
 20 
     | 
    
         
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
      
 21 
     | 
    
         
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
      
 22 
     | 
    
         
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
| 
         @@ -0,0 +1,113 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class DottableHash < Hash
         
     | 
| 
      
 2 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 3 
     | 
    
         
            +
                def new(element_to_hash = {})
         
     | 
| 
      
 4 
     | 
    
         
            +
                  hash = super().merge!(element_to_hash.symbolize_keys)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  hash.each do |key, value|
         
     | 
| 
      
 6 
     | 
    
         
            +
                    hash[key] = initialize_element(value)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  end
         
     | 
| 
      
 8 
     | 
    
         
            +
                  hash
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize_element(value)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  case value
         
     | 
| 
      
 13 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 14 
     | 
    
         
            +
                    new(value)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  when Array
         
     | 
| 
      
 16 
     | 
    
         
            +
                    value.collect { |v| initialize_element(v) }
         
     | 
| 
      
 17 
     | 
    
         
            +
                  else
         
     | 
| 
      
 18 
     | 
    
         
            +
                    value
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def []=(key, value)
         
     | 
| 
      
 24 
     | 
    
         
            +
                key = key.to_sym
         
     | 
| 
      
 25 
     | 
    
         
            +
                value = self.class.initialize_element(value)
         
     | 
| 
      
 26 
     | 
    
         
            +
                super
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              def [](key)
         
     | 
| 
      
 30 
     | 
    
         
            +
                key = key.to_sym
         
     | 
| 
      
 31 
     | 
    
         
            +
                super
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              def delete(key)
         
     | 
| 
      
 35 
     | 
    
         
            +
                key = key.to_sym
         
     | 
| 
      
 36 
     | 
    
         
            +
                super
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              def has_key?(key)
         
     | 
| 
      
 40 
     | 
    
         
            +
                super(key.to_sym)
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              def merge!(other_hash)
         
     | 
| 
      
 44 
     | 
    
         
            +
                super(other_hash.symbolize_keys)
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              def merge(other_hash)
         
     | 
| 
      
 48 
     | 
    
         
            +
                super(other_hash.symbolize_keys)
         
     | 
| 
      
 49 
     | 
    
         
            +
                self
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              def method_missing(method, *arguments)
         
     | 
| 
      
 53 
     | 
    
         
            +
                if method.match(/\=$/)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  self[method.to_sym.chop] = arguments.first
         
     | 
| 
      
 55 
     | 
    
         
            +
                elsif has_key?(method)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  self[method]
         
     | 
| 
      
 57 
     | 
    
         
            +
                else
         
     | 
| 
      
 58 
     | 
    
         
            +
                  super
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              def fetch(key)
         
     | 
| 
      
 63 
     | 
    
         
            +
                key = key.to_sym
         
     | 
| 
      
 64 
     | 
    
         
            +
                raise KeyError.new("Invalid key: #{key}") unless has_key?(key)
         
     | 
| 
      
 65 
     | 
    
         
            +
                super
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              def fetch_path(full_path)
         
     | 
| 
      
 69 
     | 
    
         
            +
                begin
         
     | 
| 
      
 70 
     | 
    
         
            +
                  fetch_path!(full_path)
         
     | 
| 
      
 71 
     | 
    
         
            +
                rescue KeyError
         
     | 
| 
      
 72 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
              def fetch_path!(full_path)
         
     | 
| 
      
 77 
     | 
    
         
            +
                full_path.split(".").inject(self) do |hash, path|
         
     | 
| 
      
 78 
     | 
    
         
            +
                  raise(KeyError) unless hash.is_a?(Hash)
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  hash.fetch(path)
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
              end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
              def set_path(full_path, value)
         
     | 
| 
      
 85 
     | 
    
         
            +
                self.dup.set_path!(full_path, value)
         
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
              def set_path!(full_path, value)
         
     | 
| 
      
 89 
     | 
    
         
            +
                built_hash_for_value = full_path.split(".").reverse.inject({}) do |cumulative, path_to_symource|
         
     | 
| 
      
 90 
     | 
    
         
            +
                  if cumulative.empty?
         
     | 
| 
      
 91 
     | 
    
         
            +
                    { path_to_symource.to_sym => value }
         
     | 
| 
      
 92 
     | 
    
         
            +
                  else
         
     | 
| 
      
 93 
     | 
    
         
            +
                    { path_to_symource.to_sym => cumulative }
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                deep_merge!(built_hash_for_value)
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              protected
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
              def deep_merge(other_hash)
         
     | 
| 
      
 103 
     | 
    
         
            +
                merge(other_hash) do |key, oldval, newval|
         
     | 
| 
      
 104 
     | 
    
         
            +
                  oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  newval = newval.to_hash if newval.respond_to?(:to_hash)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  oldval.is_a?(Hash) && newval.is_a?(Hash) ? self.class.new(oldval).deep_merge(newval) : self.class.new(newval)
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
              def deep_merge!(other_hash)
         
     | 
| 
      
 111 
     | 
    
         
            +
                replace(deep_merge(other_hash))
         
     | 
| 
      
 112 
     | 
    
         
            +
              end
         
     | 
| 
      
 113 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ::Enumerable.module_eval do
         
     | 
| 
      
 2 
     | 
    
         
            +
              # This is named the same as its Hash counterpart for a reason. I'm not
         
     | 
| 
      
 3 
     | 
    
         
            +
              # going to tell you why, consider it my riddle for you.
         
     | 
| 
      
 4 
     | 
    
         
            +
              def recursive_symbolize_keys!
         
     | 
| 
      
 5 
     | 
    
         
            +
                each do |item|
         
     | 
| 
      
 6 
     | 
    
         
            +
                  item.recursive_symbolize_keys! if item.respond_to?(:recursive_symbolize_keys!)
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
                self
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def recursive_stringify_keys!
         
     | 
| 
      
 12 
     | 
    
         
            +
                each do |item|
         
     | 
| 
      
 13 
     | 
    
         
            +
                  item.recursive_stringify_keys! if item.respond_to?(:recursive_stringify_keys!)
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              def hardcode(enumerable)
         
     | 
| 
      
 18 
     | 
    
         
            +
                dup.hardcode!(enumerable)
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              def hardcode!(enumerable)
         
     | 
| 
      
 22 
     | 
    
         
            +
                if self.empty? && enumerable.any?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  enumerable
         
     | 
| 
      
 24 
     | 
    
         
            +
                else
         
     | 
| 
      
 25 
     | 
    
         
            +
                  each_with_index do |a, index|
         
     | 
| 
      
 26 
     | 
    
         
            +
                    if a.is_a?(Hash)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      self[index] = a.hardcode(enumerable[index]) if enumerable[index]
         
     | 
| 
      
 28 
     | 
    
         
            +
                    elsif a.is_a?(Enumerable)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      self[index] = a.collect { |v| a.hardcode(enumerable[index]) }
         
     | 
| 
      
 30 
     | 
    
         
            +
                    else
         
     | 
| 
      
 31 
     | 
    
         
            +
                      self[index] = enumerable[index] if enumerable[index]
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
    
        data/extensions/hash.rb
    ADDED
    
    | 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class ::Hash
         
     | 
| 
      
 2 
     | 
    
         
            +
              def stringify_keys!
         
     | 
| 
      
 3 
     | 
    
         
            +
                keys.each do |key|
         
     | 
| 
      
 4 
     | 
    
         
            +
                  self[key.to_s] = delete(key)
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
                self
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def stringify_keys
         
     | 
| 
      
 10 
     | 
    
         
            +
                dup.stringify_keys!
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def recursive_stringify_keys!
         
     | 
| 
      
 14 
     | 
    
         
            +
                stringify_keys!
         
     | 
| 
      
 15 
     | 
    
         
            +
                values.each do |value|
         
     | 
| 
      
 16 
     | 
    
         
            +
                  value.recursive_stringify_keys! if value.respond_to?(:recursive_stringify_keys!)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
                self
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              def symbolize_keys!
         
     | 
| 
      
 22 
     | 
    
         
            +
                keys.each do |key|
         
     | 
| 
      
 23 
     | 
    
         
            +
                  self[(key.to_sym rescue key) || key] = delete(key)
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
                self
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              def recursive_symbolize_keys!
         
     | 
| 
      
 29 
     | 
    
         
            +
                symbolize_keys!
         
     | 
| 
      
 30 
     | 
    
         
            +
                values.each do |value|
         
     | 
| 
      
 31 
     | 
    
         
            +
                  value.recursive_symbolize_keys! if value.respond_to?(:recursive_symbolize_keys!)
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                self
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              def symbolize_keys
         
     | 
| 
      
 37 
     | 
    
         
            +
                dup.symbolize_keys!
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              def hardcode(other_hash)
         
     | 
| 
      
 41 
     | 
    
         
            +
                dup.hardcode!(other_hash)
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              def hardcode!(other_hash)
         
     | 
| 
      
 45 
     | 
    
         
            +
                other_hash.each_pair do |k,v|
         
     | 
| 
      
 46 
     | 
    
         
            +
                  tv = self[k]
         
     | 
| 
      
 47 
     | 
    
         
            +
                  self[k] = tv.respond_to?(:hardcode) && v.respond_to?(:hardcode) ? tv.hardcode(v) : v
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                self
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            end
         
     | 
    
        data/initialize.rb
    ADDED
    
    
| 
         @@ -0,0 +1,99 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # An Attribute represents a specific key and value as part of a Schema.
         
     | 
| 
      
 2 
     | 
    
         
            +
            # It is responsible for casting its value and generating sample (default) data when not in live-mode.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require "uuid"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Render
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Attribute
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_accessor :name, :type, :schema, :archetype, :enums
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # Initialize take a few different Hashes
         
     | 
| 
      
 11 
     | 
    
         
            +
                # { name: { type: UUID } } for standard Hashes to be aligned
         
     | 
| 
      
 12 
     | 
    
         
            +
                # { type: UUID } for elements in an array to be parsed
         
     | 
| 
      
 13 
     | 
    
         
            +
                # { name: { type: Object, attributes { ... } } for nested schemas
         
     | 
| 
      
 14 
     | 
    
         
            +
                def initialize(options = {})
         
     | 
| 
      
 15 
     | 
    
         
            +
                  self.name = options.keys.first
         
     | 
| 
      
 16 
     | 
    
         
            +
                  if (options.keys.first == :type && !options[options.keys.first].is_a?(Hash)) # todo there has to be a better way to do this
         
     | 
| 
      
 17 
     | 
    
         
            +
                    initialize_as_archetype(options)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  else
         
     | 
| 
      
 19 
     | 
    
         
            +
                    self.type = Render.parse_type(options[name][:type])
         
     | 
| 
      
 20 
     | 
    
         
            +
                    self.enums = options[name][:enum]
         
     | 
| 
      
 21 
     | 
    
         
            +
                    initialize_schema!(options) if schema_value?(options)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def initialize_as_archetype(options)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  self.type = Render.parse_type(options[:type])
         
     | 
| 
      
 27 
     | 
    
         
            +
                  self.enums = options[:enum]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  self.archetype = true
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def initialize_schema!(options)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  schema_options = {
         
     | 
| 
      
 33 
     | 
    
         
            +
                    title: name,
         
     | 
| 
      
 34 
     | 
    
         
            +
                    type: type
         
     | 
| 
      
 35 
     | 
    
         
            +
                  }
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  definition = options[name]
         
     | 
| 
      
 38 
     | 
    
         
            +
                  if definition.keys.include?(:attributes)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    schema_options.merge!({ attributes: definition[:attributes] })
         
     | 
| 
      
 40 
     | 
    
         
            +
                  else
         
     | 
| 
      
 41 
     | 
    
         
            +
                    schema_options.merge!({ elements: definition[:elements] })
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  self.schema = Schema.new(schema_options)
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def serialize(explicit_value)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  if archetype
         
     | 
| 
      
 49 
     | 
    
         
            +
                    explicit_value || default_value
         
     | 
| 
      
 50 
     | 
    
         
            +
                  else
         
     | 
| 
      
 51 
     | 
    
         
            +
                    to_hash(explicit_value)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                def to_hash(explicit_value = nil)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  value = if schema_value?
         
     | 
| 
      
 57 
     | 
    
         
            +
                    schema.serialize(explicit_value)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  else
         
     | 
| 
      
 59 
     | 
    
         
            +
                    # TODO guarantee type for explicit value
         
     | 
| 
      
 60 
     | 
    
         
            +
                    (explicit_value || default_value)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  { name.to_sym => value }
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                def default_value
         
     | 
| 
      
 67 
     | 
    
         
            +
                  Render.live ? nil : faux_value
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def schema_value?(options = {})
         
     | 
| 
      
 71 
     | 
    
         
            +
                  return true if schema
         
     | 
| 
      
 72 
     | 
    
         
            +
                  options[name].is_a?(Hash) && (options[name][:attributes] || options[name][:elements])
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                private
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                def faux_value
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # TODO implement better #faux_value
         
     | 
| 
      
 79 
     | 
    
         
            +
                  return enums.sample if enums
         
     | 
| 
      
 80 
     | 
    
         
            +
                  return generator_value if generator_value # todo optimize generator_value call
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  case(type.name)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  when("String") then "A String"
         
     | 
| 
      
 84 
     | 
    
         
            +
                  when("Integer") then rand(1000)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  when("UUID") then UUID.generate
         
     | 
| 
      
 86 
     | 
    
         
            +
                  when("Boolean") then [true, false].sample
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def generator_value
         
     | 
| 
      
 91 
     | 
    
         
            +
                  generator = Render.generators.detect do |generator|
         
     | 
| 
      
 92 
     | 
    
         
            +
                    generator.type == type && name.match(generator.matcher)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  generator.algorithm.call if generator
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
            end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,64 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Render
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Errors
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Generator
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class MalformedAlgorithm < StandardError
         
     | 
| 
      
 5 
     | 
    
         
            +
                    attr_accessor :algorithm
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                    def initialize(algorithm)
         
     | 
| 
      
 8 
     | 
    
         
            +
                      self.algorithm = algorithm
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    def to_s
         
     | 
| 
      
 12 
     | 
    
         
            +
                      "Algorithms must respond to #call, which #{algorithm.inspect} does not."
         
     | 
| 
      
 13 
     | 
    
         
            +
                    end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                class Graph
         
     | 
| 
      
 18 
     | 
    
         
            +
                  class EndpointKeyNotFound < StandardError
         
     | 
| 
      
 19 
     | 
    
         
            +
                    attr_accessor :config_key
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    def initialize(config_key)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      self.config_key = config_key
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    def to_s
         
     | 
| 
      
 26 
     | 
    
         
            +
                      "No value for key #{config_key} found in config or parental_attributes."
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                class Schema
         
     | 
| 
      
 32 
     | 
    
         
            +
                  class NotFound < StandardError
         
     | 
| 
      
 33 
     | 
    
         
            +
                    attr_accessor :title
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    def initialize(title)
         
     | 
| 
      
 36 
     | 
    
         
            +
                      self.title = title
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    def to_s
         
     | 
| 
      
 40 
     | 
    
         
            +
                      "Schema with title #{title} is not loaded"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  class RequestError < StandardError
         
     | 
| 
      
 45 
     | 
    
         
            +
                    attr_accessor :endpoint, :response
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    def initialize(endpoint, response)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      self.endpoint = endpoint
         
     | 
| 
      
 49 
     | 
    
         
            +
                      self.response = response
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    def to_s
         
     | 
| 
      
 53 
     | 
    
         
            +
                      "Could not reach #{endpoint} because #{response}."
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  class InvalidResponse < RequestError
         
     | 
| 
      
 58 
     | 
    
         
            +
                    def to_s
         
     | 
| 
      
 59 
     | 
    
         
            +
                      "Could not parse #{response.inspect} from #{endpoint}"
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "render/errors"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Render
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Generator
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :type, :matcher, :algorithm
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(attributes = {})
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attributes.symbolize_keys!
         
     | 
| 
      
 9 
     | 
    
         
            +
                  %w(type matcher algorithm).each { |attribute| self.__send__("#{attribute}=", attributes[attribute.to_sym]) }
         
     | 
| 
      
 10 
     | 
    
         
            +
                  raise Errors::Generator::MalformedAlgorithm.new(algorithm) if !algorithm.respond_to?(:call)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/render/graph.rb
    ADDED
    
    | 
         @@ -0,0 +1,106 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # The Graph is the top-level model that defines a Schema and correlating Graphs.
         
     | 
| 
      
 2 
     | 
    
         
            +
            # It also includes particular metadata:
         
     | 
| 
      
 3 
     | 
    
         
            +
            #  - Endpoint to query for its schema's data
         
     | 
| 
      
 4 
     | 
    
         
            +
            #  - Config for this endpoint, e.g. an access token
         
     | 
| 
      
 5 
     | 
    
         
            +
            #  - Relationships between it and a Graph that includes it
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            require "render/schema"
         
     | 
| 
      
 8 
     | 
    
         
            +
            require "render/errors"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            module Render
         
     | 
| 
      
 11 
     | 
    
         
            +
              class Graph
         
     | 
| 
      
 12 
     | 
    
         
            +
                PARAM = %r{:(?<param>[\w_]+)}
         
     | 
| 
      
 13 
     | 
    
         
            +
                PARAMS = %r{#{PARAM}[\/\;\&]?}
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                attr_accessor :schema,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  :raw_endpoint,
         
     | 
| 
      
 17 
     | 
    
         
            +
                  :relationships,
         
     | 
| 
      
 18 
     | 
    
         
            +
                  :graphs,
         
     | 
| 
      
 19 
     | 
    
         
            +
                  :params,
         
     | 
| 
      
 20 
     | 
    
         
            +
                  :parental_params, # TODO rename this to inherited_params
         
     | 
| 
      
 21 
     | 
    
         
            +
                  :config
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def initialize(schema, attributes = {})
         
     | 
| 
      
 24 
     | 
    
         
            +
                  self.schema = if schema.is_a?(Symbol)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    Schema.new(schema)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  else
         
     | 
| 
      
 27 
     | 
    
         
            +
                    schema
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  self.relationships = (attributes.delete(:relationships) || {})
         
     | 
| 
      
 31 
     | 
    
         
            +
                  self.raw_endpoint = (attributes.delete(:endpoint) || "")
         
     | 
| 
      
 32 
     | 
    
         
            +
                  self.graphs = (attributes.delete(:graphs) || [])
         
     | 
| 
      
 33 
     | 
    
         
            +
                  self.config = attributes
         
     | 
| 
      
 34 
     | 
    
         
            +
                  self.parental_params = {}
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  initialize_params!
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def endpoint
         
     | 
| 
      
 40 
     | 
    
         
            +
                  uri = URI(raw_endpoint)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  uri.path.gsub!(PARAMS) do |param|
         
     | 
| 
      
 43 
     | 
    
         
            +
                    key = param_key(param)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    param.gsub(PARAM, param_value(key))
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  if uri.query
         
     | 
| 
      
 48 
     | 
    
         
            +
                    uri.query.gsub!(PARAMS) do |param|
         
     | 
| 
      
 49 
     | 
    
         
            +
                      key = param_key(param)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      "#{key}=#{param_value(key)}&"
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end.chop!
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  uri.to_s
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def pull(inherited_attributes = {})
         
     | 
| 
      
 58 
     | 
    
         
            +
                  calculate_parental_params!(inherited_attributes)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  graph_attributes = schema.pull(inherited_attributes.merge(parental_params.merge({ endpoint: endpoint })))
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  graphs.inject(graph_attributes) do |attributes, nested_graph|
         
     | 
| 
      
 62 
     | 
    
         
            +
                    threads = []
         
     | 
| 
      
 63 
     | 
    
         
            +
                    # TODO threading should be configured so people may also think about Thread.abort_on_transaction!
         
     | 
| 
      
 64 
     | 
    
         
            +
                    threads << Thread.new do
         
     | 
| 
      
 65 
     | 
    
         
            +
                      title = schema.title.to_sym
         
     | 
| 
      
 66 
     | 
    
         
            +
                      parent_data = attributes[title]
         
     | 
| 
      
 67 
     | 
    
         
            +
                      nested_graph_data = if parent_data.is_a?(Array)
         
     | 
| 
      
 68 
     | 
    
         
            +
                        data = parent_data.collect do |element|
         
     | 
| 
      
 69 
     | 
    
         
            +
                          nested_graph.pull(element)
         
     | 
| 
      
 70 
     | 
    
         
            +
                        end
         
     | 
| 
      
 71 
     | 
    
         
            +
                        key = data.first.keys.first
         
     | 
| 
      
 72 
     | 
    
         
            +
                        attributes[title] = data.collect { |d| d[key] }
         
     | 
| 
      
 73 
     | 
    
         
            +
                      else
         
     | 
| 
      
 74 
     | 
    
         
            +
                        data = nested_graph.pull(parent_data)
         
     | 
| 
      
 75 
     | 
    
         
            +
                        parent_data.merge!(data)
         
     | 
| 
      
 76 
     | 
    
         
            +
                      end
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                    threads.collect(&:join)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    DottableHash.new(attributes)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                private
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                def initialize_params!
         
     | 
| 
      
 86 
     | 
    
         
            +
                  self.params = raw_endpoint.scan(PARAMS).flatten.inject({}) do |params, param|
         
     | 
| 
      
 87 
     | 
    
         
            +
                    params.merge({ param.to_sym => nil })
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                def calculate_parental_params!(inherited)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  self.parental_params = relationships.inject(inherited) do |attributes, (parent_key, child_key)|
         
     | 
| 
      
 93 
     | 
    
         
            +
                    attributes.merge({ child_key => inherited[parent_key] })
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                def param_key(string)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  string.match(PARAM)[:param].to_sym
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def param_value(key)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  parental_params[key] || config[key] || raise(Errors::Graph::EndpointKeyNotFound.new(key))
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
            end
         
     |