rapid 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 (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