rad_kit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +11 -0
- data/lib/components/kit.rb +16 -0
- data/lib/components/kit.yml +3 -0
- data/lib/components/models.rb +7 -0
- data/lib/kit/factories.rb +9 -0
- data/lib/kit/gems.rb +16 -0
- data/lib/kit/http_controller.rb +4 -0
- data/lib/kit/http_controller/authorized.rb +51 -0
- data/lib/kit/http_controller/localized.rb +13 -0
- data/lib/kit/kit.rb +29 -0
- data/lib/kit/models.rb +8 -0
- data/lib/kit/models/attachment_uploader.rb +15 -0
- data/lib/kit/models/attachments_uploader_helper.rb +79 -0
- data/lib/kit/models/authorized.rb +188 -0
- data/lib/kit/models/authorized_object.rb +167 -0
- data/lib/kit/models/default_permissions.yml +29 -0
- data/lib/kit/models/file_uploader.rb +26 -0
- data/lib/kit/models/micelaneous.rb +1 -0
- data/lib/kit/models/role.rb +88 -0
- data/lib/kit/models_after.rb +27 -0
- data/lib/kit/mongoid.rb +22 -0
- data/lib/kit/mongoid/rad_micelaneous.rb +36 -0
- data/lib/kit/mongoid/text_processor.rb +44 -0
- data/lib/kit/spec.rb +77 -0
- data/lib/kit/spec/items_controller_crud.rb +64 -0
- data/lib/kit/support.rb +14 -0
- data/lib/kit/support/string.rb +6 -0
- data/lib/kit/tasks.rb +18 -0
- data/lib/kit/text_utils.rb +43 -0
- data/lib/kit/text_utils/code_highlighter.rb +58 -0
- data/lib/kit/text_utils/custom_markdown.rb +90 -0
- data/lib/kit/text_utils/ensure_utf.rb +8 -0
- data/lib/kit/text_utils/github_flavoured_markdown.rb +32 -0
- data/lib/kit/text_utils/html_sanitizer.rb +89 -0
- data/lib/kit/text_utils/image_box.rb +35 -0
- data/lib/kit/text_utils/markup.rb +43 -0
- data/lib/kit/text_utils/processor.rb +25 -0
- data/lib/kit/text_utils/tag_shortcuts.rb +14 -0
- data/lib/kit/text_utils/truncate.rb +29 -0
- data/lib/kit/text_utils/truncator.rb +15 -0
- data/lib/kit/text_utils/urls.rb +13 -0
- data/readme.md +10 -0
- data/spec/controller/authorization_spec.rb +149 -0
- data/spec/controller/comments_spec.rb +54 -0
- data/spec/controller/items_spec.rb +45 -0
- data/spec/models/attachments_spec.rb +24 -0
- data/spec/models/attachments_spec/a.txt +1 -0
- data/spec/models/attachments_uploader_helper_spec.rb +108 -0
- data/spec/models/attachments_uploader_helper_spec/v1/a.txt +1 -0
- data/spec/models/attachments_uploader_helper_spec/v1/b.txt +1 -0
- data/spec/models/attachments_uploader_helper_spec/v2/a.txt +1 -0
- data/spec/models/authorization_spec.rb +77 -0
- data/spec/models/authorized_object_spec.rb +254 -0
- data/spec/models/comments_spec.rb +1 -0
- data/spec/models/item_spec.rb +51 -0
- data/spec/models/role_spec.rb +17 -0
- data/spec/models/tags_spec.rb +44 -0
- data/spec/models/uploader_spec.rb +37 -0
- data/spec/models/uploader_spec//321/204/320/260/320/270/314/206/320/273 /321/201 /320/277/321/200/320/276/320/261/320/265/320/273/320/260/320/274/320/270.txt" +1 -0
- data/spec/mongoid/basic_spec.rb +36 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/spec_helper/controller.rb +9 -0
- data/spec/spec_helper/factories.rb +24 -0
- data/spec/spec_helper/user.rb +17 -0
- data/spec/utils/text_utils_spec.rb +280 -0
- metadata +232 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
class Rad::TextUtils::ImageBox < Rad::TextUtils::Processor
|
2
|
+
# !![img] => [![img_thumb]][img]
|
3
|
+
def process markdown, env
|
4
|
+
img_urls = {}
|
5
|
+
markdown = markdown.gsub(/!!\[(.+?)\]/) do
|
6
|
+
img_key = $1 || ''
|
7
|
+
if url = markdown.scan(/\[#{img_key}\]:\s*([^\s]+)$/).first.try(:first)
|
8
|
+
unless url =~ /\.[^\.]+\.[^\.]+$/ # image.png
|
9
|
+
thumb_img_key = "#{img_key}_thumb"
|
10
|
+
|
11
|
+
# building url with thumb (foo.png => foo.thumb.png)
|
12
|
+
img_urls[thumb_img_key] = url.sub(/\.([^\.]+)$/){".thumb.#{$1}"}
|
13
|
+
|
14
|
+
"[![][#{thumb_img_key}]][#{img_key}]"
|
15
|
+
else # image.(icon|thumb|...).png
|
16
|
+
img_key_full = "#{img_key}_full"
|
17
|
+
|
18
|
+
# building url with thumb (foo.png => foo.thumb.png)
|
19
|
+
img_urls[img_key_full] = url.sub(/\.([^\.]+)\.([^\.]+)$/){".#{$2}"}
|
20
|
+
|
21
|
+
"[![][#{img_key}]][#{img_key_full}]"
|
22
|
+
end
|
23
|
+
else
|
24
|
+
$& || ''
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
unless img_urls.blank?
|
29
|
+
markdown << "\n"
|
30
|
+
markdown << img_urls.to_a.collect{|k, v| "[#{k}]: #{v}"}.join("\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
call_next markdown, env
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Rad::TextUtils::Markup < Rad::TextUtils::Processor
|
2
|
+
|
3
|
+
def initialize processor = nil
|
4
|
+
super
|
5
|
+
|
6
|
+
@markup = build_from(
|
7
|
+
Rad::TextUtils::EnsureUtf,
|
8
|
+
Rad::TextUtils::HtmlSanitizer,
|
9
|
+
|
10
|
+
Rad::TextUtils::CodeHighlighter,
|
11
|
+
|
12
|
+
Rad::TextUtils::CustomMarkdown,
|
13
|
+
|
14
|
+
Rad::TextUtils::Urls,
|
15
|
+
Rad::TextUtils::TagShortcuts
|
16
|
+
)
|
17
|
+
|
18
|
+
@html = build_from(
|
19
|
+
Rad::TextUtils::EnsureUtf,
|
20
|
+
Rad::TextUtils::HtmlSanitizer,
|
21
|
+
Rad::TextUtils::CodeHighlighter
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def process text, env
|
26
|
+
return text if text.blank?
|
27
|
+
|
28
|
+
if text =~ /\A\[html\]/i
|
29
|
+
text = text.sub(/\A\[html\][\s\n\r]*/i, '')
|
30
|
+
chain = @html
|
31
|
+
else
|
32
|
+
chain = @markup
|
33
|
+
end
|
34
|
+
|
35
|
+
text = chain.process text, env
|
36
|
+
|
37
|
+
unless text.encoding == Encoding::UTF_8
|
38
|
+
raise "something wrong happens, invalid encoding (#{text.encoding} instead of utf-8)!"
|
39
|
+
end
|
40
|
+
|
41
|
+
call_next text, env
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Rad::TextUtils::Processor
|
2
|
+
def initialize next_processor = nil
|
3
|
+
@next_processor = next_processor
|
4
|
+
end
|
5
|
+
|
6
|
+
protected
|
7
|
+
def call_next data, env
|
8
|
+
if @next_processor
|
9
|
+
@next_processor.process data, env
|
10
|
+
else
|
11
|
+
data
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_from *processors
|
16
|
+
processors.reverse.inject nil do |next_processor, meta|
|
17
|
+
klass, args = if meta.is_a? Array
|
18
|
+
[meta[0], meta[1..-1]]
|
19
|
+
else
|
20
|
+
[meta, []]
|
21
|
+
end
|
22
|
+
klass.new next_processor, *args
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Rad::TextUtils::TagShortcuts < Rad::TextUtils::Processor
|
2
|
+
TAGS = {
|
3
|
+
/\[clear\]/ => lambda{|match| "<div class='clear'></div>"},
|
4
|
+
/\[space\]/ => lambda{|match| "<div class='space'></div>"}
|
5
|
+
}
|
6
|
+
|
7
|
+
def process markdown, env
|
8
|
+
TAGS.each do |k, v|
|
9
|
+
markdown.gsub!(k, &v)
|
10
|
+
end
|
11
|
+
|
12
|
+
call_next markdown, env
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Rad::TextUtils::Truncate < Rad::TextUtils::Processor
|
2
|
+
def initialize processor, length
|
3
|
+
super(processor)
|
4
|
+
@length = length
|
5
|
+
end
|
6
|
+
|
7
|
+
def process str_or_html, env
|
8
|
+
str_or_html ||= ""
|
9
|
+
|
10
|
+
# Strip from HTML tags
|
11
|
+
str_or_html = str_or_html.gsub("<br", " <br").gsub("<p", " <p") # to preserve space in place of <> html elements
|
12
|
+
doc = Nokogiri::XML("<div class='root'>#{str_or_html}</div>")
|
13
|
+
str = doc.css('.root').first.content
|
14
|
+
|
15
|
+
str = str.gsub(/\s+/, ' ')
|
16
|
+
|
17
|
+
# Truncate with no broken words
|
18
|
+
str = if str.length >= @length
|
19
|
+
shortened = str[0, @length]
|
20
|
+
splitted = shortened.split(/\s/)
|
21
|
+
words = splitted.length
|
22
|
+
splitted[0, words-1].join(" ") + ' ...'
|
23
|
+
else
|
24
|
+
str
|
25
|
+
end
|
26
|
+
|
27
|
+
call_next str, env
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Rad::TextUtils::Truncator < Rad::TextUtils::Processor
|
2
|
+
def initialize processor, length
|
3
|
+
super(processor)
|
4
|
+
|
5
|
+
@chain = build_from(
|
6
|
+
Rad::TextUtils::EnsureUtf,
|
7
|
+
[Rad::TextUtils::Truncate, length]
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def process text_or_html, env
|
12
|
+
text_or_html = @chain.process text_or_html, env
|
13
|
+
call_next text_or_html, env
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Rad::TextUtils::Urls < Rad::TextUtils::Processor
|
2
|
+
# Creates <a> tags for all urls.
|
3
|
+
# IMPORTANT: make sure you've used #urls_to_images method first
|
4
|
+
# if you wanted all images urls to become <img> tags.
|
5
|
+
def process html, env
|
6
|
+
# becouse it finds only one url in such string "http://some_domain.com http://some_domain.com" we need to aply it twice
|
7
|
+
regexp, sub = /(\s|^|\A|\n|\t|\r)(http:\/\/.*?)([,.])?(\s|$|\n|\Z|\t|\r|<)/, '\1<a href="\2">\2</a>\3\4'
|
8
|
+
html = html.gsub regexp, sub
|
9
|
+
html.gsub regexp, sub
|
10
|
+
|
11
|
+
call_next html, env
|
12
|
+
end
|
13
|
+
end
|
data/readme.md
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Authorizations" do
|
4
|
+
with_controllers
|
5
|
+
|
6
|
+
isolate :config
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
@permissions = {
|
10
|
+
'call_controller_level' => [],
|
11
|
+
'call_business_logic_level' => [],
|
12
|
+
'call_with_owner' => []
|
13
|
+
}
|
14
|
+
|
15
|
+
class ::AuthorizationController
|
16
|
+
inherit Rad::Controller::Http
|
17
|
+
|
18
|
+
inherit Rad::Controller::Http::Authorized
|
19
|
+
|
20
|
+
require_permission :call_controller_level, only: :controller_level
|
21
|
+
|
22
|
+
def unprotected
|
23
|
+
render_ok
|
24
|
+
end
|
25
|
+
|
26
|
+
def controller_level
|
27
|
+
render_ok
|
28
|
+
end
|
29
|
+
|
30
|
+
def business_logic_level
|
31
|
+
require_permission :call_business_logic_level
|
32
|
+
render_ok
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_owner
|
36
|
+
require_permission :call_with_owner, owned_object
|
37
|
+
render_ok
|
38
|
+
end
|
39
|
+
|
40
|
+
def with_owner_controller_level
|
41
|
+
render_ok
|
42
|
+
end
|
43
|
+
require_permission :call_with_owner, only: :with_owner_controller_level do
|
44
|
+
owned_object
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
def owned_object
|
49
|
+
@@owned_object
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.owned_object= o
|
53
|
+
@@owned_object = o
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
rad.router.configure do |c|
|
58
|
+
c.resource :authorization_controllers, class_name: 'AuthorizationController'
|
59
|
+
end
|
60
|
+
|
61
|
+
I18n.locale = :en
|
62
|
+
end
|
63
|
+
|
64
|
+
after :all do
|
65
|
+
remove_constants %w(AuthorizationController)
|
66
|
+
end
|
67
|
+
|
68
|
+
before do
|
69
|
+
AuthorizationController.owned_object = nil
|
70
|
+
|
71
|
+
rad.config.permissions = @permissions
|
72
|
+
|
73
|
+
@user = Models::User.new
|
74
|
+
rad.user = @user
|
75
|
+
end
|
76
|
+
|
77
|
+
def raise_authorization_error
|
78
|
+
raise_error(UserError, /Access Denied/)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should allow to call unprotected methods" do
|
82
|
+
call('/authorization_controllers/unprotected')
|
83
|
+
response.body.should == "ok"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should allow declarative authorization at controller level" do
|
87
|
+
@user.stub!(:can?).and_return(false)
|
88
|
+
lambda{
|
89
|
+
call '/authorization_controllers/controller_level'
|
90
|
+
}.should raise_authorization_error
|
91
|
+
# response.should be_redirect
|
92
|
+
|
93
|
+
@user.stub!(:can?).and_return(true)
|
94
|
+
call '/authorization_controllers/controller_level'
|
95
|
+
response.body.should == "ok"
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should allow declarative authorization at business logic level" do
|
99
|
+
@user.stub!(:can?).and_return(false)
|
100
|
+
lambda{
|
101
|
+
call '/authorization_controllers/business_logic_level'
|
102
|
+
}.should raise_authorization_error
|
103
|
+
# response.should be_redirect
|
104
|
+
|
105
|
+
@user.stub!(:can?).and_return(true)
|
106
|
+
call '/authorization_controllers/business_logic_level'
|
107
|
+
response.body.should == "ok"
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should use owner if provided" do
|
111
|
+
@user.stub!(:can?){false}
|
112
|
+
lambda{
|
113
|
+
call '/authorization_controllers/with_owner'
|
114
|
+
}.should raise_authorization_error
|
115
|
+
# response.should be_redirect
|
116
|
+
|
117
|
+
|
118
|
+
o = Object.new
|
119
|
+
o.stub!(:owner_name){@user.name}
|
120
|
+
AuthorizationController.owned_object = o
|
121
|
+
|
122
|
+
@user.stub!(:can?) do |operation, object|
|
123
|
+
object and object.owner_name == @user.name
|
124
|
+
end
|
125
|
+
|
126
|
+
call '/authorization_controllers/with_owner'
|
127
|
+
response.body.should == "ok"
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should use owner if provided (action level)" do
|
131
|
+
@user.stub!(:can?){false}
|
132
|
+
lambda{
|
133
|
+
call '/authorization_controllers/with_owner_controller_level'
|
134
|
+
}.should raise_authorization_error
|
135
|
+
# response.should be_redirect
|
136
|
+
|
137
|
+
|
138
|
+
o = Object.new
|
139
|
+
o.stub!(:owner_name){@user.name}
|
140
|
+
AuthorizationController.owned_object = o
|
141
|
+
|
142
|
+
@user.stub!(:can?) do |operation, object|
|
143
|
+
object and object.owner_name == @user.name
|
144
|
+
end
|
145
|
+
|
146
|
+
call '/authorization_controllers/with_owner_controller_level'
|
147
|
+
response.body.should == "ok"
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Comments" do
|
4
|
+
with_controllers
|
5
|
+
set_controller Controllers::Comments
|
6
|
+
login_as :user
|
7
|
+
|
8
|
+
before do
|
9
|
+
@item = Factory.create :item
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_comment original_text = 'text'
|
13
|
+
Factory.create :comment, item: @item, owner: @user
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should display new dialog" do
|
17
|
+
call :new, format: 'js', item_id: @item.to_param
|
18
|
+
response.should be_ok
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should create Comment" do
|
22
|
+
comment_attributes = Factory.attributes_for :comment
|
23
|
+
pcall :create, format: 'js', item_id: @item.to_param, model: comment_attributes
|
24
|
+
response.should be_ok
|
25
|
+
|
26
|
+
Models::Comment.count.should == 1
|
27
|
+
comment = Models::Comment.first
|
28
|
+
comment.original_text.should == comment_attributes[:original_text]
|
29
|
+
comment.owner.should == @user
|
30
|
+
comment.item.should == @item
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should display edit dialog" do
|
34
|
+
comment = create_comment
|
35
|
+
call :edit, format: 'js', id: comment.to_param
|
36
|
+
response.should be_ok
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should update Comment" do
|
40
|
+
comment = create_comment
|
41
|
+
new_attributes = {original_text: 'new text'}
|
42
|
+
pcall :update, format: 'js', id: comment.to_param, model: new_attributes
|
43
|
+
response.should be_ok
|
44
|
+
|
45
|
+
comment.reload
|
46
|
+
comment.original_text.should == 'new text'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should delete Comment" do
|
50
|
+
comment = create_comment
|
51
|
+
pcall :destroy, format: 'js', id: comment.to_param
|
52
|
+
Models::Comment.count.should == 0
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Items" do
|
4
|
+
with_controllers
|
5
|
+
login_as :manager, name: 'auser'
|
6
|
+
|
7
|
+
describe "Basic" do
|
8
|
+
set_controller Controllers::Items
|
9
|
+
|
10
|
+
it "should update layout" do
|
11
|
+
@item = Factory.create :item
|
12
|
+
@item.layout.should == nil
|
13
|
+
|
14
|
+
pcall :layout, id: @item.to_param, value: 'home', format: 'js'
|
15
|
+
response.should be_ok
|
16
|
+
|
17
|
+
@item.reload
|
18
|
+
@item.layout.should == 'home'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'viewers, add_roles' do
|
22
|
+
@item = Factory.create :item
|
23
|
+
@item.viewers.should == %w{manager user:auser}
|
24
|
+
@item.owner_name.should == @user.name
|
25
|
+
|
26
|
+
pcall :viewers, id: @item.to_param, add_roles: 'user', format: 'js'
|
27
|
+
response.should be_ok
|
28
|
+
|
29
|
+
@item.reload
|
30
|
+
@item.viewers.should == %w{manager member user user:auser}
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should redirect to /items if no default_url specified" do
|
34
|
+
call :redirect
|
35
|
+
response.should redirect_to(rad.router.default_url)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should display :all"
|
39
|
+
# do
|
40
|
+
# call :all
|
41
|
+
# response.should be_ok
|
42
|
+
# end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|