diff --git a/README b/README new file mode 100644 index 0000000..4bd5820 --- /dev/null +++ b/README @@ -0,0 +1,37 @@ +NAME + shared.rb + +DESCRIPTION + shared.rb provides a super easy way to share code between classes or modules + in a simple way. shared code can be at the class and/or instance level and + users deferred evaluation so this is more powerful that the normal ruby + module inclusion facilities on which it is based. + +SYNOPSIS + require 'shared' + + Shared 'methods' do + class << self + attr :classname + end + + @classname = name.downcase + + def objectname + self.class.classname + "(#{ object_id })" + end + end + + class C + include Shared('methods') + end + + class B + include Shared('methods') + end + + p C.classname #=> 'c' + p C.new.objectname #=> 'c(1234)' + + p B.classname #=> 'b' + p B.new.objectname #=> 'b(4567)' diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..fe03379 --- /dev/null +++ b/Rakefile @@ -0,0 +1,228 @@ + +This.rubyforge_project = 'codeforpeople' +This.author = "Ara T. Howard" +This.email = "ara.t.howard@gmail.com" +This.homepage = "http://github.com/ahoward/#{ This.lib }/tree/master" + + +task :default do + puts(Rake::Task.tasks.map{|task| task.name} - ['default']) +end + + +task :gemspec do + ignore_extensions = 'git', 'svn', 'tmp', /sw./, 'bak', 'gem' + ignore_directories = 'pkg' + ignore_files = 'test/log' + + shiteless = + lambda do |list| + list.delete_if do |entry| + next unless test(?e, entry) + extension = File.basename(entry).split(%r/[.]/).last + ignore_extensions.any?{|ext| ext === extension} + end + list.delete_if do |entry| + next unless test(?d, entry) + dirname = File.expand_path(entry) + ignore_directories.any?{|dir| File.expand_path(dir) == dirname} + end + list.delete_if do |entry| + next unless test(?f, entry) + filename = File.expand_path(entry) + ignore_files.any?{|file| File.expand_path(file) == filename} + end + end + + lib = This.lib + version = This.version + files = shiteless[Dir::glob("**/**")] + executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)} + has_rdoc = true #File.exist?('doc') + test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb") + + extensions = This.extensions + if extensions.nil? + %w( Makefile configure extconf.rb ).each do |ext| + extensions << ext if File.exists?(ext) + end + end + extensions = [extensions].flatten.compact + + template = + if test(?e, 'gemspec.erb') + Template{ IO.read('gemspec.erb') } + else + Template { + <<-__ + ## #{ lib }.gemspec + # + + Gem::Specification::new do |spec| + spec.name = #{ lib.inspect } + spec.version = #{ version.inspect } + spec.platform = Gem::Platform::RUBY + spec.summary = #{ lib.inspect } + + spec.files = #{ files.inspect } + spec.executables = #{ executables.inspect } + + spec.require_path = "lib" + + spec.has_rdoc = #{ has_rdoc.inspect } + spec.test_files = #{ test_files.inspect } + #spec.add_dependency 'lib', '>= version' + #spec.add_dependency 'fattr' + + spec.extensions.push(*#{ extensions.inspect }) + + spec.rubyforge_project = #{ This.rubyforge_project.inspect } + spec.author = #{ This.author.inspect } + spec.email = #{ This.email.inspect } + spec.homepage = #{ This.homepage.inspect } + end + __ + } + end + + open("#{ lib }.gemspec", "w"){|fd| fd.puts template} + This.gemspec = "#{ lib }.gemspec" +end + +task :gem => [:clean, :gemspec] do + Fu.mkdir_p This.pkgdir + before = Dir['*.gem'] + cmd = "gem build #{ This.gemspec }" + `#{ cmd }` + after = Dir['*.gem'] + gem = ((after - before).first || after.first) or abort('no gem!') + Fu.mv gem, This.pkgdir + This.gem = File.basename(gem) +end + +task :readme do + samples = '' + prompt = '~ > ' + lib = This.lib + version = This.version + + Dir['sample*/*'].sort.each do |sample| + samples << "\n" << " <========< #{ sample } >========>" << "\n\n" + + cmd = "cat #{ sample }" + samples << Util.indent(prompt + cmd, 2) << "\n\n" + samples << Util.indent(`#{ cmd }`, 4) << "\n" + + cmd = "ruby #{ sample }" + samples << Util.indent(prompt + cmd, 2) << "\n\n" + + cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -Ilib #{ sample })'" + samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n" + end + + template = + if test(?e, 'readme.erb') + Template{ IO.read('readme.erb') } + else + Template { + <<-__ + NAME + #{ lib } + + DESCRIPTION + + INSTALL + gem install #{ lib } + + SAMPLES + #{ samples } + __ + } + end + + open("README", "w"){|fd| fd.puts template} +end + + +task :clean do + Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)} +end + + +task :release => [:clean, :gemspec, :gem] do + gems = Dir[File.join(This.pkgdir, '*.gem')].flatten + raise "which one? : #{ gems.inspect }" if gems.size > 1 + raise "no gems?" if gems.size < 1 + cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.pkgdir }/#{ This.gem }" + puts cmd + system cmd +end + + + + + +BEGIN { + $VERBOSE = nil + + require 'ostruct' + require 'erb' + require 'fileutils' + + Fu = FileUtils + + This = OpenStruct.new + + This.file = File.expand_path(__FILE__) + This.dir = File.dirname(This.file) + This.pkgdir = File.join(This.dir, 'pkg') + + lib = ENV['LIB'] + unless lib + lib = File.basename(Dir.pwd) + end + This.lib = lib + + version = ENV['VERSION'] + unless version + name = lib.capitalize + require "./lib/#{ lib }" + version = eval(name).send(:version) + end + This.version = version + + abort('no lib') unless This.lib + abort('no version') unless This.version + + module Util + def indent(s, n = 2) + s = unindent(s) + ws = ' ' * n + s.gsub(%r/^/, ws) + end + + def unindent(s) + indent = nil + s.each do |line| + next if line =~ %r/^\s*$/ + indent = line[%r/^\s*/] and break + end + indent ? s.gsub(%r/^#{ indent }/, "") : s + end + extend self + end + + class Template + def initialize(&block) + @block = block + @template = block.call.to_s + end + def expand(b=nil) + ERB.new(Util.unindent(@template)).result(b||@block) + end + alias_method 'to_s', 'expand' + end + def Template(*args, &block) Template.new(*args, &block) end + + Dir.chdir(This.dir) +} diff --git a/lib/shared.rb b/lib/shared.rb new file mode 100644 index 0000000..ba4106f --- /dev/null +++ b/lib/shared.rb @@ -0,0 +1,128 @@ +# shared.rb provides a super easy way to share code between classes or modules +# in a simple way. shared code can be at the class and/or instance level and +# users deferred evaluation so this is more powerful that the normal ruby +# module inclusion facilities on which it is based. +# +# basic usage: +# +# +# require 'shared' +# +# Shared 'methods' do +# class << self +# attr :classname +# end +# +# @classname = name.downcase +# +# def objectname +# self.class.classname + "(#{ object_id })" +# end +# end +# +# class C +# include Shared('methods') +# end +# +# class B +# include Shared('methods') +# end +# +# p C.classname #=> 'c' +# p C.new.objectname #=> 'c(1234)' +# +# p B.classname #=> 'b' +# p B.new.objectname #=> 'b(4567)' +# + + +unless defined?(Shared) + module Shared + Shared::VERSION = '1.1.0' unless defined?(Shared::VERSION) + def version() Shared::VERSION end + + Code = {} + + def load key + key = key_for(key) + unless Code.has_key?(key) + ::Kernel.load("shared/#{ key }.rb") + end + end + + def shared name, options = {}, &block + key = key_for name + via = (options[:via]||options['via']||:eval).to_s.to_sym + + if block.nil? + Shared.load(key) + return Code[key] + end + + m = (Code[key] || Module.new) + + case via + when :eval + singleton_class(m) do + unless m.respond_to?(:blocks) + blocks = [] + + define_method(:blocks){ blocks } + + define_method(:included) do |other| + blocks.each{|b| other.send(:module_eval, &b)} + end + + define_method(:extend_object) do |other| + Shared.singleton_class(other) do + m.blocks.each{|b| module_eval &b} + end + end + end + end + m.blocks << block + + when :module + m.send(:module_eval, &block) + end + + Code[key] ||= m + end + + alias_method 'share', 'shared' + alias_method 'for', 'shared' + + def key_for name + name.to_s.strip.downcase + end + + def singleton_class object, &block + singleton_class = + class << object + self + end + block ? singleton_class.module_eval(&block) : singleton_class + end + + extend self + end + + module Kernel + private + def Share(*a, &b) + if a.empty? and b.nil? + ::Shared + else + Shared.share(*a, &b) + end + end + + def Shared(*a, &b) + if a.empty? and b.nil? + ::Shared + else + Shared.shared(*a, &b) + end + end + end +end diff --git a/readme.erb b/readme.erb new file mode 100644 index 0000000..4bd5820 --- /dev/null +++ b/readme.erb @@ -0,0 +1,37 @@ +NAME + shared.rb + +DESCRIPTION + shared.rb provides a super easy way to share code between classes or modules + in a simple way. shared code can be at the class and/or instance level and + users deferred evaluation so this is more powerful that the normal ruby + module inclusion facilities on which it is based. + +SYNOPSIS + require 'shared' + + Shared 'methods' do + class << self + attr :classname + end + + @classname = name.downcase + + def objectname + self.class.classname + "(#{ object_id })" + end + end + + class C + include Shared('methods') + end + + class B + include Shared('methods') + end + + p C.classname #=> 'c' + p C.new.objectname #=> 'c(1234)' + + p B.classname #=> 'b' + p B.new.objectname #=> 'b(4567)' diff --git a/shared.gemspec b/shared.gemspec new file mode 100644 index 0000000..dfcf1f6 --- /dev/null +++ b/shared.gemspec @@ -0,0 +1,26 @@ +## shared.gemspec +# + +Gem::Specification::new do |spec| + spec.name = "shared" + spec.version = "1.1.0" + spec.platform = Gem::Platform::RUBY + spec.summary = "shared" + + spec.files = ["lib", "lib/shared.rb", "Rakefile", "README", "readme.erb", "shared.gemspec"] + spec.executables = [] + + spec.require_path = "lib" + + spec.has_rdoc = true + spec.test_files = nil + #spec.add_dependency 'lib', '>= version' + #spec.add_dependency 'fattr' + + spec.extensions.push(*[]) + + spec.rubyforge_project = "codeforpeople" + spec.author = "Ara T. Howard" + spec.email = "ara.t.howard@gmail.com" + spec.homepage = "http://github.com/ahoward/shared/tree/master" +end