monkey-lib 0.2.1 → 0.3.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.
data/README.rdoc CHANGED
@@ -0,0 +1,12 @@
1
+ Some description goes here.
2
+
3
+ == Running the specs
4
+
5
+ Run specs with all backends:
6
+ rake spec
7
+
8
+ Run specs with active_support:
9
+ rake spec:active_support
10
+
11
+ Run specs with backports and using mspec instead of rspec (so it might work on incomplete ruby implementations):
12
+ SPEC_RUNNER=mspec rake spec:backports
data/Rakefile CHANGED
@@ -1,49 +1,113 @@
1
- require "spec/rake/spectask"
2
1
  require "rake/clean"
3
- require "rake/rdoctask"
4
- require "rake/gempackagetask"
5
- require "rubygems/specification"
6
-
7
- MONKEY_VERSION = "0.2.1"
8
-
9
- spec = Gem::Specification.new do |s|
10
- s.name = "monkey-lib"
11
- s.version = MONKEY_VERSION
12
- s.authors = ["Konstantin Haase"]
13
- s.description = "collection of sane ruby extensions"
14
- s.email = "konstantin.mailinglists@googlemail.com"
15
- s.extra_rdoc_files = Dir["*.rdoc", "LICENSE", "lib/**/*.rb"]
16
- s.files = Dir["LICENSE", "Rakefile", "README.rdoc", "lib/**/*.rb", "spec/**/*.rb"]
17
- s.has_rdoc = true
18
- s.homepage = "http://github.com/rkh/monkey-lib"
19
- s.require_paths = ["lib"]
20
- s.summary = s.description
21
- s.add_dependency "extlib"
22
- end
23
-
24
- Rake::GemPackageTask.new(spec) do |pkg|
25
- pkg.gem_spec = spec
26
- end
27
-
28
- desc "install the gem locally"
29
- task :install => [:package] do
30
- sh %{gem install pkg/monkey-lib-#{MONKEY_VERSION}.gem}
31
- end
32
-
33
- desc "create a gemspec file"
34
- task :make_spec do
35
- File.open("monkey-lib.gemspec", "w") do |file|
36
- file.puts spec.to_ruby
2
+
3
+ task :default => :spec
4
+
5
+ backends = Dir.glob('lib/monkey/backend/*.rb').map { |f| File.basename f, ".rb" }
6
+ modes = [:autodetect, :explicit]
7
+
8
+ CLEAN.include "**/*.rbc"
9
+ CLOBBER.include "monkey-lib*.gem"
10
+
11
+ setup_rspec = proc do
12
+ require "spec/rake/spectask"
13
+ SPEC_RUNNER = "mspec"
14
+ def define_spec_task(name, ruby_cmd, pattern)
15
+ Spec::Rake::SpecTask.new name do |t|
16
+ t.spec_opts = %w[-c --format progress --loadby mtime --reverse]
17
+ t.ruby_cmd = ruby_cmd
18
+ t.pattern = pattern
19
+ end
37
20
  end
38
- end
39
-
40
- Rake::RDocTask.new("doc") do |rdoc|
41
- rdoc.rdoc_dir = 'doc'
42
- rdoc.options += %w[--all --inline-source --line-numbers --main README.rdoc --quiet
43
- --tab-width 2 --title monkey-lib]
44
- rdoc.rdoc_files.add ['*.rdoc', 'lib/**/*.rb']
45
- end
46
-
47
- Spec::Rake::SpecTask.new('spec') do |t|
48
- t.spec_files = Dir.glob 'spec/**/*_spec.rb'
49
- end
21
+ end
22
+
23
+ setup_mspec = proc do
24
+ require "mspec"
25
+ SPEC_RUNNER = "mspec"
26
+ def define_spec_task(name, ruby_cmd, pattern)
27
+ task(name) { sh "#{ruby_cmd} -S mspec-run #{pattern}" }
28
+ end
29
+ end
30
+
31
+ case ENV['SPEC_RUNNER']
32
+ when "rspec" then setup_rspec.call
33
+ when "mspec" then setup_mspec.call
34
+ when nil
35
+ # yes, this code is trying to be smart, but let me have some fun, please?
36
+ raise @spec_load_error unless [setup_rspec, setup_mspec].any? { |b| b.call || true rescue (@spec_load_error ||= $!) && false }
37
+ else
38
+ puts "sorry, currently no #{ENV['SPEC_RUNNER']} support"
39
+ exit 1
40
+ end
41
+
42
+ def spec_task(name, backend = nil, mode = nil)
43
+ desc "runs specs #{"with backend #{backend} " if backend}#{"(#{mode} mode)" if mode}"
44
+ define_spec_task(name, "BACKEND=#{backend.to_s.inspect} BACKEND_SETUP=#{mode.to_s.inspect} #{ENV['RUBY'] || RUBY}", "spec/monkey/**/*_spec.rb")
45
+ end
46
+
47
+ task :environment do
48
+ $LOAD_PATH.unshift "lib"
49
+ require "monkey"
50
+ end
51
+
52
+ desc "run all specs"
53
+ task :spec => "spec:all"
54
+ namespace :spec do
55
+
56
+ desc "runs specs without explicitly setting a backend"
57
+ spec_task :autodetect
58
+
59
+ backends.each do |backend|
60
+ task :all => backend
61
+
62
+ desc "runs specs with backend #{backend}"
63
+ task backend => "#{backend}:all"
64
+
65
+ namespace backend do
66
+ modes.each do |mode|
67
+ task :all => mode
68
+ spec_task mode, backend, mode
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+
75
+ namespace :backend do
76
+
77
+ desc "lists all available backends"
78
+ task(:list) { puts backends }
79
+
80
+ desc "lists expectations a backend has to meet"
81
+ task :expectations => :environment do
82
+ Monkey::Ext.expectations.each do |core_class, methods|
83
+ print "#{core_class}:".ljust(10)
84
+ puts methods.map { |n| "##{n}" }.join(", ")
85
+ end
86
+ end
87
+
88
+ desc "checks whether specs for expectations are present"
89
+ task :spec_check =>:environment do
90
+ Monkey::Ext.expectations.each do |core_class, methods|
91
+ path = "spec/monkey/ext/#{core_class.name.to_const_path}_spec.rb"
92
+ list = path.file_exists? ? %x[spec -d -fs #{path} -e "Monkey::Ext::#{core_class.name} backend expectations"] : ""
93
+ methods.each { |m| puts "missing specs for #{core_class.name}##{m}" unless list =~ /- imports #{m} from backend/ }
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ ############
100
+ # aliases
101
+
102
+ namespace(:b) do
103
+ task :e => "backend:expectations"
104
+ task :l => "backend:list"
105
+ task :s => "backend:spec_check"
106
+ end
107
+
108
+ namespace(:s) do
109
+ task :as => "spec:active_support:explicit"
110
+ task :bp => "spec:backports:explicit"
111
+ task :ex => "spec:extlib:explicit"
112
+ task :fc => "spec:facets:explicit"
113
+ end
data/lib/monkey.rb CHANGED
@@ -1,6 +1,17 @@
1
1
  module Monkey
2
+
2
3
  Dir[File.dirname(__FILE__) + "/monkey/*.rb"].sort.each do |path|
3
4
  filename = File.basename(path, '.rb')
4
5
  require "monkey/#{filename}"
5
6
  end
7
+
8
+ def self.backend=(backend)
9
+ Backend.setup! backend
10
+ backend
11
+ end
12
+
13
+ def self.backend
14
+ Backend.backend
15
+ end
16
+
6
17
  end
@@ -1,3 +1,5 @@
1
+ require "monkey"
2
+
1
3
  module Monkey
2
4
  module Autoloader
3
5
  def const_missing(const_name)
@@ -1,45 +1,110 @@
1
- require "set"
2
-
3
1
  module Monkey
4
2
  module Backend
5
-
6
- autoload :ActiveSupport, "monkey/backend/active_support"
7
- autoload :BackendDsl, "monkey/backend/backend_dsl"
8
- autoload :Backports, "monkey/backend/backports"
9
- autoload :Extlib, "monkey/backend/extlib"
10
3
 
11
- def self.register(definition, expected_name, backend_name = nil)
12
- klass = definition.core_klass
13
- add_hook klass, expected_name
14
- add_hook klass, backend_name if backend_name
15
- backend_name ||= expected_name
4
+ @available_backends = []
5
+
6
+ class << self
7
+ attr_reader :available_backends, :backend
16
8
  end
17
-
18
- def preferred_backend
19
- available_backends.detect { |b| eval "defined? ::#{b}" }
9
+
10
+ module AbstractBackend
11
+ attr_accessor :backend_name, :backend_path
12
+
13
+ def available?
14
+ Object.const_defined? backend_name or $LOADED_FEATURES.any? { |l| l =~ /^#{backend_path}\// }
15
+ end
16
+
17
+ def setup_complete
18
+ require backend_path
19
+ setup
20
+ end
21
+
22
+ def load_libs(*data)
23
+ load_with_prefix backend_path, data
24
+ end
25
+
26
+ def load_with_prefix(prefix, libs = nil)
27
+ case libs
28
+ when String, Symbol then require File.join(prefix.to_s, libs.to_s)
29
+ when Array then libs.each { |lib| load_with_prefix prefix, lib }
30
+ when Hash then libs.each { |k, v| load_with_prefix File.join(prefix.to_s, k.to_s), v }
31
+ else raise ArgumentError, "cannot handle #{libs.inspect}"
32
+ end
33
+ end
34
+
35
+ def missing(*libs)
36
+ load_with_prefix "monkey/backend/common", libs
37
+ end
38
+
39
+ def expects_module(name)
40
+ name.split("::").inject(Object) do |parent, name|
41
+ if name.empty?
42
+ parent
43
+ else
44
+ parent.class_eval "module #{name}; self; end"
45
+ end
46
+ end
47
+ end
48
+
20
49
  end
21
50
 
22
- def available_backends
23
- @available_backends ||= Set[:ActiveSupport, :Extlib, :Backports]
51
+ def self.new(backend_name, backend_path = nil, &block)
52
+ mod = eval "module #{backend_name}; self; end"
53
+ mod.extend AbstractBackend
54
+ backend_path ||= backend_name.to_s.downcase
55
+ mod.backend_name, mod.backend_path = backend_name.to_s, backend_path.to_s
56
+ available_backends << mod
57
+ mod.class_eval(&block) if block
24
58
  end
25
-
26
- def backend
27
- @backend || preferred_backend || available_backends.first
59
+
60
+ def self.preferred_backend
61
+ available_backends.detect { |b| b.available? } || @backend
28
62
  end
29
-
30
- private
31
-
32
- def self.add_hook(klass, name)
33
- mod = module_for klass
34
- mod.class_eval "def #{name}(*a, &b); Monkey::Backend.call(#{klass.name}, #{name}, *a, &b); end"
63
+
64
+ def self.setup?
65
+ !!@setup
35
66
  end
36
-
37
- def self.module_for(klass, &block)
38
- mod = eval "module Monkey::Ext; module #{klass.name}; self; end; end"
39
- klass.send :include, mod
40
- mod.class_eval(&block) if block
41
- mod
67
+
68
+ def self.setup!(backend)
69
+ if backend
70
+ @backend = detect_backend(backend)
71
+ @backend.setup
72
+ @setup = true
73
+ @backend
74
+ else
75
+ available_backends.each do |backend|
76
+ begin
77
+ return setup!(backend)
78
+ rescue LoadError => error
79
+ @load_error ||= error
80
+ @backend = nil
81
+ end
82
+ end
83
+ raise @load_error
84
+ end
85
+ end
86
+
87
+ class << self
88
+ alias backend= setup!
89
+ end
90
+
91
+ def self.setup
92
+ setup! preferred_backend unless setup?
42
93
  end
43
94
 
95
+ def self.detect_backend(backend_or_name)
96
+ return backend_or_name if backend_or_name.respond_to? :setup
97
+ detected = available_backends.detect do |backend|
98
+ [backend.backend_name.to_s, backend.backend_path.to_s, backend.name.to_s].include? backend_or_name.to_s
99
+ end
100
+ raise ArgumentError, "cannot detect backend #{backend_or_name.inspect}" unless detected
101
+ detected
102
+ end
103
+
104
+ Dir[File.dirname(__FILE__) + "/backend/*.rb"].sort.each do |path|
105
+ filename = File.basename(path, '.rb')
106
+ require "monkey/backend/#{filename}"
107
+ end
108
+
44
109
  end
45
110
  end
@@ -1,17 +1,12 @@
1
- module Monkey
2
- module Backend
3
- module ActiveSupport
4
- def self.setup
5
- require "active_support/core_ext/array/extract_options"
6
- require "active_support/core_ext/object/metaclass"
7
- require "active_support/core_ext/object/misc"
8
- require "active_support/core_ext/string/inflection"
9
- require "active_support/core_ext/module/introspection"
10
- ::String.class_eval do
11
- alias to_const_string camelcase
12
- alias to_const_path underscore
13
- end
14
- end
1
+ Monkey::Backend.new :ActiveSupport, :active_support do
2
+ def self.setup
3
+ expects_module "::ActiveSupport::CoreExtensions::String::Inflections"
4
+ load_libs "core_ext/object" => [:metaclass, :misc], :core_ext => %w[array/extract_options string/inflections module/introspection]
5
+ Array.send :include, ActiveSupport::CoreExtensions::Array::ExtractOptions
6
+ Module.send :include, ActiveSupport::CoreExtensions::Module
7
+ ::String.class_eval do
8
+ alias to_const_string camelcase
9
+ alias to_const_path underscore
15
10
  end
16
- end
11
+ end
17
12
  end
@@ -1,17 +1,11 @@
1
- module Monkey
2
- module Backend
3
- module Backports
4
- def setup
5
- require "backports/1.8.7/kernel"
6
- require "backports/rails/array"
7
- require "backports/rails/string"
8
- require "monkey/backend/common/parent"
9
- require "monkey/backend/common/metaclass"
10
- ::String.class_eval do
11
- alias to_const_string camelcase
12
- alias to_const_path underscore
13
- end
14
- end
1
+ Monkey::Backend.new :Backports do
2
+ def self.setup
3
+ load_libs "tools", "1.8.7/kernel", :rails => [:array, :string]
4
+ missing :parent, :metaclass
5
+ ::String.class_eval do
6
+ alias camelcase camelize
7
+ alias to_const_string camelize
8
+ alias to_const_path underscore
15
9
  end
16
10
  end
17
11
  end
@@ -1,12 +1,10 @@
1
1
  module Monkey
2
2
  module Backend
3
3
  module Common
4
- module Metaclass
5
- ::Object.send :include, self
6
- def metaclass
7
- class << self
8
- self
9
- end
4
+ module Parent
5
+ ::Module.send :include, self
6
+ def parent
7
+ name =~ /^(.+)::[^:]+$/ ? $1.constantize : Object
10
8
  end
11
9
  end
12
10
  end
@@ -2,7 +2,7 @@ module Monkey
2
2
  module Backend
3
3
  module Common
4
4
  module Tap
5
- ::Kernel.send :include, self
5
+ ::Object.send :include, self
6
6
  def tap
7
7
  yield self
8
8
  self
@@ -1,21 +1,13 @@
1
- module Monkey
2
- module Backend
3
- module Extlib
4
- def setup
5
- require "extlib/object"
6
- require "extlib/string"
7
- require "extlib/constantize"
8
- require "monkey/backend/common/parent"
9
- require "monkey/backend/common/extract_options"
10
- require "monkey/backend/common/tap"
11
- ::Object.class_eval { alias metaclass meta_class }
12
- ::String.class_eval do
13
- alias camelcase to_const_string
14
- alias underscore to_const_path
15
- def constantize
16
- Extlib::Inflection.constantize(self)
17
- end
18
- end
1
+ Monkey::Backend.new :Extlib do
2
+ def self.setup
3
+ load_libs :object, :string, :inflection
4
+ missing :parent, :extract_options, :tap
5
+ ::Object.class_eval { alias metaclass meta_class }
6
+ ::String.class_eval do
7
+ alias camelcase to_const_string
8
+ alias underscore to_const_path
9
+ def constantize
10
+ Extlib::Inflection.constantize(self)
19
11
  end
20
12
  end
21
13
  end