has_browser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 James Golick, Giraffesoft Inc.
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 ADDED
@@ -0,0 +1,44 @@
1
+ HasBrowser
2
+ ==========
3
+
4
+ has_browser makes it possible to create simple, parameterized browser interfaces to your models. That is, given a set of parameters, return all the models that match.
5
+
6
+ == Usage
7
+
8
+ It's a simple plugin, with a simple syntax. Using the canonical blog example, let's imagine we want to create a browse interface to posts. We'd want the user to be able to browse by category, author, or tags, but not to be able to access any of the other finders on the Post model for obvious security reasons. To set up has_browser, we'd do something like this:
9
+
10
+ has_browser :category, :tags, :author
11
+
12
+ Then, assuming the has_finders are already written, the posts can be browsed as follows:
13
+
14
+ Post.browse(:category => 'Testing', :tags => 'activerecord', :author => 'james')
15
+
16
+ In that example, each of the finders requires an argument; has_browser also supports finders that don't. As long as the argumentless finder is present in the browse hash, it will be called:
17
+
18
+ has_browser :category, :tags, :author, :without_args => [:order_by_date, :order_by_number_of_comments]
19
+
20
+ Post.browse(:category => 'Testing', :tags => 'activerecord', :author => 'james', :order_by_number_of_comments => 'true')
21
+
22
+ Browse can also be called from association_proxies. For a multi-blog platform, we could easily restrict browsing of posts to the current blog:
23
+
24
+ @blog.posts.browse(:category => 'Testing', :tags => 'activerecord', :author => 'james', :order_by_number_of_comments => 'true')
25
+
26
+ Since has_browser returns the same proxy as has_finder, it is possible to further restrict the results of a browse by chaining finders after the browse call. With our blog, for example, we'd probably want to restrict browsing to published posts.
27
+
28
+ @blog.posts.browse(:category => 'Testing', :tags => 'activerecord', :author => 'james', :order_by_number_of_comments => 'true').published
29
+
30
+ Note: It is not possible to chain finders before the browse call.
31
+
32
+ Finally, like has_finder, has_browser is compatible with will_paginate out of the box.
33
+
34
+ == Releases & Development
35
+
36
+ has_browser will be released as a gem:
37
+
38
+ $ sudo gem install has_browser
39
+
40
+ development will continue at {github}[http://github.com/giraffesoft/has_browser]
41
+
42
+ == Credits & License
43
+
44
+ Copyright (c) 2008 {James Golick}[http://jamesgolick.com], {GiraffeSoft Inc.}[http://giraffesoft.ca], released under the {MIT License}[http://en.wikipedia.org/wiki/MIT_License]
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/clean'
5
+ require 'lib/has_browser'
6
+ require 'lib/has_browser/version'
7
+
8
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ ActiveRecord::Base.send(:include, HasBrowser)
2
+ [ActiveRecord::Associations::HasManyAssociation, ActiveRecord::Associations::HasManyThroughAssociation].each do |c|
3
+ c.send(:include, HasBrowser::AssociationProxyMethods)
4
+ end
@@ -0,0 +1,11 @@
1
+ module HasBrowser
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+
9
+ CODENAME = 'None'
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ module HasBrowser
2
+ NAME = 'has_browser'
3
+
4
+ class InvalidFinder < ArgumentError; end
5
+
6
+ def self.included(receiver)
7
+ receiver.send :extend, ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def has_browser(*args)
12
+ extend BrowserMethods
13
+ class_inheritable_accessor :has_browser_allowed_finders
14
+ self.has_browser_allowed_finders = {}
15
+
16
+ has_browser_allowed_finders[:without_args] = args.extract_options![:without_args] || []
17
+ has_browser_allowed_finders[:with_args] = args
18
+ end
19
+ end
20
+
21
+ module BrowserMethods
22
+ def browse(*args)
23
+ params = args.extract_options!.symbolize_keys
24
+
25
+ invalid_finders = params.keys.reject { |k| has_browser_allowed_finders.values.inject(&:+).include?(k) }
26
+ raise InvalidFinder.new(invalid_finders.join(', ').to_s) unless invalid_finders.empty?
27
+
28
+ params.inject(args.first || self) do |proxy, finder|
29
+ has_browser_allowed_finders[:with_args].include?(finder.first.to_sym) ? proxy.send(finder.first, finder.last) : proxy.send(finder.first)
30
+ end
31
+ end
32
+ end
33
+
34
+ module AssociationProxyMethods
35
+ def self.included(receiver)
36
+ receiver.class_eval do
37
+ alias_method_chain :method_missing, :has_browser
38
+ end
39
+ end
40
+
41
+ def method_missing_with_has_browser(method, *args, &block)
42
+ method == :browse && proxy_reflection.klass.respond_to?(:has_browser_allowed_finders) ? proxy_reflection.klass.browse(self, args.extract_options!) : method_missing_without_has_browser(method, *args, &block)
43
+ end
44
+ end
45
+ end
data/tasks/gem.rake ADDED
@@ -0,0 +1,55 @@
1
+ require 'rake/gempackagetask'
2
+
3
+ task :clean => :clobber_package
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = HasBrowser::NAME
7
+ s.version = HasBrowser::Version::STRING
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary =
10
+ s.description = "has_browser makes it possible to create simple, parameterized browser interfaces to your models."
11
+ s.author = "James Golick"
12
+ s.email = 'james@giraffesoft.ca'
13
+ s.homepage = 'http://jamesgolick.com/'
14
+
15
+ s.required_ruby_version = '>= 1.8.6'
16
+
17
+ s.files = %w(MIT-LICENSE README Rakefile init.rb) +
18
+ Dir.glob("{lib,test,tasks}/**/*")
19
+
20
+ s.require_path = "lib"
21
+ end
22
+
23
+ Rake::GemPackageTask.new(spec) do |p|
24
+ p.gem_spec = spec
25
+ end
26
+
27
+ task :tag_warn do
28
+ puts "*" * 40
29
+ puts "Don't forget to tag the release:"
30
+ puts " git tag -a v#{HasBrowser::Version::STRING}"
31
+ puts "*" * 40
32
+ end
33
+ task :gem => :tag_warn
34
+
35
+ task :compile do
36
+ end
37
+
38
+ namespace :gem do
39
+ namespace :upload do
40
+ desc 'Upload gems to rubyforge.org'
41
+ task :rubyforge => :gem do
42
+ sh 'rubyforge login'
43
+ sh "rubyforge add_release giraffesoft has_browser #{HasBrowser::Version::STRING} pkg/#{spec.full_name}.gem"
44
+ sh "rubyforge add_file giraffesoft has_browser #{HasBrowser::Version::STRING} pkg/#{spec.full_name}.gem"
45
+ end
46
+ end
47
+ end
48
+
49
+ task :install => [:clobber, :compile, :package] do
50
+ sh "sudo gem install pkg/#{spec.full_name}.gem"
51
+ end
52
+
53
+ task :uninstall => :clobber do
54
+ sh "sudo gem uninstall #{spec.name}"
55
+ end
data/tasks/test.rake ADDED
@@ -0,0 +1,8 @@
1
+ task :default => [:test]
2
+
3
+ desc "Run basic tests"
4
+ Rake::TestTask.new("test") { |t|
5
+ t.pattern = 'test/*_test.rb'
6
+ t.verbose = true
7
+ t.warning = true
8
+ }
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__)+'/test_helper'
2
+
3
+ class HasBrowserTest < Test::Unit::TestCase
4
+ def test_should_call_appropriate_finders_when_somebody_browses_by_parameters
5
+ PhotoMock.expects(:title).with('something').returns(PhotoMock)
6
+ PhotoMock.expects(:description).with('lorem ipsum dolor').returns(PhotoMock)
7
+ PhotoMock.expects(:order_by_date).returns(PhotoMock)
8
+
9
+ PhotoMock.browse(:title => 'something', :description => 'lorem ipsum dolor', :order_by_date => 'true')
10
+ end
11
+
12
+ def test_browsing_with_an_alternate_target
13
+ proxy_mock = mock
14
+ proxy_mock.expects(:title).with('something').returns(proxy_mock)
15
+ proxy_mock.expects(:description).with('lorem ipsum dolor').returns(proxy_mock)
16
+ proxy_mock.expects(:order_by_date).returns(proxy_mock)
17
+
18
+ PhotoMock.browse(proxy_mock, :title => 'something', :description => 'lorem ipsum dolor', :order_by_date => 'true')
19
+ end
20
+
21
+ def test_association_proxy_finder_should_proxy_to_the_reflected_class_browse_method
22
+ proxy_mock = AssocProxyMock.new
23
+ proxy_mock.stubs(:proxy_reflection).returns(stub(:klass => PhotoMock))
24
+
25
+ PhotoMock.expects(:browse).with(proxy_mock, :title => 'something', :description => 'lorem ipsum dolor', :order_by_date => 'true')
26
+
27
+ proxy_mock.browse(:title => 'something', :description => 'lorem ipsum dolor', :order_by_date => 'true')
28
+ end
29
+
30
+ def test_association_proxy_should_raise_if_browse_is_called_and_target_doesnt_have_a_browser
31
+ proxy_mock = AssocProxyMock.new
32
+ proxy_mock.stubs(:proxy_reflection).returns(stub(:klass => String))
33
+
34
+ assert_raise(NoMethodError) do
35
+ proxy_mock.browse({})
36
+ end
37
+ end
38
+
39
+ def test_should_not_raise_when_keys_are_strings
40
+ PhotoMock.stubs(:title)
41
+ assert_nothing_raised do
42
+ PhotoMock.browse('title' => 'asdf')
43
+ end
44
+ end
45
+
46
+ def test_should_raise_invalid_finder_exception_if_somebody_tries_to_browse_by_a_finder_not_specified_in_the_browse_call
47
+ assert_raise(HasBrowser::InvalidFinder) do
48
+ PhotoMock.browse(:invalid => 'danger!')
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,15 @@
1
+ require "rubygems"
2
+ require "mocha"
3
+ require "activesupport"
4
+ require "test/unit"
5
+ require File.dirname(__FILE__)+"/../lib/has_browser"
6
+
7
+ class PhotoMock
8
+ include HasBrowser
9
+
10
+ has_browser :title, :description, :without_args => [:order_by_date, :order_by_relevance]
11
+ end
12
+
13
+ class AssocProxyMock
14
+ include HasBrowser::AssociationProxyMethods
15
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_browser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - James Golick
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-05-19 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: has_browser makes it possible to create simple, parameterized browser interfaces to your models.
17
+ email: james@giraffesoft.ca
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - MIT-LICENSE
26
+ - README
27
+ - Rakefile
28
+ - init.rb
29
+ - lib/has_browser
30
+ - lib/has_browser/version.rb
31
+ - lib/has_browser.rb
32
+ - test/has_browser_test.rb
33
+ - test/test_helper.rb
34
+ - tasks/gem.rake
35
+ - tasks/test.rake
36
+ has_rdoc: false
37
+ homepage: http://jamesgolick.com/
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.6
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.1.1
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: has_browser makes it possible to create simple, parameterized browser interfaces to your models.
62
+ test_files: []
63
+