rad_ext 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +12 -0
- data/lib/rad/configurators/abstract.rb +22 -0
- data/lib/rad/configurators/runtime.rb +2 -0
- data/lib/rad/configurators/web.rb +32 -0
- data/lib/rad/controller/forgery_protector.rb +31 -0
- data/lib/rad/processors/ajax_helper.rb +16 -0
- data/lib/rad/processors/ensure_no_www.rb +30 -0
- data/lib/rad_ext/configurators.rb +13 -0
- data/lib/rad_ext/extensions/i18n/locales/ru/pluralization.rb +62 -0
- data/lib/rad_ext/extensions/i18n.rb +16 -0
- data/lib/rad_ext/extensions/prepare_model.rb +16 -0
- data/lib/rad_ext/extensions/user_error.rb +12 -0
- data/lib/rad_ext/extensions.rb +7 -0
- data/lib/rad_ext/gems.rb +7 -0
- data/lib/rad_ext/profiles/web_ext.rb +70 -0
- data/lib/rad_ext/protect_from_forgery.rb +56 -0
- data/lib/rad_ext/require.rb +5 -0
- data/lib/rad_ext/spec.rb +4 -0
- data/lib/rad_ext/tasks.rb +18 -0
- data/lib/rad_ext/utils/cli_helper.rb +34 -0
- data/lib/rad_ext.rb +18 -0
- data/readme.md +12 -0
- data/spec/extensions/i18n_spec/locales/en/general.yml +5 -0
- data/spec/extensions/i18n_spec/locales/ru/general.yml +7 -0
- data/spec/extensions/i18n_spec.rb +32 -0
- data/spec/extensions/prepare_model_spec.rb +36 -0
- data/spec/extensions/user_error_spec.rb +36 -0
- data/spec/processors/protect_from_forgery_spec.rb +188 -0
- data/spec/spec_helper/web_profile.rb +3 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/spec_helper_with_rad.rb +4 -0
- metadata +110 -0
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
class Abstract
|
2
|
+
def initialize dir
|
3
|
+
@dir = File.expand_path(dir)
|
4
|
+
end
|
5
|
+
|
6
|
+
# def config options = {}
|
7
|
+
# rad.config.merge_config! "#{dir}/config/config.yml", options
|
8
|
+
# end
|
9
|
+
|
10
|
+
def routes
|
11
|
+
routes_file = "#{dir}/config/routes.rb"
|
12
|
+
load routes_file if File.exist? routes_file
|
13
|
+
end
|
14
|
+
|
15
|
+
def locales
|
16
|
+
I18n.load_path += Dir["#{dir}/config/locales/**/*.{rb,yml}"]
|
17
|
+
I18n.load_path += Dir["#{dir}/config/locales/*.{rb,yml}"]
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
attr_reader :dir #, :after_config, :after_environment
|
22
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Web < Abstract
|
2
|
+
def asset_paths *relative_paths
|
3
|
+
relative_paths = relative_paths.first if relative_paths.first.is_a? Array
|
4
|
+
relative_paths.each do |relative_path|
|
5
|
+
path = "#{dir}/#{relative_path}"
|
6
|
+
rad.assets.paths << path unless rad.assets.paths.include? path
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_paths *relative_paths
|
11
|
+
relative_paths = relative_paths.first if relative_paths.first.is_a? Array
|
12
|
+
relative_paths.each do |relative_path|
|
13
|
+
path = "#{dir}/#{relative_path}"
|
14
|
+
$LOAD_PATH << path unless $LOAD_PATH.include? path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def template_paths *relative_paths
|
19
|
+
rad.template
|
20
|
+
|
21
|
+
relative_paths = relative_paths.first if relative_paths.first.is_a? Array
|
22
|
+
relative_paths.each do |relative_path|
|
23
|
+
path = "#{dir}/#{relative_path}"
|
24
|
+
rad.template.paths << path unless rad.template.paths.include? path
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def autoload_paths *relative_paths
|
29
|
+
relative_paths = relative_paths.first if relative_paths.first.is_a? Array
|
30
|
+
relative_paths.each{|d| autoload_dir "#{dir}/#{d}", true}
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ForgeryProtector
|
2
|
+
BROWSER_GENERATED_TYPES = %w(
|
3
|
+
text/html
|
4
|
+
text/plain
|
5
|
+
application/x-www-form-urlencoded
|
6
|
+
multipart/form-data
|
7
|
+
).to_set
|
8
|
+
|
9
|
+
BROWSER_GENERATED_FORMATS = %w(html js)
|
10
|
+
|
11
|
+
protected
|
12
|
+
def protect_from_forgery
|
13
|
+
request = workspace.request
|
14
|
+
if request.session
|
15
|
+
sat = request.session['authenticity_token']
|
16
|
+
content_type = request.content_type
|
17
|
+
format = workspace.params.format
|
18
|
+
|
19
|
+
allow = (
|
20
|
+
request.get? or
|
21
|
+
(content_type.present? and !BROWSER_GENERATED_TYPES.include?(content_type.downcase)) or
|
22
|
+
(format.present? and !BROWSER_GENERATED_FORMATS.include?(format)) or
|
23
|
+
(sat.present? and sat == params.authenticity_token)
|
24
|
+
)
|
25
|
+
|
26
|
+
raise "invalid authenticity token!" unless allow
|
27
|
+
|
28
|
+
@authenticity_token = sat
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Sets format for :js as :html and wraps body into <textarea>
|
2
|
+
# Forms with files can be submitted by ajax only via iframe, and it requires
|
3
|
+
# the response have 'html' encoding and be wrapped into <textarea>
|
4
|
+
class AjaxHelper < Conveyors::Processor
|
5
|
+
def call
|
6
|
+
response = workspace.response.must_be.defined
|
7
|
+
request = workspace.request.must_be.defined
|
8
|
+
|
9
|
+
next_processor.call
|
10
|
+
|
11
|
+
if workspace.params? and workspace.params.format == 'js' and !request.xhr?
|
12
|
+
response.content_type = Mime['html']
|
13
|
+
workspace.content = "<textarea>#{workspace.content}</textarea>"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# enshure domain has no www. except if there's custom subdomain
|
2
|
+
class EnsureNoWww < Conveyors::Processor
|
3
|
+
def call
|
4
|
+
workspace.params.must_be.defined
|
5
|
+
if workspace.params.format == 'html' and url_with_www?
|
6
|
+
redirect_without_www
|
7
|
+
else
|
8
|
+
next_processor.call
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
def uri
|
14
|
+
@uri ||= Uri.parse workspace.request.url
|
15
|
+
end
|
16
|
+
|
17
|
+
def url_with_www?
|
18
|
+
uri.host =~ /^www\./
|
19
|
+
end
|
20
|
+
|
21
|
+
def redirect_without_www
|
22
|
+
uri.host = uri.host.sub(/^www\./, '')
|
23
|
+
url = uri.to_s
|
24
|
+
|
25
|
+
response = workspace.response
|
26
|
+
response.status = 301
|
27
|
+
response.headers['Location'] = url
|
28
|
+
response.body = %(<html><body>You are being <a href="#{url.html_escape}">redirected</a>.</body></html>)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# %w(
|
2
|
+
# abstract
|
3
|
+
# runtime
|
4
|
+
# web
|
5
|
+
# ).each{|f| require "rad_ext/configurators/#{f}"}
|
6
|
+
|
7
|
+
class Micon::Core
|
8
|
+
def configure configurator_name, dir, &block
|
9
|
+
configurator_class = "Rad::Configurators::#{configurator_name.to_s.classify}".constantize
|
10
|
+
configurator = configurator_class.new dir
|
11
|
+
block.call configurator if block
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# {
|
2
|
+
# :'ru' => {
|
3
|
+
# pluralize: lambda { |n|
|
4
|
+
# # Правило плюрализации для русского языка, взято из CLDR, http://unicode.org/cldr/
|
5
|
+
# #
|
6
|
+
# #
|
7
|
+
# # Russian language pluralization rules, taken from CLDR project, http://unicode.org/cldr/
|
8
|
+
# #
|
9
|
+
# # one -> n mod 10 is 1 and n mod 100 is not 11;
|
10
|
+
# # few -> n mod 10 in 2..4 and n mod 100 not in 12..14;
|
11
|
+
# # many -> n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
|
12
|
+
# # other -> everything else
|
13
|
+
# #
|
14
|
+
# # Пример
|
15
|
+
# #
|
16
|
+
# # :one = 1, 21, 31, 41, 51, 61...
|
17
|
+
# # :few = 2-4, 22-24, 32-34...
|
18
|
+
# # :many = 0, 5-20, 25-30, 35-40...
|
19
|
+
# # :other = 1.31, 2.31, 5.31...
|
20
|
+
# modulo10 = n.modulo(10)
|
21
|
+
# modulo100 = n.modulo(100)
|
22
|
+
#
|
23
|
+
# if modulo10 == 1 && modulo100 != 11
|
24
|
+
# :one
|
25
|
+
# elsif (modulo10 == 2 || modulo10 == 3 || modulo10 == 4) && !(modulo100 == 12 || modulo100 == 13 || modulo100 == 14)
|
26
|
+
# :few
|
27
|
+
# elsif modulo10 == 0 || (modulo10 == 5 || modulo10 == 6 || modulo10 == 7 || modulo10 == 8 || modulo10 == 9) || (modulo100 == 11 || modulo100 == 12 || modulo100 == 13 || modulo100 == 14)
|
28
|
+
# :many
|
29
|
+
# else
|
30
|
+
# :other
|
31
|
+
# end
|
32
|
+
# }
|
33
|
+
# }
|
34
|
+
# }
|
35
|
+
|
36
|
+
{
|
37
|
+
ru: {
|
38
|
+
i18n: {
|
39
|
+
plural: {
|
40
|
+
rule: lambda{|n|
|
41
|
+
# Правило плюрализации для русского языка, взято из CLDR, http://unicode.org/cldr/
|
42
|
+
#
|
43
|
+
#
|
44
|
+
# Russian language pluralization rules, taken from CLDR project, http://unicode.org/cldr/
|
45
|
+
#
|
46
|
+
# one -> n mod 10 is 1 and n mod 100 is not 11;
|
47
|
+
# few -> n mod 10 in 2..4 and n mod 100 not in 12..14;
|
48
|
+
# many -> n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
|
49
|
+
# other -> everything else
|
50
|
+
#
|
51
|
+
# Пример
|
52
|
+
#
|
53
|
+
# :one = 1, 21, 31, 41, 51, 61...
|
54
|
+
# :few = 2-4, 22-24, 32-34...
|
55
|
+
# :many = 0, 5-20, 25-30, 35-40...
|
56
|
+
# :other = 1.31, 2.31, 5.31...
|
57
|
+
n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Hack, ActiveSupport currently uses differrent version
|
2
|
+
# gem 'i18n', '>= 0.4.1'
|
3
|
+
# require 'i18n'
|
4
|
+
|
5
|
+
require "i18n/backend/pluralization"
|
6
|
+
I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
|
7
|
+
|
8
|
+
I18n.load_path += Dir["#{__FILE__.dirname}/i18n/locales/*/*.{rb,yml}"]
|
9
|
+
|
10
|
+
|
11
|
+
#
|
12
|
+
# Helpers for Rad
|
13
|
+
#
|
14
|
+
[Rad::Controller::Abstract, Rad::Controller::Context].each do |klass|
|
15
|
+
klass.delegate :t, to: I18n
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Rad::Controller::Abstract::ClassMethods.class_eval do
|
2
|
+
def prepare_model aclass, opt = {}
|
3
|
+
opt = opt.symbolize_keys
|
4
|
+
id = opt.delete(:id) || :id
|
5
|
+
variable = opt.delete(:variable) || aclass.alias.underscore
|
6
|
+
|
7
|
+
finder = opt.delete(:finder) || :find!
|
8
|
+
|
9
|
+
method = "prepare_#{variable}"
|
10
|
+
define_method method do
|
11
|
+
model = aclass.send finder, params[id]
|
12
|
+
instance_variable_set "@#{variable}", model
|
13
|
+
end
|
14
|
+
before method, opt
|
15
|
+
end
|
16
|
+
end
|
data/lib/rad_ext/gems.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# Router
|
3
|
+
#
|
4
|
+
rad.router.routers = [
|
5
|
+
[:alias_router, Rad::Router::AliasRouter.new],
|
6
|
+
[:basic_router, Rad::Router::BasicRouter.new],
|
7
|
+
[:restful_router, Rad::Router::RestfulRouter.new],
|
8
|
+
[:object_router, Rad::Router::ObjectRouter.new]
|
9
|
+
]
|
10
|
+
|
11
|
+
|
12
|
+
#
|
13
|
+
# Conveyors
|
14
|
+
#
|
15
|
+
rad.conveyors.web do |web|
|
16
|
+
# conveyor
|
17
|
+
web.use Rad::Conveyors::Processors::ConveyorLogger
|
18
|
+
|
19
|
+
# http
|
20
|
+
web.use Rad::Http::Processors::HttpWriter
|
21
|
+
web.use Rad::Http::Processors::PrepareParams
|
22
|
+
web.use Rad::Http::Processors::EvaluateFormat
|
23
|
+
web.use Rad::Http::Processors::HttpLogger
|
24
|
+
|
25
|
+
# forgery protection
|
26
|
+
web.use Rad::Processors::PrepareAutenticityToken
|
27
|
+
|
28
|
+
# ensure no www
|
29
|
+
web.use Rad::Processors::EnsureNoWww
|
30
|
+
|
31
|
+
# ajax
|
32
|
+
web.use Rad::Processors::AjaxHelper
|
33
|
+
|
34
|
+
# html
|
35
|
+
# web.use ScopedParams
|
36
|
+
web.use Rad::Html::Processors::PrepareFlash
|
37
|
+
|
38
|
+
# controller
|
39
|
+
web.use Rad::Controller::Processors::ControllerErrorHandling
|
40
|
+
|
41
|
+
# router
|
42
|
+
web.use Rad::Router::Processors::Router, :class, :method_name
|
43
|
+
|
44
|
+
# controller
|
45
|
+
web.use Rad::Controller::Processors::ControllerLogger
|
46
|
+
web.use Rad::Controller::Processors::ControllerCaller
|
47
|
+
|
48
|
+
web.build!
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
#
|
53
|
+
# RackAdapter
|
54
|
+
#
|
55
|
+
rad.http.initialize_rack do |builder|
|
56
|
+
# CommonLogger, ShowExceptions, Lint
|
57
|
+
builder.use Rack::Lint if rad.development?
|
58
|
+
|
59
|
+
# Static Files
|
60
|
+
if rad.http.static? and rad.http.public_path and rad.development?
|
61
|
+
filters = /^\/.*?\/static\/|^\/static\/|\/favicon/
|
62
|
+
builder.use Rad::Assets::StaticFiles, filters
|
63
|
+
end
|
64
|
+
|
65
|
+
# use Rack::Session::Cookie, key: 'rack.session', domain: 'foo.com', path: '/', expire_after: 2592000, secret: 'change_me'
|
66
|
+
builder.use Rack::Session::Cookie, rad.http.session.stringify_keys if rad.http.session
|
67
|
+
|
68
|
+
# builder.use Rack::CommonLogger
|
69
|
+
builder.use Rack::MethodOverride
|
70
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#
|
2
|
+
# Processor
|
3
|
+
#
|
4
|
+
class Rad::Processors::PrepareAutenticityToken < Rad::Conveyors::Processor
|
5
|
+
def call
|
6
|
+
if rad.http.session
|
7
|
+
request = workspace.request.must_be.defined
|
8
|
+
params = workspace.params.must_be.defined
|
9
|
+
|
10
|
+
token = request.session['authenticity_token']
|
11
|
+
|
12
|
+
if token.blank? and request.get? and
|
13
|
+
token = generate_authenticity_token
|
14
|
+
request.session['authenticity_token'] = token
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
next_processor.call
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
protected
|
23
|
+
def generate_authenticity_token
|
24
|
+
ActiveSupport::SecureRandom.base64(32)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
#
|
30
|
+
# Controller
|
31
|
+
#
|
32
|
+
Rad::Controller::Http.include Rad::Controller::ForgeryProtector
|
33
|
+
|
34
|
+
Rad::Controller::Http::ClassMethods.class_eval do
|
35
|
+
def protect_from_forgery options = {}
|
36
|
+
before :protect_from_forgery, options
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
#
|
42
|
+
# View
|
43
|
+
#
|
44
|
+
Rad::Html::FormHelper.class_eval do
|
45
|
+
def authenticity_token
|
46
|
+
@authenticity_token
|
47
|
+
end
|
48
|
+
|
49
|
+
alias_method :form_tag_without_at, :form_tag
|
50
|
+
def form_tag *args, &b
|
51
|
+
form_tag_without_at *args do
|
52
|
+
concat(hidden_field_tag('authenticity_token', authenticity_token) + "\n") if authenticity_token
|
53
|
+
b.call if b
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/rad_ext/spec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rake_ext'
|
2
|
+
require 'ruby_ext'
|
3
|
+
|
4
|
+
task :environment do
|
5
|
+
require 'rad'
|
6
|
+
rad.mode = ENV['m'] || ENV['mode'] || ENV['env'] || ENV['environment'] || :development
|
7
|
+
rad.runtime_path = File.expand_path '.'
|
8
|
+
|
9
|
+
require 'rad_ext'
|
10
|
+
rad.web
|
11
|
+
|
12
|
+
require 'rad_ext/utils/cli_helper'
|
13
|
+
|
14
|
+
Rad::CliHelper.use_runtime_path!
|
15
|
+
|
16
|
+
load "./init.rb"
|
17
|
+
rad.environment
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rad::CliHelper
|
2
|
+
RUNTIME_DIR = 'runtime'
|
3
|
+
|
4
|
+
class << self
|
5
|
+
inject logger: :logger
|
6
|
+
|
7
|
+
def run_console
|
8
|
+
prepare_running_environment
|
9
|
+
|
10
|
+
require 'irb'
|
11
|
+
IRB.start
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_server
|
15
|
+
app = prepare_running_environment
|
16
|
+
|
17
|
+
rad.http.run app, rad.http.host, rad.http.port
|
18
|
+
end
|
19
|
+
|
20
|
+
def use_runtime_path!
|
21
|
+
runtime_path = "./#{RUNTIME_DIR}"
|
22
|
+
Dir.chdir runtime_path if Dir.exist? runtime_path
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def prepare_running_environment
|
27
|
+
use_runtime_path!
|
28
|
+
|
29
|
+
require 'rack'
|
30
|
+
app, options = Rack::Builder.parse_file 'config.ru'
|
31
|
+
app
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/rad_ext.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rad'
|
2
|
+
require 'rad_ext/gems'
|
3
|
+
|
4
|
+
lib_dir = __FILE__.dirname
|
5
|
+
autoload_dir lib_dir
|
6
|
+
|
7
|
+
rad.must_not.include :web
|
8
|
+
rad.register :web, depends_on: [:html, :router, :controller, :http] do
|
9
|
+
require 'rad/core_web/_require'
|
10
|
+
require 'rad_ext/require'
|
11
|
+
|
12
|
+
load 'rad_ext/profiles/web_ext.rb'
|
13
|
+
load 'rad/profiles/mailer.rb'
|
14
|
+
|
15
|
+
rad.assets
|
16
|
+
|
17
|
+
true
|
18
|
+
end
|
data/readme.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Extensions for the Rad framework.
|
2
|
+
|
3
|
+
- face - tools for rapid interface creation.
|
4
|
+
- common_interface - common user interface for Rad.
|
5
|
+
- rad_ext - additions and extensions of the Rad.
|
6
|
+
- rad_js - JS plugin for the Rad.
|
7
|
+
- rad - Rapid Application Development tool built upon of the Rad.
|
8
|
+
|
9
|
+
Please go to specs for docs.
|
10
|
+
|
11
|
+
P.S.
|
12
|
+
All these components will be in standalone gems, but right now it's simpler for me to hold all of them in one place.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper_with_rad"
|
4
|
+
|
5
|
+
describe 'I18n' do
|
6
|
+
before :all do
|
7
|
+
rad.web
|
8
|
+
rad.reset :conveyors
|
9
|
+
|
10
|
+
I18n.load_path += Dir["#{spec_dir}/locales/*/*.{rb,yml}"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def t *args
|
14
|
+
I18n.t *args
|
15
|
+
end
|
16
|
+
|
17
|
+
it "basic" do
|
18
|
+
I18n.locale = 'en'
|
19
|
+
t(:name).should == "Name"
|
20
|
+
t(:name).is_a?(String).should be_true
|
21
|
+
|
22
|
+
I18n.locale = 'ru'
|
23
|
+
t(:name).should == "Имя"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "pluggable pluralization" do
|
27
|
+
I18n.locale = 'ru'
|
28
|
+
t(:comments_count, count: 1).should == "1 комментарий"
|
29
|
+
t(:comments_count, count: 2).should == "2 комментария"
|
30
|
+
t(:comments_count, count: 5).should == "5 комментариев"
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "spec_helper_with_rad"
|
2
|
+
|
3
|
+
describe "User Error" do
|
4
|
+
rad.web
|
5
|
+
rad.reset :conveyors
|
6
|
+
isolate :conveyors, before: :all
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
load 'spec_helper/web_profile.rb'
|
10
|
+
|
11
|
+
class ::SomeModel
|
12
|
+
def self.find! id
|
13
|
+
id.should == 'some id'
|
14
|
+
SomeModel.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
after :all do
|
19
|
+
remove_constants %w(SomeModel ControllerSpec)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "user error" do
|
23
|
+
class ::ControllerSpec
|
24
|
+
inherit Rad::Controller::Http
|
25
|
+
|
26
|
+
prepare_model SomeModel, id: :some_model, variable: 'some_model'
|
27
|
+
|
28
|
+
def action
|
29
|
+
@some_model.should_not == nil
|
30
|
+
render inline: 'ok'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
ccall(ControllerSpec, :action, some_model: 'some id').should == 'ok'
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "spec_helper_with_rad"
|
2
|
+
|
3
|
+
describe "User Error" do
|
4
|
+
rad.web
|
5
|
+
rad.reset :conveyors
|
6
|
+
|
7
|
+
isolate :conveyors, before: :all
|
8
|
+
|
9
|
+
before(:all){load 'spec_helper/web_profile.rb'}
|
10
|
+
|
11
|
+
after :all do
|
12
|
+
remove_constants %w(UserErrorSpec)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "user error" do
|
16
|
+
class ::UserErrorSpec
|
17
|
+
inherit Rad::Controller::Http
|
18
|
+
|
19
|
+
def call
|
20
|
+
raise_user_error "some error"
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def catch_user_error
|
25
|
+
begin
|
26
|
+
yield
|
27
|
+
rescue UserError => ue
|
28
|
+
render inline: "Catched #{ue.message}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
around :catch_user_error
|
32
|
+
end
|
33
|
+
|
34
|
+
ccall(UserErrorSpec, :call).should == "Catched some error"
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require "spec_helper_with_rad"
|
2
|
+
|
3
|
+
describe "Forgery protection" do
|
4
|
+
rad.web
|
5
|
+
rad.reset :conveyors
|
6
|
+
|
7
|
+
with_prepare_params
|
8
|
+
|
9
|
+
isolate :conveyors
|
10
|
+
|
11
|
+
before :all do
|
12
|
+
class ForgerySpecHelper < Rad::Conveyors::Processor
|
13
|
+
def call
|
14
|
+
block = workspace.check_forgery.before_request
|
15
|
+
block.call workspace if block
|
16
|
+
workspace.before_request_done = true
|
17
|
+
|
18
|
+
next_processor.call
|
19
|
+
|
20
|
+
block = workspace.check_forgery.after_request
|
21
|
+
block.call workspace if block
|
22
|
+
workspace.after_request_done = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
rad.conveyors.web do |web|
|
27
|
+
web.use Rad::Http::Processors::PrepareParams
|
28
|
+
web.use Rad::Http::Processors::EvaluateFormat
|
29
|
+
web.use ForgerySpecHelper
|
30
|
+
web.use Rad::Processors::PrepareAutenticityToken
|
31
|
+
web.use Rad::Controller::Processors::ControllerCaller
|
32
|
+
end
|
33
|
+
|
34
|
+
class ::AnRemote
|
35
|
+
inherit Rad::Controller::Http
|
36
|
+
|
37
|
+
protect_from_forgery only: :protected_method
|
38
|
+
|
39
|
+
def protected_method
|
40
|
+
render inline: 'protected result'
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_without_protection
|
44
|
+
render inline: 'result'
|
45
|
+
end
|
46
|
+
|
47
|
+
def dumb_method; end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
after :all do
|
52
|
+
rad.reset :conveyors
|
53
|
+
remove_constants %w(AnRemote ForgerySpecHelper)
|
54
|
+
end
|
55
|
+
|
56
|
+
before do
|
57
|
+
rad.http.stub(:session).and_return({'key' => 'session_id'})
|
58
|
+
end
|
59
|
+
|
60
|
+
def check_forgery opt
|
61
|
+
workspace = nil
|
62
|
+
|
63
|
+
result = rad.http.call(rad.http.mock_environment, check_forgery: opt.to_openobject) do |c|
|
64
|
+
c.call
|
65
|
+
workspace = rad[:workspace]
|
66
|
+
end
|
67
|
+
|
68
|
+
workspace.before_request_done.should be_true
|
69
|
+
workspace.after_request_done.should be_true
|
70
|
+
workspace
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should set :authenticity_token only for :get and 'html' request" do
|
74
|
+
check_forgery(
|
75
|
+
before_request: lambda{|workspace|
|
76
|
+
workspace.env['REQUEST_METHOD'] = 'GET'
|
77
|
+
workspace.env['CONTENT_TYPE'] = 'text/html'
|
78
|
+
workspace.class = AnRemote
|
79
|
+
workspace.method_name = :dumb_method
|
80
|
+
},
|
81
|
+
after_request: lambda{|workspace|
|
82
|
+
workspace.request.session['authenticity_token'].should_not be_blank
|
83
|
+
}
|
84
|
+
)
|
85
|
+
|
86
|
+
# post
|
87
|
+
check_forgery(
|
88
|
+
before_request: lambda{|workspace|
|
89
|
+
workspace.env['REQUEST_METHOD'] = 'POST'
|
90
|
+
workspace.env['CONTENT_TYPE'] = 'text/html'
|
91
|
+
workspace.class = AnRemote
|
92
|
+
workspace.method_name = :dumb_method
|
93
|
+
},
|
94
|
+
after_request: lambda{|workspace|
|
95
|
+
workspace.request.session['authenticity_token'].should be_blank
|
96
|
+
}
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should check any non :get request with browser's formats for :authenticity_token" do
|
101
|
+
lambda{
|
102
|
+
check_forgery(
|
103
|
+
before_request: lambda{|workspace|
|
104
|
+
workspace.env['REQUEST_METHOD'] = 'POST'
|
105
|
+
workspace.env['CONTENT_TYPE'] = 'text/html'
|
106
|
+
workspace.class = AnRemote
|
107
|
+
workspace.method_name = 'protected_method'
|
108
|
+
}
|
109
|
+
)
|
110
|
+
}.should raise_error(/invalid authenticity token/)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should pass request with correct authenticity_token" do
|
114
|
+
check_forgery(
|
115
|
+
before_request: lambda{|workspace|
|
116
|
+
workspace.env['REQUEST_METHOD'] = 'POST'
|
117
|
+
workspace.env['CONTENT_TYPE'] = 'text/html'
|
118
|
+
workspace.request.session['authenticity_token'] = 'secure token'
|
119
|
+
workspace.params['authenticity_token'] = 'secure token'
|
120
|
+
workspace.class = AnRemote
|
121
|
+
workspace.method_name = 'protected_method'
|
122
|
+
},
|
123
|
+
after_request: lambda{|workspace|
|
124
|
+
workspace.content.should == "protected result"
|
125
|
+
}
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should not check request with non-browser content type" do
|
130
|
+
check_forgery(
|
131
|
+
before_request: lambda{|workspace|
|
132
|
+
workspace.env['REQUEST_METHOD'] = 'POST'
|
133
|
+
workspace.env['CONTENT_TYPE'] = 'non-browser-format'
|
134
|
+
workspace.class = AnRemote
|
135
|
+
workspace.method_name = 'protected_method'
|
136
|
+
},
|
137
|
+
after_request: lambda{|workspace|
|
138
|
+
workspace.content.should == "protected result"
|
139
|
+
}
|
140
|
+
)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should not check request with non-browser format" do
|
144
|
+
check_forgery(
|
145
|
+
before_request: lambda{|workspace|
|
146
|
+
workspace.env['REQUEST_METHOD'] = 'POST'
|
147
|
+
workspace.env['CONTENT_TYPE'] = 'text/html'
|
148
|
+
workspace.params['format'] = 'json'
|
149
|
+
workspace.class = AnRemote
|
150
|
+
workspace.method_name = 'protected_method'
|
151
|
+
},
|
152
|
+
after_request: lambda{|workspace|
|
153
|
+
workspace.content.should == "protected result"
|
154
|
+
}
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should not protect non protected methods" do
|
159
|
+
check_forgery(
|
160
|
+
before_request: lambda{|workspace|
|
161
|
+
workspace.env['REQUEST_METHOD'] = 'POST'
|
162
|
+
workspace.env['CONTENT_TYPE'] = 'text/html'
|
163
|
+
workspace.class = AnRemote
|
164
|
+
workspace.method_name = 'method_without_protection'
|
165
|
+
},
|
166
|
+
after_request: lambda{|workspace|
|
167
|
+
workspace.content.should == "result"
|
168
|
+
}
|
169
|
+
)
|
170
|
+
end
|
171
|
+
|
172
|
+
# it "OUTDATED should use :session_authenticity_token from params (for flash support)" do
|
173
|
+
# check_forgery(
|
174
|
+
# before_request: lambda{|workspace|
|
175
|
+
# workspace.env['REQUEST_METHOD'] = 'POST'
|
176
|
+
# workspace.params.format = 'text/html'
|
177
|
+
# # workspace.params['session_authenticity_token'] = 'secure token'
|
178
|
+
# workspace.params['authenticity_token'] = 'secure token'
|
179
|
+
# workspace.class = AnRemote
|
180
|
+
# workspace.method_name = 'protected_method'
|
181
|
+
# },
|
182
|
+
# after_request: lambda{|workspace|
|
183
|
+
# workspace.content.should == "protected result"
|
184
|
+
# }
|
185
|
+
# )
|
186
|
+
# end
|
187
|
+
|
188
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rad_ext
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alexey Petrushin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-07-04 00:00:00.000000000 +04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: i18n
|
17
|
+
requirement: &2956510 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - =
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.5.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2956510
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rad_core
|
28
|
+
requirement: &2956210 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2956210
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rad_assets
|
39
|
+
requirement: &2955960 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *2955960
|
48
|
+
description:
|
49
|
+
email:
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- Rakefile
|
55
|
+
- readme.md
|
56
|
+
- lib/rad/configurators/abstract.rb
|
57
|
+
- lib/rad/configurators/runtime.rb
|
58
|
+
- lib/rad/configurators/web.rb
|
59
|
+
- lib/rad/controller/forgery_protector.rb
|
60
|
+
- lib/rad/processors/ajax_helper.rb
|
61
|
+
- lib/rad/processors/ensure_no_www.rb
|
62
|
+
- lib/rad_ext/configurators.rb
|
63
|
+
- lib/rad_ext/extensions/i18n/locales/ru/pluralization.rb
|
64
|
+
- lib/rad_ext/extensions/i18n.rb
|
65
|
+
- lib/rad_ext/extensions/prepare_model.rb
|
66
|
+
- lib/rad_ext/extensions/user_error.rb
|
67
|
+
- lib/rad_ext/extensions.rb
|
68
|
+
- lib/rad_ext/gems.rb
|
69
|
+
- lib/rad_ext/profiles/web_ext.rb
|
70
|
+
- lib/rad_ext/protect_from_forgery.rb
|
71
|
+
- lib/rad_ext/require.rb
|
72
|
+
- lib/rad_ext/spec.rb
|
73
|
+
- lib/rad_ext/tasks.rb
|
74
|
+
- lib/rad_ext/utils/cli_helper.rb
|
75
|
+
- lib/rad_ext.rb
|
76
|
+
- spec/extensions/i18n_spec/locales/en/general.yml
|
77
|
+
- spec/extensions/i18n_spec/locales/ru/general.yml
|
78
|
+
- spec/extensions/i18n_spec.rb
|
79
|
+
- spec/extensions/prepare_model_spec.rb
|
80
|
+
- spec/extensions/user_error_spec.rb
|
81
|
+
- spec/processors/protect_from_forgery_spec.rb
|
82
|
+
- spec/spec_helper/web_profile.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
- spec/spec_helper_with_rad.rb
|
85
|
+
has_rdoc: true
|
86
|
+
homepage: http://github.com/alexeypetrushin/rad_ext
|
87
|
+
licenses: []
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
requirements: []
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 1.5.1
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: Extensions for Rad Framework
|
110
|
+
test_files: []
|