dinject 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0117d663baa4930c4d3fc4ecb07590964299de2d
4
+ data.tar.gz: 675365a6dd4ba61f0de0550c05c2c5c4c5bcc187
5
+ SHA512:
6
+ metadata.gz: ce89a7c1e23f1c3e13378ff4c58e4cfe471eb41efa1980cb327ec0f50a983562d0c72f7e0f15d4d1c51b09b86f278beba98cd9863dacc32da61ff998ec542481
7
+ data.tar.gz: 8deff621d8903d14bff29c73e8902146c28568d1ed704f10c6725ab5fa2c68467a2dcbae15c728934721d1a831c2eea0760b6dd8e60dd9268c6df6e2d99c4a8c
data/.gitignore ADDED
@@ -0,0 +1,22 @@
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
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in inject.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Danyel Bayraktar
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.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Inject
2
+
3
+ Inspired by angular's injection capabilities, this library provides some dependency injection
4
+ via the `injector` module.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'inject'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install inject
19
+
20
+ ## Usage
21
+
22
+ Install the gem, then use it like this:
23
+
24
+ ```ruby
25
+ require 'inject'
26
+ # Create injector
27
+ injector = Injector.new
28
+ # same as injector.insert(:epoch, :setter, Time.now, before: :all)
29
+ injector.epoch = Time.now
30
+
31
+ # Add a rule for `ip_address` that queries ipecho.net if potential other methods fail
32
+ # The `after: :all` ensures that this is only run if other rules fail
33
+ injector.insert(:ip_address, :ipecho, -> { open("http://ipecho.net/plain").read }, after: :all)
34
+
35
+ # Add another rule for `ip_address`.
36
+ # We name this rule "ifconfig" as it calls that UNIX command to retrieve network information
37
+ # from the system. We assume a call to `ifconfig` is faster than a HTTP request, so we prioritize
38
+ # this over the :ipecho method.
39
+ injector.insert(:ip_address, :ifconfig, -> { SomeModule.extract_ip(`ifconfig`) }, before: :ipecho)
40
+
41
+ # We may also use a gem that does the job for us - presumably, it uses some native syscalls
42
+ # and is the fastest method, so we give it the highest priority.
43
+ injector.insert(:ip_address, :some_gem, -> { require 'some_gem'; SomeGem.ip_address }, before: :all)
44
+
45
+ # Use the variables. The arguments will be injected by name.
46
+ # If any of the arguments associates with a non-existing rule,
47
+ # a RuleNotFound error will be raised.
48
+ # Optional arguments (as in `|x, y = 42|`) have the same effect.
49
+ # For optional arguments, you must use the key arguments (`|x, y: 42|`) introduced in Ruby 2.0
50
+ # Note that rules that return `nil` will override those.
51
+ injector.inject do |ip_address, epoch|
52
+ puts "ip: #{ip_address}, epoch: #{epoch}"
53
+ end
54
+ ```
55
+
56
+ The injector works with a priority queue, so in the above example, it will try to retrieve
57
+ the ip_address *lazily* using `some_gem`. If `SomeGem.ip_address` returned `nil`, the next
58
+ rule `ifconfig` will be used and if that returns nil, a call to `ipecho.net` will be made via the
59
+ `ipecho` rule. If all rules return `nil`, `nil` is returned. If no rules exist,
60
+ `RuleNotFound` will be raised (or `NoMethodError` if called directly via e.g. `injector.ip_address`)
61
+
62
+ ## Contributing
63
+
64
+ 1. Fork it
65
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
66
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
67
+ 4. Push to the branch (`git push origin my-new-feature`)
68
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/inject.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'inject/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dinject"
8
+ spec.version = Inject::VERSION
9
+ spec.authors = ["Danyel Bayraktar"]
10
+ spec.email = ["cydrop@gmail.com"]
11
+ spec.summary = %q{Inject objects into variables by name or symbol}
12
+ spec.description = %q{Inspired by angular's inject, with addition of rules and flexibility.}
13
+ spec.homepage = ""
14
+ spec.licenses = ["MIT"]
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.required_ruby_version = ">= 2.0.0"
21
+ spec.add_dependency "pqueue", "~> 2.1"
22
+ end
data/lib/inject.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'inject/version'
2
+ require 'injector'
@@ -0,0 +1,17 @@
1
+ class Inject::Rule
2
+ attr_reader :key, :identifier, :value, :options
3
+
4
+ def initialize(key, identifier, value, **options)
5
+ @key = key
6
+ @identifier = identifier
7
+ @value = value
8
+ @options = options
9
+ end
10
+
11
+ def <=>(other)
12
+ # this one has before: :all or before: other
13
+ [other.identifier, :all].include?(self.options[:before]) ||
14
+ # other one has after: :all, or after: self
15
+ [self.identifier, :all].include?(other.options[:after])
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Inject
2
+ VERSION = "0.1.0"
3
+ end
data/lib/injector.rb ADDED
@@ -0,0 +1,141 @@
1
+ require 'pqueue'
2
+ require 'inject/rule'
3
+
4
+ class Injector
5
+ class RuleNotFound < StandardError; end
6
+
7
+ def initialize(**params)
8
+ @rules = {}
9
+ @injected = {injector: self}
10
+ @callbacks = {}
11
+ params.each do |k, v|
12
+ set(k, v)
13
+ end
14
+ end
15
+
16
+ def get(key, overrides = {})
17
+ # (lazily) evaluate rule, then store it
18
+ @injected[key] ||= begin
19
+ arr = @rules[key]
20
+ if arr.nil?
21
+ raise RuleNotFound,
22
+ "No rule for #{key.inspect}. Available fields: #{@rules.keys.map(&:inspect).join(', ')}"
23
+ end
24
+ result = nil
25
+ arr.to_a.reverse.each do |rule|
26
+ rule.value.tap do |x|
27
+ # if it's a callable value, call that and return its result, else return itself
28
+ result = if x.is_a?(Proc) or x.is_a?(Method)
29
+ inject(overrides, &x)
30
+ else
31
+ x
32
+ end
33
+ end
34
+ unless result.nil?
35
+ fire(:after_fetch, key, result, rule.identifier)
36
+ break
37
+ end
38
+ end
39
+ result
40
+ end
41
+ end
42
+
43
+ def inject(overrides = {}, &block)
44
+ params = []
45
+ keys = {}
46
+ block.parameters.each_with_index do |(type, value), index|
47
+ raise(
48
+ ArgumentError,
49
+ [
50
+ "splash operator arguments are not permitted when injecting.",
51
+ ":#{value} in #{ block.source_location }"
52
+ ].join(" ")
53
+ ) if type == :rest or type == :keyrest
54
+
55
+ # fetch by index first! e.g. inject(0 => 40) { |x| x + 2 }
56
+ result = overrides.fetch(index) do
57
+ # then fetch by value
58
+ overrides.fetch(value) do
59
+ puts type
60
+ # Unfortunately, optional arguments are not possible, as
61
+ # a Ruby block passed to `inject` will have exclusively optional arguments.
62
+ # Instead, key arguments introduced in Ruby 2.0 should be used.
63
+ if type == :key
64
+ begin self.get(value)
65
+ rescue RuleNotFound
66
+ end
67
+ else
68
+ self.get(value)
69
+ end
70
+ end
71
+ end
72
+ if type == :key
73
+ keys[value] = result
74
+ else
75
+ params << result
76
+ end
77
+ end
78
+
79
+ if keys.empty?
80
+ block.call(*params)
81
+ else
82
+ block.call(*params, keys)
83
+ end
84
+ end
85
+
86
+ def set(key, value)
87
+ insert(key, :setter, value, before: :all)
88
+ end
89
+
90
+ alias :[] :get
91
+ alias :[]= :set
92
+
93
+ def when_fetched(key = nil, &block)
94
+ add_callback(:after_fetch, key, &block)
95
+ end
96
+
97
+ def insert(key, identifier = nil, block, **options)
98
+ insert_rule(Inject::Rule.new(key, identifier, block, **options))
99
+ end
100
+ alias_method :provide, :insert
101
+
102
+ def insert_rules(rules)
103
+ rules.each(&method(:insert_rule))
104
+ end
105
+
106
+ def insert_rule(rule)
107
+ (@rules[rule.key] ||= PQueue.new) << rule
108
+ define_singleton_method(rule.key) { get(rule.key) }
109
+ end
110
+
111
+ def invalidate(key, identifier = :all)
112
+ if queue = @rules[key]
113
+ new_queue = case identifier
114
+ when :all then []
115
+ else
116
+ queue.to_a.delete_if do |rule|
117
+ rule.identifier == identifier
118
+ end
119
+ end
120
+ queue.replace(new_queue)
121
+ true
122
+ end
123
+ end
124
+
125
+ private
126
+ def add_callback(event, key, &block)
127
+ key ||= block.parameters.first.last
128
+ ((@callbacks[event] ||= {})[key] ||= []) << block
129
+ end
130
+
131
+ def fire(event, key, value, identifier)
132
+ @callbacks.fetch(event, {}).fetch(key, []).each do |block|
133
+ inject(method: identifier, 0 => value, &block)
134
+ end
135
+ end
136
+
137
+ def method_missing(method, *args)
138
+ super unless method.to_s.end_with? "="
139
+ self.set(method[0...-1].to_sym, *args)
140
+ end
141
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dinject
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Danyel Bayraktar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pqueue
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
27
+ description: Inspired by angular's inject, with addition of rules and flexibility.
28
+ email:
29
+ - cydrop@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - Gemfile
36
+ - LICENSE.txt
37
+ - README.md
38
+ - Rakefile
39
+ - inject.gemspec
40
+ - lib/inject.rb
41
+ - lib/inject/rule.rb
42
+ - lib/inject/version.rb
43
+ - lib/injector.rb
44
+ homepage: ''
45
+ licenses:
46
+ - MIT
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 2.0.0
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.4.5.1
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Inject objects into variables by name or symbol
68
+ test_files: []