hinge 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 674762229cddb1ad8dc8dc7ed1efcb6efaf1736e
4
+ data.tar.gz: eba6a6ac27016e695befd31b4e99cddd266a31da
5
+ SHA512:
6
+ metadata.gz: 7c9b6045fce65cb1781703d79a737fabfa71fb5dc708962cc27f45033ac902b87c9040f9b9d4821ab37fcbd1d300dac6e5a029a3001c20ca9ce23a46ea61cd82
7
+ data.tar.gz: cee48df84aeba75c3641dbfc00f37b3618b12fce607e18bc46b5f3f69f3fc4735f897c1cdbff1e9dfc85f8d30be0e41299ea63f4c1eb8fb8fa303649b6fe44b2
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hinge.gemspec
4
+ gemspec
5
+
6
+ gem "rspec"
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # Hinge
2
+
3
+ Hinge is a trivial dependency resolver. With Hinge, you can write down object dependencies
4
+ in a linear fashion to have a simple overview of them.
5
+
6
+ ```
7
+ class Deps
8
+ # This method implements building a logger. It has no dependencies.
9
+ def build_logger
10
+ Logger.new($stdout)
11
+ end
12
+
13
+ # This method builds an instance of the `Processor` class you want to use,
14
+ # initialized with the logger from the previous method.
15
+ # This matching is done by the name of the parameter!
16
+ def build_processor(logger)
17
+ Processor.new(logger)
18
+ end
19
+
20
+ # Named parameters can be used as well!
21
+ def build_runner(logger:)
22
+ Runner.new(logger)
23
+ end
24
+ end
25
+
26
+ deps = Deps.new
27
+ resolver = Hinge.resolver(deps)
28
+ processor = resolver.resolve(:processor)
29
+ resolver.resolve(:processor).equal?(processor) # => true (the same object is returned as last time)
30
+ ```
31
+
32
+ And that's all!
33
+
34
+ Varying dependencies depending on the environment can now easily be handled in a number of ways,
35
+ ranging from `if/else` or `case` constructs to inheritance or overriding methods of `Deps`.
36
+
37
+
38
+ ## Installation
39
+
40
+ Add this line to your application's Gemfile:
41
+
42
+ ```ruby
43
+ gem 'hinge'
44
+ ```
45
+
46
+ And then execute:
47
+
48
+ $ bundle
49
+
50
+ Or install it yourself as:
51
+
52
+ $ gem install hinge
53
+
54
+ ## Development
55
+
56
+ Tests are written in RSpec.
57
+
58
+ ## Contributing
59
+
60
+ Bug reports and pull requests are welcome on GitHub at https://github.com/schnittchen/hinge.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "hinge"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/hinge.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hinge/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hinge"
8
+ spec.version = Hinge::VERSION
9
+ spec.authors = ["Thomas Stratmann"]
10
+ spec.email = ["thomas.stratmann@9elements.com"]
11
+
12
+ spec.summary = %q{Trivial dependency resolver using introspection}
13
+ spec.description = %q{Trivial dependency resolver using introspection}
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.13"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ end
data/lib/hinge.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "hinge/version"
2
+ require 'hinge/resolver'
3
+
4
+ module Hinge
5
+ def self.resolver(container)
6
+ Resolver.new(container)
7
+ end
8
+ end
@@ -0,0 +1,95 @@
1
+ module Hinge
2
+ class Resolver
3
+ Error = Class.new(RuntimeError)
4
+ UnknownDependency = Class.new(Error)
5
+ CircularReference = Class.new(Error)
6
+
7
+ def initialize(container)
8
+ @container = container
9
+ @nodes = {}
10
+ @resolved = {}
11
+ end
12
+ attr_reader :container
13
+ attr_reader :resolved
14
+
15
+ def resolve(name)
16
+ return resolved[name] if resolved.key?(name)
17
+
18
+ raise ArgumentError unless name.is_a?(Symbol)
19
+
20
+ node = node(name)
21
+ populate_node(node)
22
+
23
+ resolved[name]
24
+ end
25
+
26
+ private
27
+
28
+ def populate_node(node, history = [])
29
+ unless node.method
30
+ complain(UnknownDependency, node, history, "method #{node.method_name} not found")
31
+ end
32
+ if history.include?(node.name)
33
+ complain(CircularReference, node, history, "dependency #{node.name} depends on itself")
34
+ end
35
+
36
+ node.dependency_names.each do |dep_name|
37
+ populate_node node(dep_name), history + [node.name]
38
+ end
39
+
40
+ ordered_dependent_values =
41
+ node.dependency_names.map do |dep_name|
42
+ @resolved[dep_name]
43
+ end
44
+ @resolved[node.name] = node.invoke(ordered_dependent_values)
45
+ end
46
+
47
+ def complain(exn, node, history, msg = "")
48
+ raise exn, "#{msg} (resolving #{[*history, node.name].join(" -> ")})"
49
+ end
50
+
51
+ def node(name)
52
+ @nodes[name] ||= Node.new(name, container)
53
+ end
54
+
55
+ class Node
56
+ def initialize(name, container)
57
+ @name = name
58
+ @method_name = "build_#{@name}"
59
+ @method =
60
+ begin
61
+ container.method(@method_name)
62
+ rescue NameError
63
+ nil
64
+ end
65
+
66
+ if @method
67
+ @dependency_names = []
68
+ @positional_args_count = 0
69
+ @keyword_arg_names = []
70
+
71
+ @method.parameters.each do |kind, argument_name|
72
+ argument_name = argument_name.to_sym
73
+ @dependency_names << argument_name
74
+ case kind
75
+ when :req
76
+ @positional_args_count += 1
77
+ when :keyreq
78
+ @keyword_arg_names << argument_name
79
+ end
80
+ end
81
+ end
82
+ end
83
+ attr_reader :name, :method_name, :method, :dependency_names
84
+
85
+ def invoke(ordered_values)
86
+ splat = ordered_values.take(@positional_args_count)
87
+ splat << Hash[
88
+ @keyword_arg_names.zip(ordered_values.drop(@positional_args_count))
89
+ ] if @keyword_arg_names.any?
90
+ @method.call(*splat)
91
+ end
92
+ end
93
+ end
94
+ end
95
+
@@ -0,0 +1,3 @@
1
+ module Hinge
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hinge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Thomas Stratmann
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-03-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Trivial dependency resolver using introspection
42
+ email:
43
+ - thomas.stratmann@9elements.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".rspec"
50
+ - Gemfile
51
+ - README.md
52
+ - Rakefile
53
+ - bin/console
54
+ - bin/setup
55
+ - hinge.gemspec
56
+ - lib/hinge.rb
57
+ - lib/hinge/resolver.rb
58
+ - lib/hinge/version.rb
59
+ homepage:
60
+ licenses: []
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.5.2
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Trivial dependency resolver using introspection
82
+ test_files: []