needs_resources 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.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013, David McCullars
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,4 @@
1
+ needs_resources
2
+ ===============
3
+
4
+ A lightweight Inversion of Control provider of resources for an application
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,35 @@
1
+ require 'set'
2
+ require 'active_support'
3
+ require 'active_support/core_ext'
4
+
5
+ module NeedsResources
6
+
7
+ def needs_resources(*names)
8
+ container = is_a?(ResourceContainer) ? self : TopLevelResources.instance
9
+ names.flatten.each do |name|
10
+ if is_a? Class
11
+ define_method name, lambda { container[name] }
12
+ end
13
+ define_singleton_method name, lambda { container[name] }
14
+ container.resources_needed << name.to_sym
15
+ end
16
+ end
17
+
18
+ alias :needs_resource :needs_resources
19
+
20
+ def self.ensure_resources
21
+ if (missing = missing_resources).present?
22
+ # TODO: maybe prompt user if missing?
23
+ raise MissingResourceError.new(*missing)
24
+ end
25
+ end
26
+
27
+ def self.missing_resources
28
+ TopLevelResources.instance.missing_resources
29
+ end
30
+
31
+ end
32
+
33
+ Dir[File.expand_path('../needs_resources/**/*.rb', __FILE__)].sort.each do |f|
34
+ require f
35
+ end
@@ -0,0 +1,9 @@
1
+ module NeedsResources
2
+ class InvalidOrCorruptedResourcesError < StandardError
3
+
4
+ def initialize
5
+ super "Invalid or corrupted resources config file"
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module NeedsResources
2
+ class InvalidResourceAliasError < StandardError
3
+
4
+ def initialize(name)
5
+ super "Invalid resource alias in config file: #{name.inspect}"
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module NeedsResources
2
+ class InvalidResourceKeyError < StandardError
3
+
4
+ def initialize(name)
5
+ super "Invalid resource key in config file: #{name.inspect}"
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module NeedsResources
2
+ class InvalidResourceValueError < StandardError
3
+
4
+ def initialize(name, value)
5
+ super "Invalid resource value in config file for #{name.inspect}: #{value.inspect}"
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module NeedsResources
2
+ class MissingResourceError < StandardError
3
+
4
+ def initialize(*names)
5
+ if names.size == 1
6
+ super "The following resources is missing: #{names.first}."
7
+ else
8
+ super "The following resources are missing: #{names.to_sentence}."
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module NeedsResources
2
+ module NestedResourceType
3
+
4
+ def self.included(base)
5
+ base.send :include, ResourceType
6
+ base.send :include, ResourceContainer
7
+ base.send :include, NeedsResources
8
+
9
+ base.attr :resources, :default => {}
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module NeedsResources
2
+ class RequiredAttributeError < StandardError
3
+
4
+ def initialize(clazz, name)
5
+ super "Missing resource in config file: #{clazz} (in #{name})"
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ module NeedsResources
2
+ module ResourceContainer
3
+
4
+ def [](name)
5
+ resources[name] or raise MissingResourceError.new(child_resource_name name)
6
+ end
7
+
8
+ def child_resource_name(child_name)
9
+ if respond_to?(:name)
10
+ "#{self.name}.#{child_name}"
11
+ else
12
+ child_name.to_s
13
+ end
14
+ end
15
+
16
+ def resources
17
+ @resources ||= {}.with_indifferent_access
18
+ end
19
+
20
+ def resources_needed
21
+ @resources_needed ||= Set.new
22
+ end
23
+
24
+ def missing_resources
25
+ __missing_resources__(missing = {})
26
+ missing.map do |k, v|
27
+ k.child_resource_name(v)
28
+ end.sort
29
+ end
30
+
31
+ def __missing_resources__(missing)
32
+ resources_needed.each do |name|
33
+ if !resources[name]
34
+ missing[self] = name
35
+ elsif resources[name].is_a? ResourceContainer
36
+ resources[name].__missing_resources__(missing)
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ module NeedsResources
2
+ module ResourceType
3
+
4
+ def self.included(base)
5
+ children[base.name.demodulize.underscore] = base
6
+ base.instance_eval do
7
+ def attr(*names)
8
+ options = names.last.is_a?(Hash) ? names.pop : {}
9
+ names.flatten.each do |n|
10
+ attributes[n] = options.with_indifferent_access
11
+ attr_reader n
12
+ end
13
+ end
14
+
15
+ def attributes
16
+ @attributes ||= {}.with_indifferent_access
17
+ end
18
+ end
19
+ base.attr :name, :required => true
20
+ end
21
+
22
+ def self.children
23
+ @children ||= {}
24
+ end
25
+
26
+ def initialize(args={})
27
+ args = args.with_indifferent_access
28
+ self.class.attributes.each do |name, options|
29
+ value = args.delete(name) || options[:default]
30
+ if options[:required] && value.nil?
31
+ raise RequiredAttributeError.new(self, name)
32
+ end
33
+ value = value.with_indifferent_access if value.is_a? Hash
34
+ instance_variable_set "@#{name}", value
35
+ end
36
+ args.each do |k, v|
37
+ warn "Invalid initializer argument (#{k.inspect}) for class #{self}"
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,85 @@
1
+ require 'singleton'
2
+ require 'yaml'
3
+
4
+ module NeedsResources
5
+ class TopLevelResources
6
+
7
+ include Singleton
8
+ include ResourceContainer
9
+
10
+ private
11
+
12
+ def resources
13
+ @resources ||= parse(resource_config_file)
14
+ end
15
+
16
+ def resource_config_file
17
+ Dir[ENV['RESOURCES'].to_s, File.expand_path('~/.resources'), '.resources'].first
18
+ end
19
+
20
+ def parse(file)
21
+ resources = {}.with_indifferent_access
22
+ return resources unless File.exist? file
23
+
24
+ yaml = YAML.load_file(file)
25
+ raise InvalidOrCorruptedResources unless yaml.is_a? Hash
26
+
27
+ yaml.each do |name, value|
28
+ prefix, type = parse_resource_name(name)
29
+ resources[name] = parse_resource_value(name, type, value)
30
+
31
+ # Make sure both default_xyz and xyz are aliases
32
+ if prefix == 'default_'
33
+ resources[type] ||= name.to_sym
34
+ elsif prefix.nil?
35
+ resources[type] ||= "default_#{type}".to_sym
36
+ end
37
+ end
38
+
39
+ # Copy symbol aliases
40
+ resources.each do |name, value|
41
+ resources[name] = dereference_alias(resources, name, value) or raise InvalidResourceAliasError.new(value)
42
+ end
43
+
44
+ resources
45
+ end
46
+
47
+ def top_level_names
48
+ ResourceType.children.keys
49
+ end
50
+
51
+ def parse_resource_name(name)
52
+ if m = /^(.*_)?(#{top_level_names * '|'})$/.match(name)
53
+ m[1..2]
54
+ else
55
+ raise InvalidResourceKeyError.new(name)
56
+ end
57
+ end
58
+
59
+ def parse_resource_value(name, type, value)
60
+ case value
61
+ when Symbol
62
+ value
63
+ when Hash
64
+ ResourceType.children[type].new(value.merge :name => name)
65
+ else
66
+ raise InvalidResourceValueError.new(name, value)
67
+ end
68
+ end
69
+
70
+ def dereference_alias(hash, name, value)
71
+ considered = Set.new
72
+ while value.is_a?(Symbol) and !considered.include?(value)
73
+ considered << name
74
+ name = value
75
+ value = hash[value]
76
+ end
77
+ value
78
+ end
79
+
80
+ def to_s
81
+ "Top Level Resources"
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ module NeedsResources
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/needs_resources/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["David McCullars"]
6
+ gem.email = ["david.mccullars@gmail.com"]
7
+ gem.description = %q{Ruby gem to provide lightweight inversion of control type resources for an application}
8
+ gem.summary = %q{Ruby gem to provide lightweight inversion of control type resources for an application}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "needs_resources"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = NeedsResources::VERSION
17
+
18
+ gem.add_dependency('active_support', '> 2.0')
19
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: needs_resources
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David McCullars
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: active_support
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>'
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>'
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ description: Ruby gem to provide lightweight inversion of control type resources for
31
+ an application
32
+ email:
33
+ - david.mccullars@gmail.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - .gitignore
39
+ - Gemfile
40
+ - LICENSE
41
+ - README.md
42
+ - Rakefile
43
+ - lib/needs_resources.rb
44
+ - lib/needs_resources/invalid_or_corrupted_resources_error.rb
45
+ - lib/needs_resources/invalid_resource_alias_error.rb
46
+ - lib/needs_resources/invalid_resource_key_error.rb
47
+ - lib/needs_resources/invalid_resource_value_error.rb
48
+ - lib/needs_resources/missing_resource_error.rb
49
+ - lib/needs_resources/nested_resource_type.rb
50
+ - lib/needs_resources/required_attribute_error.rb
51
+ - lib/needs_resources/resource_container.rb
52
+ - lib/needs_resources/resource_type.rb
53
+ - lib/needs_resources/top_level_resources.rb
54
+ - lib/needs_resources/version.rb
55
+ - needs_resources.gemspec
56
+ homepage: ''
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.24
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Ruby gem to provide lightweight inversion of control type resources for an
80
+ application
81
+ test_files: []