rad_kit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/Rakefile +11 -0
  2. data/lib/components/kit.rb +16 -0
  3. data/lib/components/kit.yml +3 -0
  4. data/lib/components/models.rb +7 -0
  5. data/lib/kit/factories.rb +9 -0
  6. data/lib/kit/gems.rb +16 -0
  7. data/lib/kit/http_controller.rb +4 -0
  8. data/lib/kit/http_controller/authorized.rb +51 -0
  9. data/lib/kit/http_controller/localized.rb +13 -0
  10. data/lib/kit/kit.rb +29 -0
  11. data/lib/kit/models.rb +8 -0
  12. data/lib/kit/models/attachment_uploader.rb +15 -0
  13. data/lib/kit/models/attachments_uploader_helper.rb +79 -0
  14. data/lib/kit/models/authorized.rb +188 -0
  15. data/lib/kit/models/authorized_object.rb +167 -0
  16. data/lib/kit/models/default_permissions.yml +29 -0
  17. data/lib/kit/models/file_uploader.rb +26 -0
  18. data/lib/kit/models/micelaneous.rb +1 -0
  19. data/lib/kit/models/role.rb +88 -0
  20. data/lib/kit/models_after.rb +27 -0
  21. data/lib/kit/mongoid.rb +22 -0
  22. data/lib/kit/mongoid/rad_micelaneous.rb +36 -0
  23. data/lib/kit/mongoid/text_processor.rb +44 -0
  24. data/lib/kit/spec.rb +77 -0
  25. data/lib/kit/spec/items_controller_crud.rb +64 -0
  26. data/lib/kit/support.rb +14 -0
  27. data/lib/kit/support/string.rb +6 -0
  28. data/lib/kit/tasks.rb +18 -0
  29. data/lib/kit/text_utils.rb +43 -0
  30. data/lib/kit/text_utils/code_highlighter.rb +58 -0
  31. data/lib/kit/text_utils/custom_markdown.rb +90 -0
  32. data/lib/kit/text_utils/ensure_utf.rb +8 -0
  33. data/lib/kit/text_utils/github_flavoured_markdown.rb +32 -0
  34. data/lib/kit/text_utils/html_sanitizer.rb +89 -0
  35. data/lib/kit/text_utils/image_box.rb +35 -0
  36. data/lib/kit/text_utils/markup.rb +43 -0
  37. data/lib/kit/text_utils/processor.rb +25 -0
  38. data/lib/kit/text_utils/tag_shortcuts.rb +14 -0
  39. data/lib/kit/text_utils/truncate.rb +29 -0
  40. data/lib/kit/text_utils/truncator.rb +15 -0
  41. data/lib/kit/text_utils/urls.rb +13 -0
  42. data/readme.md +10 -0
  43. data/spec/controller/authorization_spec.rb +149 -0
  44. data/spec/controller/comments_spec.rb +54 -0
  45. data/spec/controller/items_spec.rb +45 -0
  46. data/spec/models/attachments_spec.rb +24 -0
  47. data/spec/models/attachments_spec/a.txt +1 -0
  48. data/spec/models/attachments_uploader_helper_spec.rb +108 -0
  49. data/spec/models/attachments_uploader_helper_spec/v1/a.txt +1 -0
  50. data/spec/models/attachments_uploader_helper_spec/v1/b.txt +1 -0
  51. data/spec/models/attachments_uploader_helper_spec/v2/a.txt +1 -0
  52. data/spec/models/authorization_spec.rb +77 -0
  53. data/spec/models/authorized_object_spec.rb +254 -0
  54. data/spec/models/comments_spec.rb +1 -0
  55. data/spec/models/item_spec.rb +51 -0
  56. data/spec/models/role_spec.rb +17 -0
  57. data/spec/models/tags_spec.rb +44 -0
  58. data/spec/models/uploader_spec.rb +37 -0
  59. 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
  60. data/spec/mongoid/basic_spec.rb +36 -0
  61. data/spec/spec_helper.rb +20 -0
  62. data/spec/spec_helper/controller.rb +9 -0
  63. data/spec/spec_helper/factories.rb +24 -0
  64. data/spec/spec_helper/user.rb +17 -0
  65. data/spec/utils/text_utils_spec.rb +280 -0
  66. metadata +232 -0
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake_ext'
2
+
3
+ project(
4
+ name: "kit",
5
+ official_name: 'rad_kit',
6
+ gem: true,
7
+ summary: "Rapid Application Development Kit for Rad Framework",
8
+
9
+ author: "Alexey Petrushin",
10
+ homepage: "http://github.com/alexeypetrushin/rad_kit"
11
+ )
@@ -0,0 +1,16 @@
1
+ rad.register :kit, depends_on: [:web, :assets, :common_interface, :models] do
2
+ require 'kit/kit'
3
+
4
+ rad.router.default_url = '/'
5
+ (rad.face.availiable_layouts[:default] ||= []) << :default
6
+
7
+ rad.configure :web, "#{__FILE__}/../../.." do |c|
8
+ c.routes
9
+ c.locales
10
+ c.template_paths 'app/views'
11
+ c.asset_paths 'app/static'
12
+ c.autoload_paths 'app'
13
+ end
14
+
15
+ Rad::Kit.new
16
+ end
@@ -0,0 +1,3 @@
1
+ fs_prefix: '/fs'
2
+ fs_type: :file
3
+ tags_count: 40
@@ -0,0 +1,7 @@
1
+ rad.register :models do
2
+ require 'kit/models'
3
+ Rad::Models.new
4
+ end
5
+ rad.after :models do
6
+ require 'kit/models_after'
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'factory_girl'
2
+
3
+ Factory.define :item, class: 'Models::Item' do |o|
4
+ o.sequence(:name){|i| "item_#{i}"}
5
+ end
6
+
7
+ Factory.define :comment, class: 'Models::Comment', parent: :item do |c|
8
+ c.original_text "Some text"
9
+ end
data/lib/kit/gems.rb ADDED
@@ -0,0 +1,16 @@
1
+ # core gems
2
+ gem 'bluecloth', '2.0.9'
3
+ gem 'sanitize', '1.2.1'
4
+ gem 'stringex', '1.2.0'
5
+ gem 'state_machine', '0.10.4'
6
+ # gem 'paperclip', '2.3.1.1'
7
+ gem 'factory_girl', '1.3.3'
8
+ gem 'coderay', '0.9.7'
9
+
10
+ if respond_to? :fake_gem
11
+ fake_gem 'rad_core'
12
+ fake_gem 'rad_ext'
13
+ fake_gem 'rad_common_interface'
14
+ fake_gem 'rad_assets'
15
+ fake_gem 'mongoid_misc'
16
+ end
@@ -0,0 +1,4 @@
1
+ %w(
2
+ authorized
3
+ localized
4
+ ).each{|n| require "kit/http_controller/#{n}"}
@@ -0,0 +1,51 @@
1
+ module Rad::Controller::Http::Authorized
2
+ inherited do
3
+ helper_method :can?, :owner?
4
+ end
5
+
6
+ module ClassMethods
7
+ def require_permission operation, *args, &object_proc
8
+ operation = operation.must_be.a(String, Symbol).to_s
9
+
10
+ options = args.extract_options!
11
+ # object_proc = args.size > 0 ? args.first : lambda{}
12
+ object_proc ||= lambda{|controller|}
13
+
14
+ method = "require_permission_#{operation}"
15
+ define_method method do
16
+ require_permission operation, instance_eval(&object_proc)
17
+ end
18
+ before method, options
19
+ end
20
+ end
21
+
22
+ protected
23
+ def can? *args
24
+ rad.user.can? *args
25
+ end
26
+
27
+ def owner? *args
28
+ rad.user.owner? *args
29
+ end
30
+
31
+ def login_required
32
+ access_denied! unless rad.user.registered?
33
+ end
34
+
35
+ def login_not_required
36
+ raise_user_error t(:login_not_required) if rad.user.registered?
37
+ end
38
+
39
+ def require_permission operation, object = nil
40
+ operation = operation.must_be.a(String, Symbol).to_s
41
+
42
+ unless rad.user.can? operation, object
43
+ rad.logger.warn "RAD access denied, #{rad.user.name} hasn't rights to #{operation}!"
44
+ access_denied!
45
+ end
46
+ end
47
+
48
+ def access_denied!
49
+ raise_user_error t(:access_denied)
50
+ end
51
+ end
@@ -0,0 +1,13 @@
1
+ module Rad::Controller::Http::Localized
2
+ inherited do
3
+ before :prepare_locale
4
+ end
5
+
6
+ protected
7
+ def prepare_locale
8
+ language = rad.environment.language
9
+ I18n.locale = params.l || language
10
+ # Delete l from params if language is the same as default
11
+ params.delete :l if params.l == language
12
+ end
13
+ end
data/lib/kit/kit.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'kit/support'
2
+
3
+ # Configs
4
+ class Rad::Kit
5
+ attr_accessor :default_item, :fs_prefix, :fs_type, :fs_cache_path, :fs_path, :tags_count
6
+ require_attr :default_item, :tags_count, :fs_prefix, :fs_type, :fs_path, :fs_cache_path
7
+ def items; @items ||= [] end
8
+ end
9
+
10
+ rad.router.class.class_eval do
11
+ attr_accessor :default_url
12
+ require_attr :default_url
13
+ end
14
+
15
+ rad.config.class.class_eval do
16
+ def custom_roles; @custom_roles ||= [] end
17
+ def permissions; @permissions ||= {} end
18
+ def default_viewers; @default_viewers ||= [] end
19
+ end
20
+
21
+
22
+ # Kit
23
+ #
24
+ # TODO3 move :text_utils to standalone text_utils gem
25
+ %w(
26
+ support
27
+ text_utils
28
+ http_controller
29
+ ).each{|f| require "kit/#{f}"}
data/lib/kit/models.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'kit/support'
2
+ require 'kit/mongoid'
3
+ require 'state_machine'
4
+ StateMachine::Integrations::Mongoid.defaults[:action] = nil
5
+
6
+ class Rad::Models
7
+ attr_accessor :config
8
+ end
@@ -0,0 +1,15 @@
1
+ class Models::AttachmentUploader < Models::FileUploader
2
+ rad.extension :attachment_file_path, self do
3
+ define_method :file_path do
4
+ "#{rad.kit.fs_prefix}/#{model.item.id}"
5
+ end
6
+ end
7
+
8
+ version :icon do
9
+ process resize_to_fit: [50, 50]
10
+ end
11
+
12
+ version :thumb do
13
+ process resize_to_fit: [150, 150]
14
+ end
15
+ end
@@ -0,0 +1,79 @@
1
+ module Mongoid::AttachmentsUploaderHelper
2
+ extend ActiveSupport::Concern
3
+
4
+ class FileHelper
5
+ attr_reader :object
6
+ def initialize object
7
+ @object = object
8
+ end
9
+
10
+ def file?
11
+ object.is_a?(Hash) or object.is_a?(IO)
12
+ end
13
+
14
+ def name
15
+ if object.is_a?(Hash)
16
+ object['filename'] || object[:filename]
17
+ elsif object.is_a?(IO)
18
+ File.basename(object.path)
19
+ else
20
+ object
21
+ end
22
+ end
23
+ end
24
+
25
+ def get_attachments association_name, field_name
26
+ send(association_name).
27
+ sort{|a, b| a.send(field_name).name <=> b.send(field_name).name}.
28
+ collect{|o| {name: o.send(field_name).name, url: o.send(field_name).url}.to_openobject}
29
+ end
30
+
31
+ def set_attachments association_name, field_name, values
32
+ association = send(association_name)
33
+ existing_names = association.collect{|o| o.send(field_name).name}.sort
34
+
35
+ add = values.select do |o|
36
+ h = FileHelper.new o
37
+ h.file? and !existing_names.include?(h.name)
38
+ end
39
+ update = values.select do |o|
40
+ h = FileHelper.new o
41
+ h.file? and existing_names.include?(h.name)
42
+ end
43
+ remove = association.select do |model|
44
+ values.none? do |o|
45
+ h = FileHelper.new o
46
+ model.send(field_name).name == h.name
47
+ end
48
+ end
49
+
50
+ add.each{|file| association.build field_name => file}
51
+ update.each do |file|
52
+ h = FileHelper.new file
53
+ association.each do |model|
54
+ if model.send(field_name).name == h.name
55
+ model.send "#{field_name}=", file
56
+ break
57
+ end
58
+ end
59
+ end
60
+ remove.each do |model|
61
+ association.where(_id: model.id).destroy_all
62
+ end
63
+ end
64
+
65
+ module ClassMethods
66
+ def mount_attachments_uploader association_name, field_name
67
+ define_method "#{association_name}_as_attachments" do
68
+ get_attachments association_name, field_name
69
+ end
70
+
71
+ define_method "#{association_name}_as_attachments=" do |values|
72
+ set_attachments association_name, field_name, values
73
+ end
74
+
75
+ # we can't allow to destroy model with changed attachments because it's too complicated to support this case.
76
+ before_destroy{|o| raise "Can't destroy item with changed attachments!" if o.changes.include? association_name}
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,188 @@
1
+ module Mongoid::Authorized
2
+ extend ActiveSupport::Concern
3
+
4
+ class << self
5
+ attr_writer :custom_permissions
6
+ def custom_permissions; @custom_permissions ||= {} end
7
+ def permissions
8
+ @default_permissions ||= YAML.load_file("#{__FILE__.dirname}/default_permissions.yml").freeze
9
+ @default_permissions.merge(rad.config.permissions).merge(custom_permissions)
10
+ end
11
+ end
12
+
13
+ included do
14
+ validate :validate_anonymous
15
+ validates_exclusion_of :name, in: Role::PRESERVED_USER_NAMES, if: lambda{|u| u.new_record?}
16
+
17
+ rad.extension :model_authorization, self do
18
+ field :roles, type: Array, protected: true, default: []
19
+ alias_method :mm_roles, :roles
20
+ alias_method :mm_roles=, :roles=
21
+
22
+ field :admin, type: Boolean, protected: true, default: false
23
+ end
24
+
25
+ alias_method :roles, :handy_roles
26
+ end
27
+
28
+ module ClassMethods
29
+ def anonymous
30
+ Models::User.by_name('anonymous') || raise("You probably don't create Anonymous User!")
31
+ end
32
+ end
33
+
34
+ #
35
+ # Owner
36
+ #
37
+ def owner_name; anonymous? ? nil : name end
38
+
39
+ def owner? object
40
+ !object.blank? and !name.blank? and !anonymous? and object.respond_to(:owner_name) == self.name
41
+ end
42
+
43
+ #
44
+ # Roles
45
+ #
46
+ def anonymous?
47
+ name == 'anonymous'
48
+ end
49
+
50
+ def registered?
51
+ !anonymous?
52
+ end
53
+
54
+ def add_role role
55
+ role = role.to_s
56
+ unless roles.include? role
57
+ if role == 'admin'
58
+ self.admin = true
59
+ else
60
+ self.mm_roles -= Role.denormalize_to_lower_roles [role]
61
+ self.mm_roles += [role]
62
+ end
63
+ clear_cache
64
+ end
65
+ roles
66
+ end
67
+
68
+ def remove_role role
69
+ role = role.to_s
70
+ if roles.include? role
71
+ if role == 'admin'
72
+ self.admin = false
73
+ else
74
+ self.mm_roles -= Role.denormalize_to_higher_roles [role]
75
+ end
76
+ clear_cache
77
+ end
78
+ roles
79
+ end
80
+
81
+ def handy_roles
82
+ unless roles = cache[:roles]
83
+ roles = if self.mm_roles.empty?
84
+ ['user']
85
+ else
86
+ Role.denormalize_to_lower_roles self.mm_roles
87
+ end
88
+ if anonymous?
89
+ roles << 'anonymous'
90
+ else
91
+ roles << 'registered'
92
+ end
93
+ roles << "user:#{name}" unless name.blank?
94
+ if admin
95
+ roles << 'admin'
96
+ %w(manager member).each{|r| roles << r unless roles.include? r}
97
+ end
98
+
99
+ roles.must_be == roles.uniq
100
+
101
+ roles = HandyRoles.new roles.sort
102
+ cache[:roles] = roles
103
+ end
104
+ roles
105
+ end
106
+
107
+ def major_roles
108
+ cache[:major_roles] ||= Role.major_roles roles
109
+ end
110
+
111
+ def has_role? role
112
+ roles.include? role
113
+ end
114
+
115
+
116
+ #
117
+ # can?
118
+ #
119
+ def can? operation, object = nil
120
+ operation = operation.to_s
121
+
122
+ return true if has_role?(:admin)
123
+
124
+ custom_method = "able_#{operation}?"
125
+ return object.send custom_method, self if object.respond_to? custom_method
126
+
127
+ (
128
+ effective_permissions[operation] or
129
+ (owner?(object) and effective_permissions_as_owner[operation])
130
+ )
131
+ end
132
+
133
+ def can_view? object
134
+ can? :view, object
135
+ end
136
+
137
+
138
+ #
139
+ # Effective Permissions
140
+ #
141
+ def effective_permissions
142
+ unless ep = cache[:effective_permissions]
143
+ ep = calculate_effective_roles_for roles
144
+ cache[:effective_permissions] = ep
145
+ end
146
+ ep
147
+ end
148
+
149
+ def effective_permissions_as_owner
150
+ unless epo = cache[:effective_permissions_as_owner]
151
+ epo = calculate_effective_roles_for ['owner']
152
+ cache[:effective_permissions_as_owner] = epo
153
+ end
154
+ epo
155
+ end
156
+
157
+ protected
158
+ def calculate_effective_roles_for roles
159
+ effective_permissions = {}
160
+ permissions = ::Mongoid::Authorized.permissions
161
+ # permissions = rad.config.permissions(DEFAULT_PERMISSIONS).to_h #.to_h(to_s: true)
162
+ permissions.each do |operation, allowed_roles|
163
+ operation = operation.to_s
164
+ effective_permissions[operation.to_s] = roles.any?{|role| allowed_roles.include? role}
165
+ end
166
+ effective_permissions
167
+ end
168
+
169
+ def validate_anonymous
170
+ errors.add :base, "Anonymous can't have any roles!" if anonymous? and !self.mm_roles.blank?
171
+ end
172
+
173
+ class HandyRoles < Array
174
+ def include? role
175
+ super role.to_s
176
+ end
177
+ alias_method :has?, :include?
178
+
179
+ protected
180
+ def method_missing m, *args, &block
181
+ m = m.to_s
182
+ super unless m.last == '?'
183
+
184
+ self.include? m[0..-2]
185
+ end
186
+ end
187
+
188
+ end