sinatra-has_scope 0.0.1
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 +3 -0
- data/Gemfile +4 -0
- data/README.mdown +71 -0
- data/Rakefile +2 -0
- data/lib/sinatra/has_scope/version.rb +5 -0
- data/lib/sinatra/has_scope.rb +141 -0
- data/sinatra-has_scope.gemspec +22 -0
- metadata +91 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.mdown
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
## Sinatra HasScope
|
2
|
+
|
3
|
+
Sinatra HasScope is a Sinatra adaptation of the very useful [HasScope Rails library](https://github.com/plataformatec/has_scope).
|
4
|
+
|
5
|
+
Sinatra HasScope allows you to easily create filters based on your resources named scopes.
|
6
|
+
Imagine the following model called Graduation:
|
7
|
+
|
8
|
+
class Graduation < ActiveRecord::Base
|
9
|
+
scope :featured, where('featured = ?', true)
|
10
|
+
scope :by_degree, lambda { |degree| { where('degree = ?', degree) } }
|
11
|
+
end
|
12
|
+
|
13
|
+
You can use those named scopes as filters by declaring them on your routes:
|
14
|
+
|
15
|
+
class MyApp < Sinatra::Base
|
16
|
+
has_scope :gradutation, :featured, :type => :boolean
|
17
|
+
has_scope :gradutation, :by_degree
|
18
|
+
has_scope :gradutation, :by_period, :using => [:started_at, :ended_at]
|
19
|
+
end
|
20
|
+
|
21
|
+
Now, if you want to apply them to an specific resource, you just need to call `apply_scopes`:
|
22
|
+
|
23
|
+
get '/graduations' do
|
24
|
+
@graduations = apply_scopes(:graduation, Graduation, params).all
|
25
|
+
end
|
26
|
+
|
27
|
+
Then for each request:
|
28
|
+
|
29
|
+
/graduations
|
30
|
+
#=> acts like a normal request
|
31
|
+
|
32
|
+
/graduations?featured=true
|
33
|
+
#=> calls the named scope and bring featured graduations
|
34
|
+
|
35
|
+
/graduations?featured=true&by_degree=phd
|
36
|
+
#=> brings featured graduations with phd degree
|
37
|
+
|
38
|
+
/graduations?params[by_period][started_at]=20100701¶ms[by_period][ended_at]=20101013
|
39
|
+
#=> brings graduations in the given period
|
40
|
+
|
41
|
+
## Installation
|
42
|
+
|
43
|
+
Sinatra HasScope is available as gem on Gemcutter, so just run the following:
|
44
|
+
|
45
|
+
sudo gem install sinatra-has_scope
|
46
|
+
|
47
|
+
To install it with bundler, use:
|
48
|
+
|
49
|
+
gem 'sinatra-has_scope', :require => 'sinatra/has_scope'
|
50
|
+
|
51
|
+
## Options
|
52
|
+
|
53
|
+
HasScope supports several options:
|
54
|
+
|
55
|
+
* `:type` - Checks the type of the parameter sent. If set to :boolean it just calls the named scope, without any argument. By default, it does not allow hashes or arrays to be given, except if type :hash or :array are set.
|
56
|
+
|
57
|
+
* `:as` - The key in the params hash expected to find the scope. Defaults to the scope name.
|
58
|
+
|
59
|
+
* `:using` - The subkeys to be used as args when type is a hash.
|
60
|
+
|
61
|
+
* `:if` - Specifies a method, proc or string to call to determine if the scope should apply.
|
62
|
+
|
63
|
+
* `:unless` - Specifies a method, proc or string to call to determine if the scope should NOT apply.
|
64
|
+
|
65
|
+
* `:default` - Default value for the scope. Whenever supplied the scope is always called.
|
66
|
+
|
67
|
+
* `:allow_blank` - Blank values are not sent to scopes by default. Set to true to overwrite.
|
68
|
+
|
69
|
+
## Bugs and Feedback
|
70
|
+
|
71
|
+
If you discover any bugs or want to drop a line, feel free to create an [issue on GitHub](http://github.com/simonc/sinatra-has_scope/issues).
|
data/Rakefile
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
module HasScope
|
5
|
+
TRUE_VALUES = ["true", true, "1", 1]
|
6
|
+
|
7
|
+
ALLOWED_TYPES = {
|
8
|
+
:array => [ Array ],
|
9
|
+
:hash => [ Hash ],
|
10
|
+
:boolean => [ Object ],
|
11
|
+
:default => [ String, Numeric ]
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_accessor :scopes_configuration
|
15
|
+
|
16
|
+
# Detects params from url and apply as scopes to your classes.
|
17
|
+
#
|
18
|
+
# == Options
|
19
|
+
#
|
20
|
+
# * <tt>:type</tt> - Checks the type of the parameter sent. If set to :boolean
|
21
|
+
# it just calls the named scope, without any argument. By default,
|
22
|
+
# it does not allow hashes or arrays to be given, except if type
|
23
|
+
# :hash or :array are set.
|
24
|
+
#
|
25
|
+
# * <tt>:as</tt> - The key in the params hash expected to find the scope.
|
26
|
+
# Defaults to the scope name.
|
27
|
+
#
|
28
|
+
# * <tt>:using</tt> - If type is a hash, you can provide :using to convert the hash to
|
29
|
+
# a named scope call with several arguments.
|
30
|
+
#
|
31
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
32
|
+
# if the scope should apply
|
33
|
+
#
|
34
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
|
35
|
+
# if the scope should NOT apply.
|
36
|
+
#
|
37
|
+
# * <tt>:default</tt> - Default value for the scope. Whenever supplied the scope
|
38
|
+
# is always called.
|
39
|
+
#
|
40
|
+
# * <tt>:allow_blank</tt> - Blank values are not sent to scopes by default. Set to true to overwrite.
|
41
|
+
#
|
42
|
+
def has_scope(scope_group, *scopes)
|
43
|
+
options = scopes.extract_options!
|
44
|
+
options.symbolize_keys!
|
45
|
+
options.assert_valid_keys(:type, :if, :unless, :default, :as, :using, :allow_blank)
|
46
|
+
|
47
|
+
if options.key?(:using)
|
48
|
+
if options.key?(:type) && options[:type] != :hash
|
49
|
+
raise "You cannot use :using with another :type different than :hash"
|
50
|
+
else
|
51
|
+
options[:type] = :hash
|
52
|
+
end
|
53
|
+
|
54
|
+
options[:using] = [*options[:using]]
|
55
|
+
end
|
56
|
+
|
57
|
+
self.scopes_configuration ||= {}
|
58
|
+
self.scopes_configuration[scope_group] ||= {}
|
59
|
+
|
60
|
+
scopes.each do |scope|
|
61
|
+
self.scopes_configuration[scope_group][scope] ||= {
|
62
|
+
:as => scope,
|
63
|
+
:type => :default
|
64
|
+
}
|
65
|
+
self.scopes_configuration[scope_group][scope].merge!(options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Receives an object where scopes will be applied to.
|
70
|
+
#
|
71
|
+
# has_scope :graduation, :featured, :type => true
|
72
|
+
# has_scope :graduation, :by_degree
|
73
|
+
#
|
74
|
+
# get '/graduations' do
|
75
|
+
# @graduations = apply_scopes(:graduation, Graduation, params).all
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
def apply_scopes(scope_group, target, hash)
|
79
|
+
return target unless scopes_configuration
|
80
|
+
|
81
|
+
self.scopes_configuration[scope_group].each do |scope, options|
|
82
|
+
key = options[:as].to_s
|
83
|
+
|
84
|
+
if hash.key?(key)
|
85
|
+
value, call_scope = hash[key], true
|
86
|
+
elsif options.key?(:default)
|
87
|
+
value, call_scope = options[:default], true
|
88
|
+
value = value.call(self) if value.is_a?(Proc)
|
89
|
+
end
|
90
|
+
|
91
|
+
value = parse_value(options[:type], key, value)
|
92
|
+
|
93
|
+
if call_scope && (value.present? || options[:allow_blank])
|
94
|
+
target = call_scope_by_type(options[:type], scope, target, value, options)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
target
|
99
|
+
end
|
100
|
+
|
101
|
+
# Set the real value for the current scope if type check.
|
102
|
+
def parse_value(type, key, value) #:nodoc:
|
103
|
+
if type == :boolean
|
104
|
+
TRUE_VALUES.include?(value)
|
105
|
+
elsif value && ALLOWED_TYPES[type].none?{ |klass| value.is_a?(klass) }
|
106
|
+
raise "Expected type :#{type} in params[:#{key}], got #{value.class}"
|
107
|
+
else
|
108
|
+
value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Call the scope taking into account its type.
|
113
|
+
def call_scope_by_type(type, scope, target, value, options) #:nodoc:
|
114
|
+
if type == :boolean
|
115
|
+
target.send(scope)
|
116
|
+
elsif value && options.key?(:using)
|
117
|
+
value = value.values_at(*options[:using])
|
118
|
+
target.send(scope, *value)
|
119
|
+
else
|
120
|
+
target.send(scope, value)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Evaluates the scope options :if or :unless. Returns true if the proc
|
125
|
+
# method, or string evals to the expected value.
|
126
|
+
def applicable?(string_proc_or_symbol, expected) #:nodoc:
|
127
|
+
case string_proc_or_symbol
|
128
|
+
when String
|
129
|
+
eval(string_proc_or_symbol) == expected
|
130
|
+
when Proc
|
131
|
+
string_proc_or_symbol.call(self) == expected
|
132
|
+
when Symbol
|
133
|
+
send(string_proc_or_symbol) == expected
|
134
|
+
else
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
register HasScope
|
141
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/sinatra/has_scope/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "sinatra-has_scope"
|
6
|
+
s.version = Sinatra::HasScope::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ['Simon COURTOIS']
|
9
|
+
s.email = ['scourtois@cubyx.fr']
|
10
|
+
s.homepage = "http://rubygems.org/gems/sinatra-has_scope"
|
11
|
+
s.summary = "HasScope equivalent for Sinatra"
|
12
|
+
s.description = "HasScope readaptation for the Sinatra micro-framework"
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "sinatra-has_scope"
|
16
|
+
|
17
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
21
|
+
s.require_path = 'lib'
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinatra-has_scope
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Simon COURTOIS
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-06 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: bundler
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 1.0.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
description: HasScope readaptation for the Sinatra micro-framework
|
38
|
+
email:
|
39
|
+
- scourtois@cubyx.fr
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- .gitignore
|
48
|
+
- Gemfile
|
49
|
+
- README.mdown
|
50
|
+
- Rakefile
|
51
|
+
- lib/sinatra/has_scope.rb
|
52
|
+
- lib/sinatra/has_scope/version.rb
|
53
|
+
- sinatra-has_scope.gemspec
|
54
|
+
has_rdoc: true
|
55
|
+
homepage: http://rubygems.org/gems/sinatra-has_scope
|
56
|
+
licenses: []
|
57
|
+
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
hash: 3
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 23
|
78
|
+
segments:
|
79
|
+
- 1
|
80
|
+
- 3
|
81
|
+
- 6
|
82
|
+
version: 1.3.6
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project: sinatra-has_scope
|
86
|
+
rubygems_version: 1.3.7
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: HasScope equivalent for Sinatra
|
90
|
+
test_files: []
|
91
|
+
|