nanoboy 1.0.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/.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
+