rad_kit 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.
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