ruse 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ed5777f12733b8e7e6ed8fc9711354f77b93db58
4
+ data.tar.gz: 62796162f3a934ff9668cc39f029b7bb6b58947f
5
+ SHA512:
6
+ metadata.gz: f5a5251bc60259ea099c2cf2f5402f8dd2b642c8b0ae736305ecfc71e28398ac911b6e68c3e017f9267ab7b1b3da5974f563d3f784ff55496e37fd4ab08fdbd1
7
+ data.tar.gz: 1a1a86387be403adfa675b87b9d0b91f3ea401939da9bd8937dddec6c4a34d3cca3948e86f4bc228f0d98e92ab9244869dcc04850542140b4cbb9768203ceb06
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruse.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Joshua Flanagan
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,120 @@
1
+ # Ruse
2
+
3
+ Ruse is a low friction dependency injection tool for ruby.
4
+
5
+ It was inspired by the injector in [angular.js](http://docs.angularjs.org/guide/di)
6
+ (and so, transitively, [Guice](https://code.google.com/p/google-guice/)).
7
+
8
+ ## Usage
9
+
10
+ Create an injector at the top level of your application:
11
+
12
+ ```ruby
13
+ injector = Ruse.create_injector
14
+ ```
15
+
16
+ Retrieve instances via that injector:
17
+
18
+ ```ruby
19
+ command = injector.get :create_order_command
20
+ #=> #<CreateOrderCommand:0x00000105cbea70>
21
+ ```
22
+
23
+ ## Example
24
+
25
+ Suppose you have a command that collaborates with a notifier service:
26
+
27
+ ```ruby
28
+ class CreateOrderCommand
29
+ def execute(customer, order_details)
30
+ save_order(order_details)
31
+ notifier.notify(customer)
32
+ end
33
+
34
+ def notifier
35
+ @notifier ||= Notifier.new
36
+ end
37
+ end
38
+
39
+ class Notifier
40
+ def notify(customer)
41
+ # send a notification to customer
42
+ end
43
+ end
44
+ ```
45
+
46
+ That `CreateOrderCommand` class is now tightly coupled to the `Notifier` class.
47
+ It has to know how to construct the Notifier. You would have to change
48
+ `CreateOrderCommand` to use a different notifier service.
49
+
50
+ You can improve the class by using dependency injection:
51
+
52
+
53
+ ```ruby
54
+ class CreateOrderCommand
55
+ def initialize(notifier)
56
+ @notifier = notifier
57
+ end
58
+
59
+ def execute(customer, order_details)
60
+ save_order(order_details)
61
+ @notifier.notify(customer)
62
+ end
63
+ end
64
+ ```
65
+
66
+ The `CreateOrderCommand` class is now open for extension, but closed for
67
+ modification. It can use a different notifier service, without any changes.
68
+ It also does not need to know how to construct a `Notifier` instance, which is
69
+ really important if `Notifier` has its own dependencies.
70
+
71
+ You have now passed the burden of creating and configuring the
72
+ `CreateOrderCommand`, the `Notifier`, and any of its dependencies on to the
73
+ caller. This is where an Inversion of Control/Dependency Injection tool becomes
74
+ valuable:
75
+
76
+ ```ruby
77
+ command = injector.get "CreateOrderCommand"
78
+ ```
79
+
80
+ The tool did the tedious work of identifying and populating the dependencies,
81
+ all the way down the object graph.
82
+
83
+ ## Instance Resolution
84
+
85
+ Dependencies are determined by the identifiers used in constructure parameters.
86
+ This was lifted directly from angular.js, and I believe may be the key to
87
+ reducing the overhead in using a tool like this. Your dependency consuming
88
+ classes do not have to be annotated or registered in any way.
89
+
90
+ In this early alpha state, identifiers are resolved to types through simple
91
+ string manipulation (similiar to ActiveSupport's `classify` and `constantize`).
92
+ That means you can get an instance of `SomeService` by requesting
93
+ `"SomeService"`, `"some_service"` or `:some_service`.
94
+
95
+ In the future, I can imagine a simple configuration mechanism that lets you
96
+ resolve an identifier to some other type, so `"notifier"` resolves to
97
+ `EmailNotifier`.
98
+
99
+ ## Installation
100
+
101
+ Add this line to your application's Gemfile:
102
+
103
+ gem 'ruse'
104
+
105
+ And then execute:
106
+
107
+ $ bundle
108
+
109
+ Or install it yourself as:
110
+
111
+ $ gem install ruse
112
+
113
+
114
+ ## Contributing
115
+
116
+ 1. [Fork it](http://github.com/joshuaflanagan/ruse/fork)
117
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
118
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
119
+ 4. Push to the branch (`git push origin my-new-feature`)
120
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task default: :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs.push "lib"
8
+ t.test_files = FileList['specs/*_spec.rb']
9
+ t.verbose = true
10
+ end
@@ -0,0 +1,8 @@
1
+ require "ruse/injector"
2
+ require "ruse/version"
3
+
4
+ module Ruse
5
+ def self.create_injector
6
+ Ruse::Injector.new
7
+ end
8
+ end
@@ -0,0 +1,32 @@
1
+ module Ruse
2
+ class Injector
3
+ def get(identifier)
4
+ type = resolve_type identifier
5
+ args = resolve_dependencies type
6
+ type.new *args
7
+ end
8
+
9
+ def resolve_type(identifier)
10
+ type_name = classify(identifier)
11
+ unless Object.const_defined?(type_name)
12
+ raise UnknownServiceError.new(type_name)
13
+ end
14
+ Object.const_get type_name
15
+ end
16
+
17
+ def resolve_dependencies(type)
18
+ type.instance_method(:initialize).parameters.map{|_, identifier|
19
+ get identifier
20
+ }
21
+ end
22
+
23
+ def classify(term)
24
+ # lifted from active_support gem: lib/active_support/inflector/methods.rb
25
+ string = term.to_s
26
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
27
+ string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{ $2.capitalize}" }.gsub('/', '::')
28
+ end
29
+ end
30
+
31
+ class UnknownServiceError < StandardError; end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Ruse
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ruse/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ruse"
8
+ spec.version = Ruse::VERSION
9
+ spec.authors = ["Joshua Flanagan"]
10
+ spec.email = ["joshuaflanagan@gmail.com"]
11
+ spec.summary = %q{Ruse}
12
+ spec.description = %q{Ruse}
13
+ spec.homepage = "https://github.com/joshuaflanagan/ruse"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,44 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'ruse/injector'
4
+
5
+ describe Ruse::Injector do
6
+ it "retrieves an instance it can infer from an identifier" do
7
+ injector = Ruse::Injector.new
8
+ injector.get("SomeService").must_be_instance_of(SomeService)
9
+ end
10
+
11
+ it "raises UnknownServiceError for an identifier it cannot resolve" do
12
+ injector = Ruse::Injector.new
13
+ ->{
14
+ injector.get("cannot_be_resolved")
15
+ }.must_raise(Ruse::UnknownServiceError)
16
+ end
17
+
18
+ it "populates dependencies for the instance it retrieves" do
19
+ injector = Ruse::Injector.new
20
+ instance = injector.get("ServiceConsumer")
21
+ instance.must_be_instance_of(ServiceConsumer)
22
+ instance.service1.must_be_instance_of(SomeService)
23
+ instance.service2.must_be_instance_of(OtherService)
24
+ end
25
+
26
+ class SomeService; end
27
+ class OtherService; end
28
+
29
+ class ServiceConsumer
30
+ attr_reader :service1, :service2
31
+ def initialize(some_service, other_service)
32
+ @service1 = some_service
33
+ @service2 = other_service
34
+ end
35
+ end
36
+ end
37
+
38
+ # classify will be moved
39
+ describe "classify" do
40
+ it "converts an underscored_term to PascalCase" do
41
+ resolver = Ruse::Injector.new
42
+ resolver.classify("camel_case").must_equal("CamelCase")
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'ruse'
4
+
5
+ describe Ruse do
6
+ it "can create an injector" do
7
+ Ruse.create_injector.must_be_kind_of Ruse::Injector
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruse
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Flanagan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ type: :development
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.6'
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ type: :development
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Ruse
42
+ email:
43
+ - joshuaflanagan@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - lib/ruse.rb
54
+ - lib/ruse/injector.rb
55
+ - lib/ruse/version.rb
56
+ - ruse.gemspec
57
+ - specs/injector_spec.rb
58
+ - specs/ruse_spec.rb
59
+ homepage: https://github.com/joshuaflanagan/ruse
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 2.1.11
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Ruse
83
+ test_files: []