monkey-lib 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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