Class: Activr::Activity
- Inherits:
-
Object
- Object
- Activr::Activity
- Extended by:
- ActiveModel::Callbacks
- Defined in:
- lib/activr/activity.rb
Overview
An activity is an event that is (most of the time) performed by a user in your application.
When defining an activity you specify allowed entities and a humanization template.
When instanciated, an activity contains:
- Concrete `entities` instances
- A (the `at` field)
- User-defined `meta` data
By default, entities are mandatory and the exception MissingEntityError is raised when trying to store an activity with a missing entity.
When an activity is stored in database, its ‘_id` field is filled.
Model callbacks:
- `before_store`, `around_store` and `after_store` are called when activity is stored in database
- `before_route`, `around_route` and `after_route` are called when activity is routed by the dispatcher
Defined Under Namespace
Classes: MissingEntityError
Instance Attribute Summary collapse
-
#_id ⇒ Object
Activity id.
-
#at ⇒ Time
Activity timestamp.
-
#entities ⇒ Hash{Symbol=>Entity}
readonly
Activity entities.
-
#meta ⇒ Hash{Symbol=>Object}
readonly
Activity meta hash (symbolized).
Class Method Summary collapse
-
.entity(name, options = { }) ⇒ Object
Define an allowed entity for that activity class.
-
.from_hash(data_hash) ⇒ Activity
Instanciate an activity from a hash.
-
.humanize(tpl) ⇒ Object
Define a humanization template for that activity class.
-
.kind ⇒ String
Get activity class kind.
-
.set_kind(forced_kind) ⇒ Object
Set activity kind.
-
.unserialize_hash(data_hash) ⇒ Hash
Unserialize an activity hash.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Get a meta.
-
#[]=(key, value) ⇒ Object
Set a meta.
-
#check! ⇒ Object
private
Check if activity is valid.
-
#humanization_bindings(options = { }) ⇒ Hash
Bindings for humanization sentence.
-
#humanize(options = { }) ⇒ String
Humanize that activity.
-
#initialize(data_hash = { }) ⇒ Activity
constructor
A new instance of Activity.
-
#kind ⇒ String
Activity kind.
-
#method_missing(sym, *args, &blk) ⇒ Object
private
Sugar so that we can try to fetch an entity defined for another activity (yes, I hate myself for that…).
-
#store! ⇒ Object
Store activity in database.
-
#stored? ⇒ true, false
Check if activity is stored in database.
-
#to_hash ⇒ Hash
Serialize activity to a hash.
Constructor Details
#initialize(data_hash = { }) ⇒ Activity
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/activr/activity.rb', line 251 def initialize(data_hash = { }) @_id = nil @at = nil @entities = { } = { } data_hash.each do |data_name, data_value| data_name = data_name.to_sym if (self.allowed_entities[data_name] != nil) # entity @entities[data_name] = Activr::Entity.new(data_name, data_value, self.allowed_entities[data_name].merge(:activity => self)) elsif (data_name == :_id) # activity id @_id = data_value elsif (data_name == :at) # timestamp raise "Wrong :at class: #{data_value.inspect}" unless data_value.is_a?(Time) @at = data_value elsif self.respond_to?("#{data_name}=") # ivar self.send("#{data_name}=", data_value) elsif (data_name == :kind) # ignore it elsif (data_name == :meta) # meta .merge!(data_value.symbolize_keys) else # sugar for meta data self[data_name] = data_value end end # default timestamp @at ||= Time.now.utc end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args, &blk) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Sugar so that we can try to fetch an entity defined for another activity (yes, I hate myself for that…)
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
# File 'lib/activr/activity.rb', line 423 def method_missing(sym, *args, &blk) # match: actor_entity | actor_id | actor match_data = sym.to_s.match(/(.+)_(entity|id)$/) entity_name = match_data ? match_data[1].to_sym : sym if Activr.registry.entities_names.include?(entity_name) # ok, don't worry... # define an instance method so that future calls on that method do not rely on method_missing self.instance_eval " def \#{sym}(*args, &blk)\n nil\n end\n RUBY\n\n self.__send__(sym, *args, &blk)\n else\n # super Michel !\n super\n end\nend\n" |
Instance Attribute Details
#_id ⇒ Object
239 240 241 |
# File 'lib/activr/activity.rb', line 239 def _id @_id end |
#at ⇒ Time
242 243 244 |
# File 'lib/activr/activity.rb', line 242 def at @at end |
#entities ⇒ Hash{Symbol=>Entity} (readonly)
245 246 247 |
# File 'lib/activr/activity.rb', line 245 def entities @entities end |
#meta ⇒ Hash{Symbol=>Object} (readonly)
248 249 250 |
# File 'lib/activr/activity.rb', line 248 def end |
Class Method Details
.entity(name, options = { }) ⇒ Object
By convention the entity that correspond to a user performing an action should be named ‘:actor`
Define an allowed entity for that activity class
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/activr/activity.rb', line 168 def entity(name, = { }) name = name.to_sym raise "Entity already defined: #{name}" unless self.allowed_entities[name].nil? if [:class].nil? = .dup [:class] = name.to_s.camelize.constantize end # NOTE: always use a setter on a class_attribute (cf. http://apidock.com/rails/Class/class_attribute) self.allowed_entities = self.allowed_entities.merge(name => ) # create entity methods class_eval " # eg: actor\n def \#{name}\n @entities[:\#{name}] && @entities[:\#{name}].model\n end\n\n # eg: actor = ...\n def \#{name}=(value)\n @entities.delete(:\#{name})\n\n if (value != nil)\n @entities[:\#{name}] = Activr::Entity.new(:\#{name}, value, self.allowed_entities[:\#{name}])\n end\n end\n\n # eg: actor_id\n def \#{name}_id\n @entities[:\#{name}] && @entities[:\#{name}].model_id\n end\n\n # eg: actor_entity\n def \#{name}_entity\n @entities[:\#{name}]\n end\n EOS\n\n if (name == :actor) && options[:class] &&\n (options[:class] < Activr::Entity::ModelMixin) &&\n options[:class].activr_entity_settings[:name].nil?\n # sugar so that we don't have to explicitly call `activr_entity` on model class\n options[:class].activr_entity_settings = options[:class].activr_entity_settings.merge(:name => :actor)\n end\n\n # register used entity\n Activr.registry.add_entity(name, options, self)\nend\n", __FILE__, __LINE__ |
.from_hash(data_hash) ⇒ Activity
Correct activity subclass is resolved thanks to ‘kind` field
Instanciate an activity from a hash
98 99 100 101 102 103 104 |
# File 'lib/activr/activity.rb', line 98 def from_hash(data_hash) activity_kind = data_hash['kind'] || data_hash[:kind] raise "No kind found in activity hash: #{data_hash.inspect}" unless activity_kind klass = Activr.registry.class_for_activity(activity_kind) klass.new(data_hash) end |
.humanize(tpl) ⇒ Object
Define a humanization template for that activity class
221 222 223 224 225 |
# File 'lib/activr/activity.rb', line 221 def humanize(tpl) raise "Humanize already defined: #{self.humanize_tpl}" unless self.humanize_tpl.blank? self.humanize_tpl = tpl end |
.kind ⇒ String
Kind is inferred from class name, unless ‘#set_kind` method is used to force a custom value
Get activity class kind
88 89 90 |
# File 'lib/activr/activity.rb', line 88 def kind @kind ||= @forced_kind || Activr::Utils.kind_for_class(self, 'activity') end |
.set_kind(forced_kind) ⇒ Object
Default kind is inferred from class name
Set activity kind
232 233 234 |
# File 'lib/activr/activity.rb', line 232 def set_kind(forced_kind) @forced_kind = forced_kind.to_s end |
.unserialize_hash(data_hash) ⇒ Hash
Unserialize an activity hash
That method fixes issues remaining after an activity hash has been unserialized partially:
- the `at` field is converted from String to Time
- the `_id` field is converted from `{ '$oid' => [String] }` format to correct `ObjectId` class (`BSON::ObjectId` or `Moped::BSON::ObjectId`)
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/activr/activity.rb', line 115 def unserialize_hash(data_hash) result = { } data_hash.each do |key, val| result[key] = if Activr.storage.serialized_id?(val) Activr.storage.unserialize_id(val) elsif (key == 'at') && val.is_a?(String) Time.parse(val) else val end end result end |
Instance Method Details
#[](key) ⇒ Object
Get a meta
297 298 299 |
# File 'lib/activr/activity.rb', line 297 def [](key) [key.to_sym] end |
#[]=(key, value) ⇒ Object
Set a meta
308 309 310 |
# File 'lib/activr/activity.rb', line 308 def []=(key, value) [key.to_sym] = value end |
#check! ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Check if activity is valid
389 390 391 392 393 394 395 396 |
# File 'lib/activr/activity.rb', line 389 def check! # check mandatory entities self.allowed_entities.each do |entity_name, | if ![:optional] && @entities[entity_name].blank? raise Activr::Activity::MissingEntityError, "Missing '#{entity_name}' entity in this '#{self.kind}' activity: #{self.inspect}" end end end |
#humanization_bindings(options = { }) ⇒ Hash
Bindings for humanization sentence
For each entity, returned hash contains:
:<entity_name> => <entity humanization>
:<entity_name>_model => <entity model instance>
All ‘meta` are merged in returned hash too.
363 364 365 366 367 368 369 370 371 372 |
# File 'lib/activr/activity.rb', line 363 def humanization_bindings( = { }) result = { } @entities.each do |entity_name, entity| result[entity_name] = entity.humanize(.merge(:activity => self)) result["#{entity_name}_model".to_sym] = entity.model end result.merge() end |
#humanize(options = { }) ⇒ String
Humanize that activity
379 380 381 382 383 |
# File 'lib/activr/activity.rb', line 379 def humanize( = { }) raise "No humanize_tpl defined" if self.humanize_tpl.blank? Activr.sentence(self.humanize_tpl, self.humanization_bindings()) end |
#kind ⇒ String
Kind is inferred from Class name
Activity kind
349 350 351 |
# File 'lib/activr/activity.rb', line 349 def kind self.class.kind end |
#store! ⇒ Object
SIDE EFFECT: The ‘_id` field is set
Store activity in database
410 411 412 413 414 415 416 417 418 |
# File 'lib/activr/activity.rb', line 410 def store! run_callbacks(:store) do # check validity self.check! # store @_id = Activr.storage.insert_activity(self) end end |
#stored? ⇒ true, false
Check if activity is stored in database
401 402 403 |
# File 'lib/activr/activity.rb', line 401 def stored? !@_id.nil? end |
#to_hash ⇒ Hash
All keys are stringified (ie. there is no Symbol)
Serialize activity to a hash
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/activr/activity.rb', line 317 def to_hash result = { } # id result['_id'] = @_id if @_id # timestamp result['at'] = @at # kind result['kind'] = kind.to_s # entities @entities.each do |entity_name, entity| result[entity_name.to_s] = entity.model_id end # meta result['meta'] = .stringify_keys unless .blank? result end |