nanoboy 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ /nbproject
6
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Bernat Foj Capell
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Nanoboy
2
+
3
+ nanoboy is a little ruby gem that allows you to extend classes and/or modules with several advantages over the usual ruby include/extend:
4
+
5
+ * lazy extension -- the extended class is not loaded if it's not already in the memory. The extension will be applied once the class is loaded.
6
+ * persistent on class reloading -- if you are developing in Rails, where classes are reloaded on each request, use nanoboy and forget about reloading problems and callbacks.
7
+
8
+ Due to the lazy extension feature, this gem is specially useful when you have loading order problems. For example, if you are extending a lib that is extending another lib. Yes, these things happen.
9
+
10
+
11
+ Syntax
12
+ ------
13
+
14
+ There are two possible syntaxes:
15
+
16
+ * Explicit
17
+
18
+ ```ruby
19
+ Nanoboy.include! :ClassToExtend, MyAwesomeModule
20
+ ```
21
+
22
+ * Sugared
23
+
24
+ ```ruby
25
+ :ClassToExtend.include! MyAwesomeModule
26
+ ```
27
+
28
+ Note that the class to be extended is named with its symbol, not the constant. If you use the constant, it gets loaded and you lose.
29
+
30
+ And yes it works with namespaces too:
31
+
32
+ ```ruby
33
+ :"CrazyModule::ClassToExtend".include! MyAwesomeModule
34
+ ```
35
+
36
+ About the name
37
+ --------------
38
+
39
+ As @jondeandres said, if there is a well-known gem called FactoryGirl, what's the problem with Nanoboy?
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require "bundler/gem_tasks"
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test nanoboy'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ def update_version(function, name)
17
+ major, minor, tiny = File.read("VERSION").strip.split(".").map { |i| i.to_i }
18
+ eval "#{name} #{function}= 1"
19
+ File.open("VERSION", "w") { |f| f.puts [major, minor, tiny].join(".") }
20
+ puts `cat VERSION`
21
+ end
22
+
23
+ { :bump => "+", :debump => "-"}.each do |key, value|
24
+ namespace key do
25
+ [ :major, :minor, :tiny].each do |position|
26
+ eval <<-CODE
27
+ desc "#{key.to_s.capitalize} #{position} number by 1"
28
+ task :#{position} do
29
+ update_version("#{value}", "#{position}")
30
+ end
31
+ CODE
32
+ end
33
+ end
34
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/lib/nanoboy.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'nanoboy/hook'
2
+
3
+ module Nanoboy
4
+ class << self
5
+ attr_accessor :extensions, :methods
6
+ end
7
+
8
+ self.extensions ||= {}
9
+ self.methods = %w{include extend helper}
10
+
11
+ # Returns true if the +sym+ class has scheduled extensions to be included
12
+ def self.has_extensions?(sym)
13
+ extensions[sym.to_s]
14
+ end
15
+
16
+ # Returns a Module that, when included, will provide all the scheduled funcionality for +sym+
17
+ def self.extensions_for(sym)
18
+ Module.new{
19
+ block = ::Proc.new{|recipient|
20
+ Nanoboy.methods.each do |method|
21
+ Array(Nanoboy.extensions[sym.to_s][method]).each do |k|
22
+ recipient.send(method, k)
23
+ end
24
+ end
25
+ }
26
+ define_method :included, block
27
+ module_function :included
28
+ }
29
+ end
30
+
31
+ def self.load_extensions_for klass, recipient = klass
32
+ if has_extensions?(klass.name)
33
+ recipient.send :include, extensions_for(klass.name)
34
+ end
35
+ end
36
+
37
+ methods.each do |method|
38
+
39
+ self.class_eval <<-EOS
40
+ # Schedules the inclusion of +klass+ inside +recipient+
41
+ # Use this instead of sending direct includes, extends or helpers
42
+ def self.#{method}!(recipient, klass) # def self.append_include('ClassToExtend', MyModule)
43
+ extensions[recipient.to_s] ||= {} # extensions['ClassToExtend'.to_s] ||= {}
44
+ extensions[recipient.to_s]["#{method}"] ||= [] # extensions['ClassToExtend'.to_s]["include"] ||= []
45
+ extensions[recipient.to_s]["#{method}"] << klass # extensions['ClassToExtend'.to_s]["include"] << klass
46
+ # use class_eval to avoid evaluation of recipient # # use class_eval to avoid evaluation of ClassToExtend
47
+ class_eval <<-EOCONSTANTIZE # class_eval <<-EOCONSTANTIZE
48
+ if defined?(\#{recipient}) # if defined?(ClassToExtend)
49
+ \#{recipient}.send("#{method}", klass) # ClassToExtend.send("include", MyModule)
50
+ end # end
51
+ EOCONSTANTIZE
52
+ end # end
53
+ EOS
54
+ end
55
+
56
+ def self.enable
57
+ Nanoboy::Hook.activate
58
+ end
59
+
60
+ end
61
+
62
+ # Go!
63
+ Nanoboy.enable
@@ -0,0 +1,72 @@
1
+ module Nanoboy
2
+ # This module acts as a hook to enable Nanoboy in the class that includes it
3
+ module Hook
4
+
5
+ # This is the main Nanoboy hook, that is fired in the usual class creation
6
+ # class MyClass (< MyParent); end
7
+ module InheritedHook
8
+ def inherited_with_nanoboy(subclass)
9
+ inherited_without_nanoboy(subclass)
10
+ Nanoboy.load_extensions_for subclass
11
+ end
12
+
13
+ def self.included klass
14
+ Hook.nanoboy_alias klass, :inherited
15
+ end
16
+ end
17
+
18
+ # This module allows to also fire Nanoboy when defining new classes using
19
+ # the Module#const_set method
20
+ module ConstSetHook
21
+ def const_set_with_nanoboy(name, klass)
22
+ set_object = const_set_without_nanoboy(name, klass)
23
+ if set_object.is_a? Class
24
+ Nanoboy.load_extensions_for set_object
25
+ end
26
+ set_object
27
+ end
28
+
29
+ def self.included klass
30
+ Hook.nanoboy_alias klass, :const_set
31
+ end
32
+ end
33
+
34
+ # This is the module that allows the sugared syntax: :ClassToExtend.include!
35
+ module SymbolHook
36
+ def self.included(klass)
37
+ klass.class_eval do
38
+ Nanoboy.methods.each do |method|
39
+ define_method "#{method}!" do |extension|
40
+ Nanoboy.send("#{method}!", self, extension)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # You can include Nanoboy::Hook manually in your classes.
48
+ # Useful if it's activated too late for you
49
+ def self.included(klass)
50
+ klass.singleton_class.instance_eval { include InheritedHook, ConstSetHook }
51
+ end
52
+
53
+ # Similar to ActiveSupport alias_method_chain, but ensuring the alias
54
+ # is created only once
55
+ def self.nanoboy_alias klass, name
56
+ klass.class_eval do
57
+ unless (instance_methods + private_methods).include? :"#{name}_without_nanoboy"
58
+ alias_method "#{name}_without_nanoboy", name
59
+ alias_method name, "#{name}_with_nanoboy"
60
+ end
61
+ end
62
+ end
63
+
64
+ # Performs the hooking into the system that will make Nanoboy work
65
+ def self.activate
66
+ # Modules do not have Class#inherited since it is only defined for classes
67
+ Class.instance_eval { include InheritedHook }
68
+ Module.instance_eval { include ConstSetHook }
69
+ Symbol.instance_eval { include SymbolHook }
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,13 @@
1
+ module Nanoboy
2
+ def self.version
3
+ VERSION::STRING
4
+ end
5
+ module VERSION #:nodoc:
6
+ file = File.join(File.dirname(__FILE__), "..", "..", "VERSION")
7
+ version = File.read(file).strip.split(".")
8
+
9
+ MAJOR, MINOR, *TINY = version
10
+
11
+ STRING = [MAJOR, MINOR, TINY].join('.')
12
+ end
13
+ end
data/nanoboy.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "nanoboy/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "nanoboy"
7
+ s.version = Nanoboy.version
8
+ s.authors = ["Bernat Foj"]
9
+ s.homepage = "http://www.github.com/bfcapell/nanoboy"
10
+ s.summary = %q{Lazy extend ruby classes and modules}
11
+ s.description = %q{Lazy extend ruby classes and modules}
12
+
13
+ s.rubyforge_project = "nanoboy"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_development_dependency "mocha", "~> 0.10.0"
21
+ end
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'nanoboy'
4
+ require 'mocha'
5
+
6
+ class NanoboyTest < Test::Unit::TestCase
7
+
8
+ # Mock classes, with namespace to test the general case
9
+ class Test::MyClass; end
10
+ class Test::OtherClass; end
11
+ module Test::MyModule
12
+ def my_method; end
13
+ end
14
+
15
+ def test_load_methods_should_trigger_include_when_defined
16
+ Nanoboy.methods.each do |method|
17
+ Test::MyClass.expects(method).with(Test::MyModule)
18
+ Nanoboy.send("#{method}!", 'Test::MyClass', Test::MyModule)
19
+ end
20
+ end
21
+
22
+ def test_load_methods_should_trigger_include_with_sugared_syntax
23
+ Nanoboy.methods.each do |method|
24
+ Test::MyClass.expects(method).with(Test::MyModule)
25
+ :'Test::MyClass'.send("#{method}!", Test::MyModule)
26
+ end
27
+ end
28
+
29
+ def test_load_methods_should_schedule_automatic_inclusion_when_defined_after_with_const_set
30
+ Test.send('remove_const', 'MyClass')
31
+ Nanoboy.include!('Test::MyClass', Test::MyModule)
32
+ Test.const_set('MyClass', Class.new)
33
+ assert Test::MyClass.instance_methods.map(&:to_sym).include?(:my_method)
34
+ end
35
+
36
+ def test_load_methods_should_schedule_automatic_inclusion_when_defined_after_with_normal_def
37
+ Test.send('remove_const', 'MyClass')
38
+ Nanoboy.include!('Test::MyClass', Test::MyModule)
39
+ Test.class_eval "class MyClass; end"
40
+ assert Test::MyClass.instance_methods.map(&:to_sym).include?(:my_method)
41
+ end
42
+
43
+ def test_load_methods_should_allow_inclusion_in_other_classes
44
+ Nanoboy.include!('Test::MyClass', Test::MyModule)
45
+ Test::OtherClass.class_eval do
46
+ Nanoboy.load_extensions_for Test::MyClass, self
47
+ end
48
+ assert Test::OtherClass.instance_methods.map(&:to_sym).include?(:my_method)
49
+ end
50
+
51
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nanoboy
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Bernat Foj
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-07-18 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: mocha
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 55
29
+ segments:
30
+ - 0
31
+ - 10
32
+ - 0
33
+ version: 0.10.0
34
+ type: :development
35
+ version_requirements: *id001
36
+ description: Lazy extend ruby classes and modules
37
+ email:
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - .gitignore
46
+ - Gemfile
47
+ - MIT-LICENSE
48
+ - README.md
49
+ - Rakefile
50
+ - VERSION
51
+ - lib/nanoboy.rb
52
+ - lib/nanoboy/hook.rb
53
+ - lib/nanoboy/version.rb
54
+ - nanoboy.gemspec
55
+ - test/nanoboy_test.rb
56
+ homepage: http://www.github.com/bfcapell/nanoboy
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 3
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project: nanoboy
85
+ rubygems_version: 1.8.10
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Lazy extend ruby classes and modules
89
+ test_files: []
90
+