lazy_methods 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ pkg
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007 Brian Durand
1
+ Copyright (c) 2010 Brian Durand
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -0,0 +1,33 @@
1
+ == LazyMethods
2
+
3
+ This gem adds a virtual lazy version of every method on every class. Lazy methods have the same name as the original method name but prefixed with +lazy_+. A lazy method will return a proxy object that looks and acts just like the result from calling the actual method. The trick is that the actual method will not be called until a method is invoked on the proxy object.
4
+
5
+ This pattern works great with caching. You can have your business logic invoke lazy methods and your view logic sitting behind a cache. If the cache is hit, you won't actually end up invoking any of your business logic.
6
+
7
+ === A simple example:
8
+
9
+ The business logic:
10
+
11
+ def my_action
12
+ @records = MyResource.find(params[:name])
13
+ end
14
+
15
+ And in the view:
16
+
17
+ <% cache(params[:names]) -%>
18
+ <% @records.each do |record| -%>
19
+ <div><%=record.title%></div>
20
+ <% end -%>
21
+ <% end -%>
22
+
23
+ Now even if you cache the fragments in your view that use @records, the database will still be hit to select and instantiate all the records. You could remove the resource call from the business logic and add it to the view. However, this just feels wrong and is inherently harder to test. Instead just use a lazy method.
24
+
25
+ def my_action
26
+ @records = MyResource.lazy_find(params[:name])
27
+ end
28
+
29
+ Now, as long as no methods are invoked on @records, the original find method will never be called. As soon as the first method is called, the original find method will be called. It will never be called more than once. You can even pass in a block to the lazy method.
30
+
31
+ == Testing
32
+
33
+ Since the proxy object looks and acts just like the real result object, all your view tests should still pass. Your controller tests should pass will little or no tweaking.
data/Rakefile CHANGED
@@ -1,43 +1,45 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
  require 'rake/rdoctask'
4
- require 'rake/gempackagetask'
5
- require 'spec/rake/spectask'
6
4
 
7
5
  desc 'Default: run unit tests.'
8
6
  task :default => :test
9
7
 
10
- desc 'Test the lazy_methods plugin.'
11
- Spec::Rake::SpecTask.new(:test) do |t|
12
- t.spec_files = 'spec/**/*_spec.rb'
8
+ begin
9
+ require 'spec/rake/spectask'
10
+ desc 'Test the gem.'
11
+ Spec::Rake::SpecTask.new(:test) do |t|
12
+ t.spec_files = FileList.new('spec/**/*_spec.rb')
13
+ end
14
+ rescue LoadError
15
+ tast :test do
16
+ STDERR.puts "You must have rspec >= 1.3.0 to run the tests"
17
+ end
13
18
  end
14
19
 
15
- desc 'Generate documentation for the lazy_methods plugin.'
20
+ desc 'Generate documentation for the gem.'
16
21
  Rake::RDocTask.new(:rdoc) do |rdoc|
17
22
  rdoc.rdoc_dir = 'rdoc'
18
- rdoc.options << '--title' << 'LazyMethods' << '--line-numbers' << '--inline-source' << '--main' << 'README'
19
- rdoc.rdoc_files.include('README')
23
+ rdoc.options << '--title' << 'Lazy Methods' << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc'
24
+ rdoc.rdoc_files.include('README.rdoc')
20
25
  rdoc.rdoc_files.include('lib/**/*.rb')
21
26
  end
22
27
 
23
- spec = Gem::Specification.new do |s|
24
- s.name = "lazy_methods"
25
- s.version = "1.0.2"
26
- s.author = "Brian Durand"
27
- s.platform = Gem::Platform::RUBY
28
- s.summary = "Provide lazy method calls for all methods on every object to aid in caching."
29
- s.files = FileList["lib/**/*", "MIT-LICENSE", 'Rakefile'].to_a
30
- s.require_path = "lib"
31
- s.test_files = FileList["{spec}/**/*.rb"].to_a
32
- s.has_rdoc = true
33
- s.rdoc_options << '--title' << 'LazyMethods' << '--line-numbers' << '--inline-source' << '--main' << 'README'
34
- s.extra_rdoc_files = ["README"]
35
- s.homepage = "http://lazymethods.rubyforge.org"
36
- s.rubyforge_project = "lazymethods"
37
- s.email = 'brian@embellishedvisions.com'
38
- end
39
-
40
- Rake::GemPackageTask.new(spec) do |pkg|
41
- pkg.need_tar = true
42
- end
28
+ begin
29
+ require 'jeweler'
30
+ Jeweler::Tasks.new do |gem|
31
+ gem.name = "lazy_methods"
32
+ gem.summary = %Q{Gem that adds lazy method wrapping to every object. Preceding any method with lazy_ will defer the invocation until the result is actually needed.}
33
+ gem.description = %Q(Gem that adds lazy method wrapping to every object. Preceding any method with lazy_ will defer the invocation until the result is actually needed. This pattern is useful when used with caching systems.)
34
+ gem.email = "brian@embellishedvisions.com"
35
+ gem.homepage = "http://github.com/bdurand/acts_as_revisionable"
36
+ gem.authors = ["Brian Durand"]
37
+ gem.rdoc_options = ["--charset=UTF-8", "--main", "README.rdoc"]
38
+
39
+ gem.add_development_dependency('rspec', '>= 1.3.0')
40
+ gem.add_development_dependency('jeweler')
41
+ end
43
42
 
43
+ Jeweler::GemcutterTasks.new
44
+ rescue LoadError
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.3
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{lazy_methods}
8
+ s.version = "1.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brian Durand"]
12
+ s.date = %q{2010-06-22}
13
+ s.description = %q{Gem that adds lazy method wrapping to every object. Preceding any method with lazy_ will defer the invocation until the result is actually needed. This pattern is useful when used with caching systems.}
14
+ s.email = %q{brian@embellishedvisions.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "lazy_methods.gemspec",
25
+ "lib/lazy_methods.rb",
26
+ "lib/lazy_methods/lazy_methods.rb",
27
+ "spec/lazy_method_spec.rb",
28
+ "spec/method_tester.rb",
29
+ "spec/spec_helper.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/bdurand/acts_as_revisionable}
32
+ s.rdoc_options = ["--charset=UTF-8", "--main", "README.rdoc"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.3.7}
35
+ s.summary = %q{Gem that adds lazy method wrapping to every object. Preceding any method with lazy_ will defer the invocation until the result is actually needed.}
36
+ s.test_files = [
37
+ "spec/lazy_method_spec.rb",
38
+ "spec/method_tester.rb",
39
+ "spec/spec_helper.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
48
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
49
+ else
50
+ s.add_dependency(%q<rspec>, [">= 1.3.0"])
51
+ s.add_dependency(%q<jeweler>, [">= 0"])
52
+ end
53
+ else
54
+ s.add_dependency(%q<rspec>, [">= 1.3.0"])
55
+ s.add_dependency(%q<jeweler>, [">= 0"])
56
+ end
57
+ end
58
+
@@ -1,2 +1,2 @@
1
- require 'lazy_methods/lazy_methods'
1
+ require File.expand_path('../lazy_methods/lazy_methods', __FILE__)
2
2
  Object.send(:include, LazyMethods::InstanceMethods) unless Object.include?(LazyMethods::InstanceMethods)
@@ -4,18 +4,16 @@ require File.expand_path(File.dirname(__FILE__) + '/method_tester')
4
4
 
5
5
  context "LazyMethods InstanceMethods" do
6
6
 
7
- setup do
8
- @object = MethodTester.new
9
- end
7
+ let(:object) { MethodTester.new }
10
8
 
11
9
  specify "should inject lazy method handling" do
12
- proxy = @object.lazy_test("arg")
10
+ proxy = object.lazy_test("arg")
13
11
  proxy.to_s.should == "ARG"
14
12
  proxy.__proxy_loaded__.should == true
15
13
  end
16
14
 
17
15
  specify "should return a proxy object that has not been invoked yet" do
18
- proxy = @object.lazy_test("arg")
16
+ proxy = object.lazy_test("arg")
19
17
  proxy.__proxy_loaded__.should == false
20
18
  end
21
19
 
@@ -23,37 +21,35 @@ end
23
21
 
24
22
  context "LazyMethods Proxy" do
25
23
 
26
- setup do
27
- @object = MethodTester.new
28
- end
24
+ let(:object) { MethodTester.new }
29
25
 
30
26
  specify "should be able to wrap a method without executing it" do
31
- proxy = @object.lazy_test("arg")
32
- @object.test_called.should == 0
27
+ proxy = object.lazy_test("arg")
28
+ object.test_called.should == 0
33
29
  end
34
30
 
35
31
  specify "should execute the wrapped method when it needs to" do
36
- proxy = @object.lazy_test("arg")
32
+ proxy = object.lazy_test("arg")
37
33
  proxy.to_s
38
- @object.test_called.should == 1
34
+ object.test_called.should == 1
39
35
  end
40
36
 
41
37
  specify "should only execute the wrapped method once" do
42
- proxy = @object.lazy_test("arg")
38
+ proxy = object.lazy_test("arg")
43
39
  proxy.to_s
44
40
  proxy.to_s
45
- @object.test_called.should == 1
41
+ object.test_called.should == 1
46
42
  end
47
43
 
48
44
  specify "should allow nil as a valid proxied value" do
49
- proxy = @object.lazy_test(nil)
45
+ proxy = object.lazy_test(nil)
50
46
  proxy.should_not
51
- @object.test_called.should == 1
47
+ object.test_called.should == 1
52
48
  end
53
49
 
54
50
  specify "should allow blocks in the lazy method" do
55
51
  n = 1
56
- proxy = @object.lazy_test("arg") do
52
+ proxy = object.lazy_test("arg") do
57
53
  n = 2
58
54
  end
59
55
  n.should == 1
@@ -62,7 +58,7 @@ context "LazyMethods Proxy" do
62
58
  end
63
59
 
64
60
  specify "should be indistinguishable from the real object" do
65
- proxy = @object.lazy_test("arg")
61
+ proxy = object.lazy_test("arg")
66
62
  proxy.class.should == String
67
63
  proxy.kind_of?(String).should == true
68
64
  end
@@ -73,13 +69,13 @@ context "LazyMethods Proxy" do
73
69
  end
74
70
 
75
71
  specify "should proxy missing methods" do
76
- proxy = @object.lazy_find_test
72
+ proxy = object.lazy_find_test
77
73
  proxy.to_s.should == "FINDER"
78
74
  end
79
75
 
80
76
  specify "should allow blocks in the lazy missing methods" do
81
77
  n = 1
82
- proxy = @object.lazy_find_test do
78
+ proxy = object.lazy_find_test do
83
79
  n = 2
84
80
  end
85
81
  n.should == 1
@@ -88,14 +84,14 @@ context "LazyMethods Proxy" do
88
84
  end
89
85
 
90
86
  specify "should not interfere with the proxied object's method_missing" do
91
- real = @object.find_test
87
+ real = object.find_test
92
88
  real.to_s.should == "FINDER"
93
89
  end
94
90
 
95
91
  specify "should not interfere with real methods that begin with lazy_" do
96
- @object.lazy_real_method_called.should == false
97
- @object.lazy_real_method
98
- @object.lazy_real_method_called.should == true
92
+ object.lazy_real_method_called.should == false
93
+ object.lazy_real_method
94
+ object.lazy_real_method_called.should == true
99
95
  end
100
96
 
101
97
  end
@@ -20,7 +20,7 @@ class MethodTester
20
20
  end
21
21
 
22
22
  def method_missing (method, *args, &block)
23
- if method.to_s.starts_with?('find_')
23
+ if method.to_s[0, 5] == 'find_'
24
24
  yield if block_given?
25
25
  "FINDER"
26
26
  else
@@ -0,0 +1,26 @@
1
+ # This file is copied to ~/spec when you run 'ruby script/generate rspec'
2
+ # from the project root directory.
3
+ ENV["RAILS_ENV"] ||= "test"
4
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
5
+ require 'spec/rails'
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.use_transactional_fixtures = true
9
+ config.use_instantiated_fixtures = false
10
+ config.fixture_path = RAILS_ROOT + '/spec/fixtures'
11
+ config.before(:each, :behaviour_type => :controller) do
12
+ raise_controller_errors
13
+ end
14
+
15
+ # You can declare fixtures for each behaviour like this:
16
+ # describe "...." do
17
+ # fixtures :table_a, :table_b
18
+ #
19
+ # Alternatively, if you prefer to declare them only once, you can
20
+ # do so here, like so ...
21
+ #
22
+ # config.global_fixtures = :table_a, :table_b
23
+ #
24
+ # If you declare global fixtures, be aware that they will be declared
25
+ # for all of your examples, even those that don't use them.
26
+ end
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lazy_methods
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ hash: 17
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 3
10
+ version: 1.0.3
5
11
  platform: ruby
6
12
  authors:
7
13
  - Brian Durand
@@ -9,56 +15,96 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2008-01-17 00:00:00 -06:00
18
+ date: 2010-06-22 00:00:00 -05:00
13
19
  default_executable:
14
- dependencies: []
15
-
16
- description:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 27
30
+ segments:
31
+ - 1
32
+ - 3
33
+ - 0
34
+ version: 1.3.0
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: jeweler
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ description: Gem that adds lazy method wrapping to every object. Preceding any method with lazy_ will defer the invocation until the result is actually needed. This pattern is useful when used with caching systems.
17
52
  email: brian@embellishedvisions.com
18
53
  executables: []
19
54
 
20
55
  extensions: []
21
56
 
22
57
  extra_rdoc_files:
23
- - README
58
+ - README.rdoc
24
59
  files:
25
- - lib/lazy_methods
26
- - lib/lazy_methods.rb
27
- - lib/lazy_methods/lazy_methods.rb
60
+ - .gitignore
28
61
  - MIT-LICENSE
62
+ - README.rdoc
29
63
  - Rakefile
30
- - README
64
+ - VERSION
65
+ - lazy_methods.gemspec
66
+ - lib/lazy_methods.rb
67
+ - lib/lazy_methods/lazy_methods.rb
68
+ - spec/lazy_method_spec.rb
69
+ - spec/method_tester.rb
70
+ - spec/spec_helper.rb
31
71
  has_rdoc: true
32
- homepage: http://lazymethods.rubyforge.org
72
+ homepage: http://github.com/bdurand/acts_as_revisionable
73
+ licenses: []
74
+
33
75
  post_install_message:
34
76
  rdoc_options:
35
- - --title
36
- - LazyMethods
37
- - --line-numbers
38
- - --inline-source
77
+ - --charset=UTF-8
39
78
  - --main
40
- - README
79
+ - README.rdoc
41
80
  require_paths:
42
81
  - lib
43
82
  required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
44
84
  requirements:
45
85
  - - ">="
46
86
  - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
47
90
  version: "0"
48
- version:
49
91
  required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
50
93
  requirements:
51
94
  - - ">="
52
95
  - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
53
99
  version: "0"
54
- version:
55
100
  requirements: []
56
101
 
57
- rubyforge_project: lazymethods
58
- rubygems_version: 1.0.1
102
+ rubyforge_project:
103
+ rubygems_version: 1.3.7
59
104
  signing_key:
60
- specification_version: 2
61
- summary: Provide lazy method calls for all methods on every object to aid in caching.
105
+ specification_version: 3
106
+ summary: Gem that adds lazy method wrapping to every object. Preceding any method with lazy_ will defer the invocation until the result is actually needed.
62
107
  test_files:
63
108
  - spec/lazy_method_spec.rb
64
109
  - spec/method_tester.rb
110
+ - spec/spec_helper.rb
data/README DELETED
@@ -1,37 +0,0 @@
1
- == LazyMethods
2
-
3
- So your Rails application is successful beyond your wildest dreams and now your meager servers are straining under the load. You figure you'll add some caching to your views and all will be well again. Unfortunately, your controller actions load up all sorts of instance variables with records from the database. The caching won't do you much good if your database is still getting slammed. You could move your query logic to the views behind the caching layer, but that just feels icky. Besides, that will break all your tests and you really don't feel like fixing them.
4
-
5
- == Enter LazyMethods.
6
-
7
- This plugin adds a virtual lazy version of every method on every class. Lazy methods have the same name as the original method name but prefixed with "lazy_". A lazy method will return a proxy object that looks and acts just like the result from calling the actual method. The trick is that the actual method will not be called until a method is invoked on the proxy object. This way you can continue to set up the business logic in your controller and have it only actually executed only as needed.
8
-
9
- If you add fragment caching to your views and the cache returns a value and bypasses your view code, the method will never be invoked. Thanks to the magic of Ruby the proxy object will even act like the class it is proxying in class to class and kind_of?
10
-
11
- A simple example:
12
-
13
- The normal way to do it (at least according to every tutorial):
14
-
15
- def index
16
- @records = MyRecord.find(:all, :conditions => {:name => params[:name]})
17
- end
18
-
19
- And in the view:
20
-
21
- <% cache(params[:names]) -%>
22
- <% @records.each do |record| -%>
23
- <div><%=record.title%></div>
24
- <% end -%>
25
- <% end -%>
26
-
27
- Now even if you cache the fragments in your view that use @records, the database will still be hit to select and instantiate all the records. You could remove the code from the action and simple add it back into the view. However, this just feels wrong and is inherently harder to test. Instead just use a lazy method.
28
-
29
- def index
30
- @records = MyRecord.lazy_find(:all, :conditions => {:name => params[:name]})
31
- end
32
-
33
- Now, as long as no methods are invoked on @records, the original find method will never be called. As soon as the first method is called, the original find method will be called. It will never be called more than once. You can even pass in a block to the lazy method.
34
-
35
- == Testing
36
-
37
- Since the proxy object looks and acts just like the real result object, all your view tests should still pass. Your controller tests should pass will little or no tweaking.