context_aware_scope 0.0.2

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,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/README.textile ADDED
@@ -0,0 +1,41 @@
1
+ h1. Context Aware Scope
2
+
3
+ An extension to ActiveRecord's named scope
4
+
5
+ h2. Usage
6
+
7
+ 1. Add a context to a named_scope:
8
+
9
+ <pre>
10
+ named_scope :luxurious, :conditions => ['price > ?', 100], :context => {:price => 'luxurious'}
11
+ named_scope :recent, :conditions => ['created_at > ?', 1.week.ago], :context => {:created_at => 'brand new'}
12
+ </pre>
13
+
14
+
15
+ 2. Execute the scopes:
16
+
17
+ <pre>
18
+ @products = Product.new.luxurious
19
+ </pre>
20
+
21
+ 3. And get the context:
22
+
23
+ <pre>
24
+ @products.context
25
+ # => {:price => 'luxurious', :created_at => 'brand new'}
26
+ "You are looking at #{@products.context.values.to_sentence} products"
27
+ # => "You are looking at brand new and luxurious products"
28
+ </pre>
29
+
30
+ h2. Installation
31
+
32
+ Install the ContextAwareScope gem:
33
+
34
+ <pre>
35
+ gem install context_aware_scope
36
+ </pre>
37
+
38
+
39
+ h2. Compatibility
40
+ Only tested with ActiveRecord 2.3.8.
41
+ Rails 3 support planned.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "context_aware_scope"
8
+ gem.summary = %Q{Add context to named_scopes in ActiveRecord}
9
+ gem.description = %Q{Allows to add a context to a named_scope that will be passed through the whole scope chain}
10
+ gem.email = "info@simplificator.com"
11
+ gem.homepage = "http://github.com/simplificator/context_aware_scope"
12
+ gem.authors = ["Simplificator", "Fabio Kuhn"]
13
+ gem.add_development_dependency "shoulda", ">= 2.11"
14
+ gem.add_development_dependency "leftright"
15
+ gem.add_dependency "activerecord", ">= 2.0.0"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/*_test.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/*_test.rb'
35
+ test.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :test => :check_dependencies
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "context aware scope #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
@@ -0,0 +1,64 @@
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{context_aware_scope}
8
+ s.version = "0.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Simplificator", "Fabio Kuhn"]
12
+ s.date = %q{2010-08-10}
13
+ s.description = %q{Allows to add a context to a named_scope that will be passed through the whole scope chain}
14
+ s.email = %q{info@simplificator.com}
15
+ s.extra_rdoc_files = [
16
+ "README.textile"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README.textile",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "context_aware_scope.gemspec",
24
+ "lib/context_aware_scope.rb",
25
+ "test/database.yml",
26
+ "test/models.rb",
27
+ "test/test_helper.rb",
28
+ "test/units/lambda_scope_test.rb",
29
+ "test/units/normal_scope_test.rb",
30
+ "test/units/readme_sample_test.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/simplificator/context_aware_scope}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{Add context to named_scopes in ActiveRecord}
37
+ s.test_files = [
38
+ "test/models.rb",
39
+ "test/test_helper.rb",
40
+ "test/units/lambda_scope_test.rb",
41
+ "test/units/normal_scope_test.rb",
42
+ "test/units/readme_sample_test.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_development_dependency(%q<shoulda>, [">= 2.11"])
51
+ s.add_development_dependency(%q<leftright>, [">= 0"])
52
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.0.0"])
53
+ else
54
+ s.add_dependency(%q<shoulda>, [">= 2.11"])
55
+ s.add_dependency(%q<leftright>, [">= 0"])
56
+ s.add_dependency(%q<activerecord>, [">= 2.0.0"])
57
+ end
58
+ else
59
+ s.add_dependency(%q<shoulda>, [">= 2.11"])
60
+ s.add_dependency(%q<leftright>, [">= 0"])
61
+ s.add_dependency(%q<activerecord>, [">= 2.0.0"])
62
+ end
63
+ end
64
+
@@ -0,0 +1,58 @@
1
+ module ContextAwareScope
2
+ def self.included(base)
3
+ base.class_eval do
4
+ extend ClassMethods
5
+ class_eval do
6
+ def initialize_with_context(proxy_scope, options, &block)
7
+ @context = options ? options[:context] || {} : {}
8
+ initialize_without_context(proxy_scope, options, &block)
9
+ end
10
+
11
+ alias_method_chain :initialize, :context
12
+
13
+ # get current context from scope chain
14
+ def context
15
+ p @proxy_scope.class
16
+ if @proxy_scope.class == ActiveRecord::NamedScope::Scope
17
+ p @context
18
+ recursive_context = @proxy_scope.context
19
+ recursive_context = recursive_context.keep_merge(@context)
20
+ recursive_context
21
+ else
22
+ p @context
23
+ @context
24
+ end
25
+ end
26
+
27
+ # exclude context from proxy options
28
+ # that way it is not sent to the with_scope on the ActiveRecord model
29
+ def proxy_options
30
+ @proxy_options.except(:context)
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ module ClassMethods
37
+
38
+ end
39
+ end
40
+
41
+ class ActiveRecord::NamedScope::Scope
42
+ include ContextAwareScope
43
+ end
44
+
45
+ class Hash
46
+ def keep_merge(hash)
47
+ target = dup
48
+ hash.keys.each do |key|
49
+ if hash[key].is_a? Hash and self[key].is_a? Hash
50
+ target[key] = target[key].keep_merge(hash[key])
51
+ next
52
+ end
53
+ #target[key] = hash[key]
54
+ target.update(hash) { |key, *values| values.flatten.uniq }
55
+ end
56
+ target
57
+ end
58
+ end
data/test/database.yml ADDED
@@ -0,0 +1,3 @@
1
+ sqlite:
2
+ adapter: sqlite3
3
+ database: ":memory:"
data/test/models.rb ADDED
@@ -0,0 +1,73 @@
1
+ class Order < ActiveRecord::Base
2
+ belongs_to :customer
3
+ named_scope :by_price, lambda{|price| {:conditions => ['price > ?', price], :context => {:price => "> #{price}"}}}
4
+ named_scope :by_product, lambda{|name| {:conditions => {:product_name => name}, :context => {:product => name}}}
5
+ named_scope :expensive, :conditions => ['price > ?', 10], :context => {:price => '> 10'}
6
+ named_scope :with_camel, :conditions => {:product_name => 'camel'}, :context => {:product => 'camel'}
7
+
8
+ end
9
+
10
+ class Customer < ActiveRecord::Base
11
+ has_many :orders
12
+ belongs_to :city
13
+ named_scope :by_name, lambda{|name| {:conditions => ['name like ?', "%#{name}%"], :context => {:filter => {:customer => {:name => name}}}}}
14
+ end
15
+
16
+ class City < ActiveRecord::Base
17
+ has_many :customers
18
+ end
19
+
20
+ class Product < ActiveRecord::Base
21
+ named_scope :luxurious, :conditions => ['price > ?', 100], :context => {:price => 'luxurious'}
22
+ named_scope :recent, :conditions => ['created_at > ?', 1.week.ago], :context => {:created_at => 'brand new'}
23
+ end
24
+
25
+ def setup_database(name)
26
+ database_yml = YAML.load_file(File.join(File.dirname(__FILE__), 'database.yml'))
27
+ ActiveRecord::Base.establish_connection(database_yml[name])
28
+ ActiveRecord::Base.configurations = true
29
+
30
+ ActiveRecord::Schema.verbose = false
31
+ ActiveRecord::Schema.define(:version => 1) do
32
+ create_table :orders do |t|
33
+ t.string :product_name
34
+ t.integer :price
35
+ t.date :purchased_at
36
+ t.integer :customer_id
37
+ end
38
+
39
+ create_table :customers do |t|
40
+ t.string :name
41
+ t.integer :credit
42
+ t.integer :city_id
43
+ end
44
+
45
+ create_table :cities do |t|
46
+ t.string :name
47
+ end
48
+
49
+ create_table :products do |t|
50
+ t.string :name
51
+ t.datetime :created_at
52
+ t.integer :price
53
+ end
54
+ end
55
+ end
56
+
57
+ def seed_models
58
+ city1 = City.create(:name => 'Zurich')
59
+ city2 = City.create(:name => 'Basel')
60
+ customer1 = Customer.create(:name => 'Aladin', :city => city1)
61
+ customer2 = Customer.create(:name => 'Ali Baba', :city => city1)
62
+ customer3 = Customer.create(:name => 'Sidi Abdel', :city => city2)
63
+ order1 = Order.create(:customer => customer1, :product_name => 'magic carpet', :price => 500, :purchased_at => '2010-07-22')
64
+ order2 = Order.create(:customer => customer1, :product_name => 'old lamp', :price => 5, :purchased_at => '2010-07-22')
65
+ order3 = Order.create(:customer => customer2, :product_name => 'camel', :price => 1200, :purchased_at => '2010-07-24')
66
+ product = Product.create(:name => 'Bracelet', :price => 324, :created_at => 1.day.ago)
67
+ end
68
+
69
+ def cleanup_database
70
+ [City, Customer, Order, Product].each do |model_class|
71
+ model_class.delete_all
72
+ end
73
+ end
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'leftright'
5
+ require 'active_record'
6
+
7
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib', 'context_aware_scope'))
8
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
9
+ require 'context_aware_scope'
10
+
11
+ #ActiveRecord::Base.logger = Logger.new(STDOUT)
12
+ #ActiveRecord::Base.logger.level = Logger::DEBUG
13
+
14
+ require File.join(File.dirname(__FILE__), 'models')
15
+
16
+ setup_database('sqlite')
17
+
18
+ class Test::Unit::TestCase
19
+ def teardown
20
+ cleanup_database
21
+ end
22
+ end
@@ -0,0 +1,63 @@
1
+ require 'test_helper'
2
+
3
+ class LambdaScopeTest < Test::Unit::TestCase
4
+ context 'orders' do
5
+ setup do
6
+ seed_models
7
+ end
8
+
9
+ context 'with a simple scope with text context' do
10
+ setup do
11
+ @orders = Order.by_price(10)
12
+ end
13
+
14
+ should 'should have results' do
15
+ assert_equal 2, @orders.count
16
+ end
17
+
18
+ should 'have context on list' do
19
+ assert_equal ActiveRecord::NamedScope::Scope, @orders.class
20
+ assert_equal Hash, @orders.context.class
21
+ assert_equal ({:price => '> 10'}), @orders.context
22
+ end
23
+ end
24
+
25
+ context 'with a chained scope with text context' do
26
+ setup do
27
+ @orders = Order.by_product('camel').by_price(10)
28
+ end
29
+
30
+ should 'should have results' do
31
+ assert_equal 1, @orders.count
32
+ end
33
+
34
+ should 'set a string context' do
35
+ assert_equal Hash, @orders.context.class
36
+ assert_equal 2, @orders.context.size
37
+ assert_equal '> 10', @orders.context[:price]
38
+ assert_equal 'camel', @orders.context[:product]
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'customers' do
44
+ setup do
45
+ seed_models
46
+ end
47
+
48
+ context 'with a chained scope and a deep merging context' do
49
+ setup do
50
+ @customers = Customer.by_name('Al').by_name('Ba')
51
+ end
52
+
53
+ should 'should have results' do
54
+ assert_equal 1, @customers.count
55
+ end
56
+
57
+ should 'set a string context' do
58
+ assert_equal Hash, @customers.context.class
59
+ assert_equal ({:name => ['Al', 'Ba']}), @customers.context[:filter][:customer]
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ class NormalScopeTest < Test::Unit::TestCase
4
+ context 'orders' do
5
+ setup do
6
+ seed_models
7
+ end
8
+
9
+ context 'with a simple scope with text context' do
10
+ setup do
11
+ @orders = Order.expensive
12
+ end
13
+
14
+ should 'should have results' do
15
+ assert_equal 2, @orders.count
16
+ end
17
+
18
+ should 'have context on list' do
19
+ assert_equal ActiveRecord::NamedScope::Scope, @orders.class
20
+ assert_equal Hash, @orders.context.class
21
+ assert_equal ({:price => '> 10'}), @orders.context
22
+ end
23
+ end
24
+
25
+ context 'with a chained scope with text context' do
26
+ setup do
27
+ @orders = Order.with_camel.expensive
28
+ end
29
+
30
+ should 'should have results' do
31
+ assert_equal 1, @orders.count
32
+ end
33
+
34
+ should 'set a string context' do
35
+ assert_equal Hash, @orders.context.class
36
+ assert_equal 2, @orders.context.size
37
+ assert_equal '> 10', @orders.context[:price]
38
+ assert_equal 'camel', @orders.context[:product]
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ class ReadmeSampleTest < Test::Unit::TestCase
4
+ context 'new and luxurious products' do
5
+ setup do
6
+ seed_models
7
+ @products = Product.recent.luxurious
8
+ end
9
+
10
+ should 'should have results' do
11
+ assert_equal 1, @products.count
12
+ assert_equal ({:price => 'luxurious', :created_at => 'brand new'}), @products.context
13
+ assert_equal "You are looking at brand new and luxurious products", "You are looking at #{@products.context.values.to_sentence} products"
14
+ end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: context_aware_scope
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Simplificator
14
+ - Fabio Kuhn
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-10 00:00:00 +02:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: shoulda
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 21
31
+ segments:
32
+ - 2
33
+ - 11
34
+ version: "2.11"
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: leftright
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
+ - !ruby/object:Gem::Dependency
52
+ name: activerecord
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 15
60
+ segments:
61
+ - 2
62
+ - 0
63
+ - 0
64
+ version: 2.0.0
65
+ type: :runtime
66
+ version_requirements: *id003
67
+ description: Allows to add a context to a named_scope that will be passed through the whole scope chain
68
+ email: info@simplificator.com
69
+ executables: []
70
+
71
+ extensions: []
72
+
73
+ extra_rdoc_files:
74
+ - README.textile
75
+ files:
76
+ - .gitignore
77
+ - README.textile
78
+ - Rakefile
79
+ - VERSION
80
+ - context_aware_scope.gemspec
81
+ - lib/context_aware_scope.rb
82
+ - test/database.yml
83
+ - test/models.rb
84
+ - test/test_helper.rb
85
+ - test/units/lambda_scope_test.rb
86
+ - test/units/normal_scope_test.rb
87
+ - test/units/readme_sample_test.rb
88
+ has_rdoc: true
89
+ homepage: http://github.com/simplificator/context_aware_scope
90
+ licenses: []
91
+
92
+ post_install_message:
93
+ rdoc_options:
94
+ - --charset=UTF-8
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ hash: 3
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ requirements: []
116
+
117
+ rubyforge_project:
118
+ rubygems_version: 1.3.7
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: Add context to named_scopes in ActiveRecord
122
+ test_files:
123
+ - test/models.rb
124
+ - test/test_helper.rb
125
+ - test/units/lambda_scope_test.rb
126
+ - test/units/normal_scope_test.rb
127
+ - test/units/readme_sample_test.rb