rapid 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/Rakefile +66 -0
  2. data/lib/rad/http_controller/acts_as/authenticated.rb +131 -0
  3. data/lib/rad/http_controller/acts_as/authenticated_master_domain.rb +119 -0
  4. data/lib/rad/http_controller/acts_as/authorized.rb +83 -0
  5. data/lib/rad/http_controller/acts_as/localized.rb +27 -0
  6. data/lib/rad/http_controller/acts_as/multitenant.rb +53 -0
  7. data/lib/rad/http_controller/helpers/service_mix_helper.rb +50 -0
  8. data/lib/rad/http_controller.rb +15 -0
  9. data/lib/rad/lib/text_utils.rb +334 -0
  10. data/lib/rad/locales/en.yml +80 -0
  11. data/lib/rad/locales/ru.yml +83 -0
  12. data/lib/rad/locales.rb +2 -0
  13. data/lib/rad/models/account.rb +88 -0
  14. data/lib/rad/models/default_permissions.yml +26 -0
  15. data/lib/rad/models/micelaneous.rb +1 -0
  16. data/lib/rad/models/role.rb +88 -0
  17. data/lib/rad/models/secure_token.rb +33 -0
  18. data/lib/rad/models/space.rb +184 -0
  19. data/lib/rad/models/user.rb +158 -0
  20. data/lib/rad/models.rb +41 -0
  21. data/lib/rad/mongo_mapper/acts_as/authenticated_by_open_id.rb +29 -0
  22. data/lib/rad/mongo_mapper/acts_as/authenticated_by_password.rb +120 -0
  23. data/lib/rad/mongo_mapper/acts_as/authorized.rb +197 -0
  24. data/lib/rad/mongo_mapper/acts_as/authorized_object.rb +171 -0
  25. data/lib/rad/mongo_mapper/multitenant.rb +34 -0
  26. data/lib/rad/mongo_mapper/rad_micelaneous.rb +43 -0
  27. data/lib/rad/mongo_mapper/space_keys.rb +62 -0
  28. data/lib/rad/mongo_mapper/text_processor.rb +47 -0
  29. data/lib/rad/mongo_mapper.rb +20 -0
  30. data/lib/rad/paperclip/callbacks.rb +40 -0
  31. data/lib/rad/paperclip/extensions.rb +64 -0
  32. data/lib/rad/paperclip/integration.rb +165 -0
  33. data/lib/rad/paperclip/mime.rb +5 -0
  34. data/lib/rad/paperclip/validations.rb +64 -0
  35. data/lib/rad/paperclip.rb +11 -0
  36. data/lib/rad/spec/controller.rb +51 -0
  37. data/lib/rad/spec/model/factories.rb +65 -0
  38. data/lib/rad/spec/model.rb +85 -0
  39. data/lib/rad/spec/rem_helper.rb +145 -0
  40. data/lib/rad/spec.rb +4 -0
  41. data/lib/rad/tasks/backup.rake +64 -0
  42. data/lib/rad/tasks/initialize.rake +35 -0
  43. data/lib/rad.rb +32 -0
  44. data/readme.md +3 -0
  45. data/spec/controller/authorization_spec.rb +146 -0
  46. data/spec/controller/helper.rb +14 -0
  47. data/spec/lib/helper.rb +7 -0
  48. data/spec/lib/text_utils_spec.rb +238 -0
  49. data/spec/models/authorization_spec.rb +93 -0
  50. data/spec/models/authorized_object_spec.rb +258 -0
  51. data/spec/models/file_audit_spec/100.txt +1 -0
  52. data/spec/models/file_audit_spec/302.txt +3 -0
  53. data/spec/models/file_audit_spec.rb +168 -0
  54. data/spec/models/helper.rb +11 -0
  55. data/spec/models/space_key_spec.rb +68 -0
  56. data/spec/models/user_spec.rb +80 -0
  57. data/spec/mongo_mapper/basic_spec.rb +41 -0
  58. data/spec/mongo_mapper/helper.rb +10 -0
  59. data/spec/spec.opts +4 -0
  60. metadata +138 -0
@@ -0,0 +1,197 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Authorized
4
+ ROLES = %w{admin manager member}
5
+
6
+ module ClassMethods
7
+ def acts_as_authorized
8
+ key :global_admin, Boolean, :protected => true
9
+ key :admin_of_accounts, Array, :protected => true
10
+ # has_many :roles_containers, :class_name => 'RolesContainer', :protected => true
11
+ space_key :space_roles, Array
12
+
13
+ validate :validate_anonymous
14
+ validates_exclusion_of :name, :within => Role::PRESERVED_USER_NAMES, :if => lambda{|u| u.new_record?}
15
+ end
16
+
17
+ def anonymous
18
+ User.find_by_name 'anonymous'
19
+ end
20
+ end
21
+
22
+ module InstanceMethods
23
+ #
24
+ # Owner
25
+ #
26
+ def owner_name; anonymous? ? nil : name end
27
+
28
+ def owner? object
29
+ # object.should! :respond_to?, :owner_name
30
+ !object.blank? and !name.blank? and object.respond_to(:owner_name) == self.name
31
+ end
32
+
33
+ #
34
+ # Roles
35
+ #
36
+ def anonymous?
37
+ name == 'anonymous'
38
+ end
39
+
40
+ def registered?
41
+ !anonymous?
42
+ end
43
+
44
+ def add_role role
45
+ role = role.to_s
46
+ Rad.multitenant_mode?.must_be.true
47
+
48
+ if role == 'admin'
49
+ account_id = Account.current.id
50
+ admin_of_accounts << account_id unless admin_of_accounts.include? account_id
51
+ else
52
+ roles = Role.denormalize_to_lower_roles [role]
53
+ self.space_roles = space_roles + [role] unless space_roles.include?(role)
54
+ end
55
+ clear_cache
56
+ roles
57
+ end
58
+
59
+ def remove_role role
60
+ role = role.to_s
61
+ Rad.multitenant_mode?.must_be.true
62
+
63
+ if role == 'admin'
64
+ admin_of_accounts.delete Account.current.id
65
+ else
66
+ roles = Role.denormalize_to_higher_roles [role]
67
+ self.space_roles = space_roles - roles
68
+ end
69
+ clear_cache
70
+ roles
71
+ end
72
+
73
+ def roles
74
+ unless roles = cache[:roles]
75
+ if Rad.multitenant_mode?
76
+ roles = space_roles.clone
77
+ roles << 'admin' if admin_of_accounts.include? Account.current.id
78
+ else
79
+ roles = []
80
+ end
81
+
82
+ roles << 'user'
83
+
84
+ if anonymous?
85
+ roles << 'anonymous'
86
+ else
87
+ roles << 'registered'
88
+ end
89
+
90
+ roles << "user:#{name}" unless name.blank?
91
+
92
+ roles << 'admin' if global_admin and !roles.include?('admin')
93
+
94
+ roles << 'manager' if roles.include?('admin') and !roles.include?('manager')
95
+
96
+ roles = Role.denormalize_to_lower_roles(roles)
97
+
98
+ roles = HandyRoles.new roles
99
+
100
+ cache[:roles] = roles
101
+ end
102
+ roles
103
+ end
104
+
105
+ def major_roles
106
+ cache[:major_roles] ||= Role.major_roles roles
107
+ end
108
+
109
+ def has_role? role
110
+ roles.include? role
111
+ end
112
+
113
+
114
+ #
115
+ # can?
116
+ #
117
+ def can? operation, object = nil
118
+ operation = operation.to_s
119
+
120
+ return true if has_role?(:admin)
121
+
122
+ custom_method = "able_#{operation}?"
123
+ return object.send custom_method, self if object.respond_to? custom_method
124
+
125
+ effective_space_permissions[operation] or (
126
+ owner?(object) and effective_space_permissions_as_owner[operation]
127
+ )
128
+ end
129
+
130
+ def can_view? object
131
+ can? :view, object
132
+ end
133
+
134
+
135
+ #
136
+ # Effective Permissions
137
+ #
138
+ def effective_space_permissions
139
+ unless effective_space_permissions = cache[:effective_space_permissions]
140
+ effective_space_permissions = calculate_effective_roles_for roles
141
+ cache[:effective_space_permissions] = effective_space_permissions
142
+ end
143
+ effective_space_permissions
144
+ end
145
+
146
+ def effective_space_permissions_as_owner
147
+ unless effective_space_permissions_as_owner = cache[:effective_space_permissions_as_owner]
148
+ effective_space_permissions_as_owner = calculate_effective_roles_for ['owner']
149
+ cache[:effective_space_permissions_as_owner] = effective_space_permissions_as_owner
150
+ end
151
+ effective_space_permissions_as_owner
152
+ end
153
+
154
+ protected
155
+ # def effective_space_roles
156
+ # ::Rails.should_be! :multitenant_mode
157
+ # unless roles = cache[:effective_space_roles]
158
+ # roles = space_roles.clone
159
+ # roles << 'admin' if admin_of_accounts.include? Account.current.id
160
+ # cache[:effective_space_roles] = roles
161
+ # end
162
+ # roles
163
+ # end
164
+
165
+ def validate_anonymous
166
+ if anonymous? and (global_admin or !space_roles.blank?)
167
+ errors.add :base, "Anonymous can't be member or manager!"
168
+ end
169
+ end
170
+
171
+ class HandyRoles < Array
172
+ def include? role
173
+ super role.to_s
174
+ end
175
+ alias_method :has?, :include?
176
+
177
+ def method_missing m, *args, &block
178
+ m = m.to_s
179
+ super unless m.last == '?'
180
+
181
+ self.include? m[0..-2]
182
+ end
183
+ end
184
+
185
+ def calculate_effective_roles_for roles
186
+ permissions = Rad.multitenant_mode? ? Space.current.permissions : Space.permissions
187
+ effective_space_permissions = {}
188
+ permissions.each do |operation, allowed_roles|
189
+ effective_space_permissions[operation] = roles.any?{|role| allowed_roles.include? role}
190
+ end
191
+ effective_space_permissions
192
+ end
193
+ end
194
+
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,171 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module AuthorizedObject
4
+
5
+ module ClassMethods
6
+ def acts_as_authorized_object
7
+ key :owner_name, String, :default => lambda{User.current? ? User.current.name : nil}, :protected => true
8
+ key :collaborators, Array, :protected => true
9
+ # Contains the role and all upper roles. So complex becouse we need it in indexes.
10
+ key :viewers, Array, :default => lambda{User.current? ? ["user:#{User.current.name}", 'manager'].sort : ['manager']}, :protected => true
11
+
12
+ validates_presence_of :owner_name
13
+ validate :validate_viewers
14
+ validate :validate_collaborators
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ #
20
+ # Owner
21
+ #
22
+ def owner
23
+ return nil if owner_name.blank?
24
+ cache[:owner] ||= User.find_by_name! owner_name
25
+ end
26
+
27
+ def owner= user
28
+ user.must_be.an User
29
+ cache[:owner] = user
30
+ self.owner_name = user.name
31
+ user
32
+ end
33
+
34
+ # TODO2 update it later, MM uses public API to unmarshal object
35
+ # http://groups.google.com/group/mongomapper/browse_thread/thread/ab34457e0ba9c472#
36
+ def owner_name= name
37
+ owner_role = "user:#{name}"
38
+ old_owner_role = "user:#{owner_name}"
39
+
40
+ unless viewers.include? owner_role
41
+ viewers.delete old_owner_role
42
+ viewers << owner_role
43
+ viewers.sort!
44
+ end
45
+
46
+ # write_attribute :owner_name, name
47
+ super name
48
+ clear_cache
49
+ owner_name
50
+ end
51
+
52
+ #
53
+ # Viewers and Collaborators
54
+ #
55
+ def add_viewer role
56
+ role = role.to_s
57
+ should_be_valid_user_input_role role
58
+
59
+ return if viewers.include? role
60
+
61
+ roles = viewers
62
+ roles << role
63
+ roles = Role.denormalize_to_higher_roles roles
64
+ roles << 'manager' unless roles.include? 'manager'
65
+ self.viewers = roles.sort
66
+ viewers
67
+ end
68
+
69
+ def remove_viewer role
70
+ role = role.to_s
71
+ should_be_valid_user_input_role role
72
+
73
+ return unless viewers.include? role
74
+
75
+ roles = viewers
76
+ Role.denormalize_to_higher_roles([role]).each do |r|
77
+ roles.delete r
78
+ end
79
+ roles << 'manager' unless roles.include? 'manager'
80
+ self.viewers = roles.sort
81
+
82
+ remove_collaborator role
83
+
84
+ viewers
85
+ end
86
+
87
+ def minor_viewers
88
+ unless minor_viewers = cache[:minor_viewers]
89
+ viewers = self.viewers.clone
90
+ viewers.delete 'manager'
91
+ minor_viewers = Role.minor_roles viewers
92
+ cache[:minor_viewers] = minor_viewers
93
+ end
94
+ minor_viewers
95
+ end
96
+
97
+ def add_collaborator role
98
+ role = role.to_s
99
+ should_be_valid_user_input_role role
100
+ return if collaborators.include? role
101
+ collaborators = self.collaborators.clone
102
+ collaborators << role
103
+ self.collaborators = collaborators
104
+
105
+ add_viewer role
106
+
107
+ collaborators
108
+ end
109
+
110
+ def remove_collaborator role
111
+ role = role.to_s
112
+ should_be_valid_user_input_role role
113
+ collaborators.delete role
114
+ collaborators
115
+ end
116
+
117
+ def normalized_collaborators
118
+ unless normalized_collaborators = cache[:normalized_collaborators]
119
+ normalized_collaborators = Role.denormalize_to_higher_roles collaborators
120
+ normalized_collaborators << "user:#{owner_name}"
121
+ normalized_collaborators.sort!
122
+ cache[:normalized_collaborators] = normalized_collaborators
123
+ end
124
+ normalized_collaborators
125
+ end
126
+
127
+
128
+ #
129
+ # Special Permissions
130
+ #
131
+ def able_view? user
132
+ user.roles.any?{|role| viewers.include? role}
133
+ end
134
+
135
+ def able_update? user
136
+ user.roles.any?{|role| normalized_collaborators.include? role}
137
+ end
138
+
139
+ protected
140
+ def should_be_valid_user_input_role role
141
+ # ::Rails.should_be! :multitenant_mode
142
+ # (Role::ORDERED_ROLES + Space.current.custom_roles).should! :include, role.to_s
143
+ role.must_not == 'manager'
144
+ role.must_not == "user:#{owner_name}"
145
+ end
146
+
147
+ def validate_viewers
148
+ viewers.must == viewers.uniq
149
+
150
+ viewers.must.include 'manager' # always
151
+ viewers.must.include "user:#{owner_name}"
152
+ end
153
+
154
+ def validate_collaborators
155
+ collaborators.must_not.include "user:#{owner_name}"
156
+ end
157
+ end
158
+
159
+ # module ClassMethods
160
+ # def can? operation, user
161
+ # method = "can_#{operation}?"
162
+ # if respond_to? method
163
+ # send method, user
164
+ # else
165
+ # user.effective_space_permissions[operation]
166
+ # end
167
+ # end
168
+ # end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,34 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Multitenant
4
+
5
+ module ClassMethods
6
+ def connect_to_global_database
7
+ use_database :global
8
+ end
9
+
10
+ def belongs_to_space!
11
+ keys.symbolize_keys.must_not.include :account_id
12
+
13
+ key :account_id, ObjectId, :default => lambda{Account.current? ? Account.current.id : nil}, :protected => true
14
+ belongs_to :account
15
+
16
+ key :space_id, ObjectId, :default => lambda{Space.current? ? Space.current.id : nil}, :protected => true
17
+ belongs_to :space
18
+
19
+ validates_presence_of :account_id, :space_id
20
+
21
+ default_scope do
22
+ Space.current? ? {:space_id => Space.current.id} : {}
23
+ end
24
+
25
+ # defer do
26
+ ensure_index :account_id
27
+ ensure_index :space_id
28
+ # end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module RadMicelaneous
4
+ module InstanceMethods
5
+ delegate :config, :to => Crystal
6
+ delegate :t, :to => I18n
7
+
8
+ def to_rson options = {}
9
+ with_errors = if options.include?('errors')
10
+ options.delete 'errors'
11
+ elsif options.include?(:errors)
12
+ options.delete :errors
13
+ else
14
+ true
15
+ end
16
+
17
+ # standard MM as_json conversion
18
+ hash = as_json(options)
19
+
20
+ # MM fix
21
+ hash['id'] = hash.delete :id if hash.include? :id
22
+
23
+ # adding errors
24
+ if with_errors
25
+ errors = {}
26
+ errors.each do |name, list|
27
+ errors[name.to_s] = list
28
+ end
29
+ hash['errors'] = errors unless errors.empty?
30
+ end
31
+
32
+ hash
33
+ end
34
+ end
35
+
36
+ module ClassMethods
37
+ delegate :config, :to => Crystal
38
+ delegate :t, :to => I18n
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,62 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module SpaceKeys
4
+
5
+ class SpaceKeysContainer
6
+ include MongoMapper::EmbeddedDocument
7
+ key :space_id, ObjectId
8
+
9
+ SKIP_KEYS = %w{_id space_id}
10
+
11
+ def blank?
12
+ self.class.keys.keys.select{|k| !SKIP_KEYS.include?(k.to_s)}.all?{|k| value = send(k); value.blank? or value == 0}
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+ def get_create_or_delete_space_keys_container modifying_operation, &block
18
+ Rad.multitenant_mode?.must_be.true
19
+
20
+ unless modifying_operation
21
+ container = space_keys_containers.select{|c| c.space_id == Space.current.id}.first || \
22
+ SpaceKeysContainer.new(:space_id => Space.current.id)
23
+ block.call container
24
+ else
25
+ container = space_keys_containers.select{|c| c.space_id == Space.current.id}.first || \
26
+ space_keys_containers.build(:space_id => Space.current.id)
27
+ block.call container
28
+ space_keys_containers.delete container if container.blank?
29
+ end
30
+ end
31
+ end
32
+
33
+ module ClassMethods
34
+ def space_key key, type, options = {}
35
+ define_space_keys_containers
36
+
37
+ SpaceKeysContainer.send :key, key, type, options
38
+
39
+ define_method key do
40
+ get_create_or_delete_space_keys_container false do |container|
41
+ container.send key
42
+ end
43
+ end
44
+
45
+ define_method "#{key}=" do |value|
46
+ get_create_or_delete_space_keys_container true do |container|
47
+ container.send "#{key}=", value
48
+ value
49
+ end
50
+ end
51
+ end
52
+
53
+ def define_space_keys_containers
54
+ unless associations.keys.include? 'space_keys_containers'
55
+ has_many :space_keys_containers, :class_name => 'MongoMapper::Plugins::SpaceKeys::SpaceKeysContainer', :protected => true
56
+ end
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,47 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module TextProcessor
4
+
5
+ module ClassMethods
6
+ def markup_key attr_name, opt = {}
7
+ attr_name = attr_name.to_s
8
+ opt = opt.to_openobject
9
+ original_attr_name = "original_#{attr_name}"
10
+
11
+ key original_attr_name, String
12
+ key attr_name, String, :protected => true unless keys.keys.include? attr_name
13
+
14
+ validates_presence_of attr_name, original_attr_name if opt.required?
15
+
16
+ alias_method "#{attr_name}_without_markup=", "#{attr_name}="
17
+ alias_method "#{original_attr_name}_without_markup=", "#{original_attr_name}="
18
+
19
+ define_method "#{attr_name}=" do |value|
20
+ send "#{original_attr_name}_without_markup=", value
21
+ send "#{attr_name}_without_markup=", value
22
+ end
23
+
24
+ define_method "#{original_attr_name}=" do |value|
25
+ send "#{original_attr_name}_without_markup=", value
26
+ send "#{attr_name}_without_markup=", TextUtils.markup(value)
27
+ end
28
+
29
+ define_method "#{attr_name}_as_text" do
30
+ value = send(attr_name)
31
+ return "" if value.blank?
32
+ Nokogiri::XML(value).content
33
+ end
34
+
35
+ ce_method_name = "copy_errors_for_#{attr_name}"
36
+ define_method ce_method_name do
37
+ if !errors.on(original_attr_name) and (err = errors.on(attr_name))
38
+ errors.add original_attr_name, err
39
+ end
40
+ end
41
+ after_validation ce_method_name
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ require 'mongo_mapper_ext'
2
+
3
+ %w(
4
+ multitenant
5
+ space_keys
6
+ text_processor
7
+ rad_micelaneous
8
+ acts_as/authenticated_by_open_id
9
+ acts_as/authenticated_by_password
10
+ acts_as/authorized
11
+ acts_as/authorized_object
12
+ ).each{|n| require "rad/mongo_mapper/#{n}"}
13
+
14
+ module RadPluginsAddition
15
+ def self.included(model)
16
+ model.plugin MongoMapper::Plugins::Multitenant
17
+ model.plugin MongoMapper::Plugins::RadMicelaneous
18
+ end
19
+ end
20
+ MongoMapper::Document.append_inclusions(RadPluginsAddition)
@@ -0,0 +1,40 @@
1
+ Paperclip::ClassMethods.class_eval do
2
+ def trace_file attachment
3
+ upd_method_name = "update_file_size_for_#{attachment}"
4
+ define_method upd_method_name do
5
+ return if disable_file_audit?
6
+
7
+ size = file_size_for(attachment)
8
+ old_size = old_file_size_for(attachment)
9
+
10
+ if (difference = size - old_size) != 0
11
+ self.class.increase_user_and_account_files_size difference
12
+ set_old_file_size_for(attachment, size)
13
+ end
14
+ end
15
+ protected upd_method_name
16
+ before_save upd_method_name
17
+
18
+ clear_method_name = "clear_file_size_for_#{attachment}"
19
+ define_method clear_method_name do
20
+ return if disable_file_audit?
21
+
22
+ difference = - old_file_size_for(attachment)
23
+ self.class.increase_user_and_account_files_size difference
24
+ end
25
+ protected clear_method_name
26
+ after_destroy clear_method_name
27
+ end
28
+
29
+ def increase_user_and_account_files_size difference
30
+ # Upsert can't be used becouse user.files_size is not just an attribute
31
+ # User.current.files_size += difference
32
+ # User.upsert :$inc => {:files_size => difference}
33
+ u = User.current
34
+ u.files_size += difference
35
+ u.save!
36
+
37
+ Account.current.files_size += difference
38
+ Account.current.upsert :$inc => {:files_size => difference}
39
+ end
40
+ end
@@ -0,0 +1,64 @@
1
+ # interpolation = "/system/:class/:attachment/:id_partition/:style_:filename"
2
+ interpolation = "/system/:account/:space/:class/:slug/:attachment/:filename_with_style"
3
+
4
+ # Paperclip::Attachment.class_eval do
5
+ # default_options.merge!({
6
+ # :url => "#{ActionController::Base.relative_url_root}#{interpolation}",
7
+ # :path => ":rails_root/public" + interpolation,
8
+ # # :default_url => "/:attachment/:style/missing.png",
9
+ # })
10
+ # end
11
+
12
+ crystal.after :config do |config|
13
+ Paperclip::Attachment.class_eval do
14
+ default_options.merge!({
15
+ :url => "#{config.root('')}#{interpolation}",
16
+ :path => ":crystal_root/public" + interpolation,
17
+ # :default_url => "/:attachment/:style/missing.png",
18
+ })
19
+ end
20
+ end
21
+
22
+ module Paperclip
23
+ class << self
24
+ inject :logger => :logger
25
+ end
26
+
27
+ module Interpolations
28
+ def filename_with_style attachment, style
29
+ val = filename(attachment, style).clone
30
+ if style.to_s != 'original'
31
+ basename_index = val.rindex('.') || (val.size - 1)
32
+ val.insert basename_index, ".#{style}"
33
+ end
34
+ val
35
+ end
36
+
37
+ # Handle string ids (mongo)
38
+ # def id_partition attachment, style
39
+ # if (id = attachment.instance.id).is_a?(Integer)
40
+ # ("%09d" % id).scan(/\d{3}/).join("/")
41
+ # else
42
+ # id.to_s.scan(/.{3}/).first(3).join("/")
43
+ # end
44
+ # end
45
+
46
+ %w{name slug}.each do |name|
47
+ define_method name do |attachment, style|
48
+ attachment.instance.send name
49
+ end
50
+ end
51
+
52
+ def account attachment, style
53
+ Account.current.name
54
+ end
55
+
56
+ def space attachment, style
57
+ Space.current.name
58
+ end
59
+
60
+ def crystal_root attachment, style
61
+ crystal.config.root('')
62
+ end
63
+ end
64
+ end