redcrumbs 0.5.0 → 0.5.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.
- checksums.yaml +15 -0
 - data/.gitignore +1 -0
 - data/.travis.yml +33 -0
 - data/Gemfile +6 -0
 - data/LICENSE +21 -0
 - data/README.md +140 -105
 - data/gemfiles/Gemfile.rails-3.1.x +12 -0
 - data/gemfiles/Gemfile.rails-3.2.x +12 -0
 - data/gemfiles/Gemfile.rails-4.0.x +12 -0
 - data/gemfiles/Gemfile.rails-4.1.x +12 -0
 - data/lib/generators/redcrumbs/templates/initializer.rb +9 -26
 - data/lib/redcrumbs.rb +12 -7
 - data/lib/redcrumbs/config.rb +55 -5
 - data/lib/redcrumbs/creation.rb +60 -47
 - data/lib/redcrumbs/crumb.rb +100 -0
 - data/lib/redcrumbs/options.rb +8 -3
 - data/lib/redcrumbs/serializable_association.rb +193 -0
 - data/lib/redcrumbs/users.rb +27 -37
 - data/lib/redcrumbs/version.rb +1 -1
 - data/redcrumbs.gemspec +11 -9
 - data/spec/redcrumbs/config_spec.rb +168 -0
 - data/spec/redcrumbs/creation_spec.rb +271 -0
 - data/spec/redcrumbs/crumb_spec.rb +254 -0
 - data/spec/redcrumbs/options_spec.rb +70 -0
 - data/spec/redcrumbs/serializable_association_spec.rb +101 -0
 - data/spec/redcrumbs/users_spec.rb +55 -0
 - data/spec/spec_helper.rb +27 -0
 - data/spec/support/models.rb +34 -0
 - data/spec/support/schema.rb +26 -0
 - metadata +106 -34
 - data/app/models/redcrumbs/crumb.rb +0 -68
 - data/app/models/redcrumbs/crumb/expiry.rb +0 -23
 - data/app/models/redcrumbs/crumb/getters.rb +0 -74
 - data/app/models/redcrumbs/crumb/setters.rb +0 -28
 - data/lib/redcrumbs/engine.rb +0 -8
 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            source 'https://rubygems.org'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            gem 'activerecord', '~> 4.0'
         
     | 
| 
      
 4 
     | 
    
         
            +
            gem 'activesupport', '~> 4.0'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            gemspec :path => '..'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            group :test do
         
     | 
| 
      
 9 
     | 
    
         
            +
              gem 'codeclimate-test-reporter', :group => :test, :require => nil
         
     | 
| 
      
 10 
     | 
    
         
            +
              gem 'rspec', '~> 3.0'
         
     | 
| 
      
 11 
     | 
    
         
            +
              gem 'sqlite3', '~> 1.0'
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            source 'https://rubygems.org'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            gem 'activerecord', '~> 4.1'
         
     | 
| 
      
 4 
     | 
    
         
            +
            gem 'activesupport', '~> 4.1'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            gemspec :path => '..'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            group :test do
         
     | 
| 
      
 9 
     | 
    
         
            +
              gem 'codeclimate-test-reporter', :group => :test, :require => nil
         
     | 
| 
      
 10 
     | 
    
         
            +
              gem 'rspec', '~> 3.0'
         
     | 
| 
      
 11 
     | 
    
         
            +
              gem 'sqlite3', '~> 1.0'
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,34 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Redcrumbs.setup do |config|
         
     | 
| 
       2 
     | 
    
         
            -
              #  
     | 
| 
       3 
     | 
    
         
            -
              #  
     | 
| 
       4 
     | 
    
         
            -
              #  
     | 
| 
      
 2 
     | 
    
         
            +
              # If your activity feeds are user-based you can store creator and target 
         
     | 
| 
      
 3 
     | 
    
         
            +
              # attributes on the crumb object to avoid having to touch your main database
         
     | 
| 
      
 4 
     | 
    
         
            +
              # at all. Keep it sensible and evaluate whether the additional space used in 
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Redis is really worth the time saving. 
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Note: You don't need to store the object id, it is already stored.
         
     | 
| 
       5 
7 
     | 
    
         
             
              #
         
     | 
| 
       6 
     | 
    
         
            -
              # config. 
     | 
| 
       7 
     | 
    
         
            -
              # config. 
     | 
| 
       8 
     | 
    
         
            -
              # config.target_class_sym = :user
         
     | 
| 
       9 
     | 
    
         
            -
              # config.target_primary_key = 'id'
         
     | 
| 
       10 
     | 
    
         
            -
              #
         
     | 
| 
       11 
     | 
    
         
            -
              #
         
     | 
| 
       12 
     | 
    
         
            -
              # If you're using the crumbs to report news back to a user you can store creator and target 
         
     | 
| 
       13 
     | 
    
         
            -
              # attributes on the crumb object to avoid having to touch your main database at all. Keep it 
         
     | 
| 
       14 
     | 
    
         
            -
              # sensible and evaluate whether the additional space used in Redis is really worth the time saving.
         
     | 
| 
       15 
     | 
    
         
            -
              #
         
     | 
| 
       16 
     | 
    
         
            -
              # config.store_creator_attributes = [:id, :name, :email]
         
     | 
| 
       17 
     | 
    
         
            -
              # config.store_target_attributes = [:id, :name, :email]
         
     | 
| 
      
 8 
     | 
    
         
            +
              # config.store_creator_attributes = [:name, :email]
         
     | 
| 
      
 9 
     | 
    
         
            +
              # config.store_target_attributes = [:name, :email]
         
     | 
| 
       18 
10 
     | 
    
         
             
              #
         
     | 
| 
       19 
11 
     | 
    
         
             
              #
         
     | 
| 
       20 
12 
     | 
    
         
             
              # Set the mortality to make crumbs automatically expire in time. Default is infinity.
         
     | 
| 
       21 
13 
     | 
    
         
             
              # config.mortality = 30.days
         
     | 
| 
       22 
14 
     | 
    
         
             
            end
         
     | 
| 
       23 
15 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
            # Point this to your redis connection. It can be a Redis  
     | 
| 
       25 
     | 
    
         
            -
            Redcrumbs.redis = 'localhost:6379'
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
            # You may want to create a config/redcrumbs.yml file and base your redis setting
         
     | 
| 
       28 
     | 
    
         
            -
            # from that. e.g. (thanks to Resque for this):
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
            # rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
         
     | 
| 
       31 
     | 
    
         
            -
            # rails_env = ENV['RAILS_ENV'] || 'development'
         
     | 
| 
       32 
     | 
    
         
            -
            # 
         
     | 
| 
       33 
     | 
    
         
            -
            # redcrumbs_config = YAML.load_file(rails_root + '/config/redcrumbs.yml')
         
     | 
| 
       34 
     | 
    
         
            -
            # Redcrumbs.redis = redcrumbs_config[rails_env]
         
     | 
| 
      
 16 
     | 
    
         
            +
            # Point this to your redis connection. It can be a Redis client, namespace or a URL string.
         
     | 
| 
      
 17 
     | 
    
         
            +
            Redcrumbs.redis = 'localhost:6379'
         
     | 
    
        data/lib/redcrumbs.rb
    CHANGED
    
    | 
         @@ -1,24 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'active_support'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'active_support/core_ext'
         
     | 
| 
       1 
3 
     | 
    
         
             
            require 'active_support/concern'
         
     | 
| 
       2 
4 
     | 
    
         
             
            require 'active_support/dependencies/autoload'
         
     | 
| 
       3 
     | 
    
         
            -
            require  
     | 
| 
       4 
     | 
    
         
            -
            require 'redcrumbs/ 
     | 
| 
      
 5 
     | 
    
         
            +
            require 'active_record'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'redcrumbs/version'
         
     | 
| 
       5 
7 
     | 
    
         
             
            require 'redcrumbs/config'
         
     | 
| 
       6 
8 
     | 
    
         
             
            require 'redis'
         
     | 
| 
       7 
9 
     | 
    
         
             
            require 'redis-namespace'
         
     | 
| 
       8 
10 
     | 
    
         
             
            require 'dm-core'
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            # Redcrumbs  
     | 
| 
      
 12 
     | 
    
         
            +
            # Redcrumbs uses `dirty attributes` to track and store changes to ActiveRecord models in a way that is fast and
         
     | 
| 
       11 
13 
     | 
    
         
             
            # unobtrusive. By storing the data in Redis instead of a SQL database the footprint is greatly reduced, no
         
     | 
| 
       12 
14 
     | 
    
         
             
            # schema changes are necessary and we can harness all the advantages of a key value store; such as key expiry.
         
     | 
| 
       13 
15 
     | 
    
         
             
            #
         
     | 
| 
       14 
16 
     | 
    
         
             
            # Author:: John Hope
         
     | 
| 
       15 
     | 
    
         
            -
            # Copyright:: Copyright (c)  
     | 
| 
      
 17 
     | 
    
         
            +
            # Copyright:: Copyright (c) 2014 John Hope for Project Zebra
         
     | 
| 
       16 
18 
     | 
    
         
             
            # License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
         
     | 
| 
       17 
19 
     | 
    
         
             
            #
         
     | 
| 
       18 
20 
     | 
    
         
             
            # To start tracking a model use the 'redcrumbed' method:
         
     | 
| 
       19 
21 
     | 
    
         
             
            #
         
     | 
| 
       20 
22 
     | 
    
         
             
            #   class Venue
         
     | 
| 
       21 
23 
     | 
    
         
             
            #     redcrumbed, :only => [:name, :latlng]
         
     | 
| 
      
 24 
     | 
    
         
            +
            #
         
     | 
| 
      
 25 
     | 
    
         
            +
            #     has_one :creator, :class_name => 'User'
         
     | 
| 
       22 
26 
     | 
    
         
             
            #   end
         
     | 
| 
       23 
27 
     | 
    
         
             
            #
         
     | 
| 
       24 
28 
     | 
    
         
             
            #   venue = Venue.last
         
     | 
| 
         @@ -38,6 +42,10 @@ module Redcrumbs 
     | 
|
| 
       38 
42 
     | 
    
         
             
              autoload :Options
         
     | 
| 
       39 
43 
     | 
    
         
             
              autoload :Users
         
     | 
| 
       40 
44 
     | 
    
         
             
              autoload :Creation
         
     | 
| 
      
 45 
     | 
    
         
            +
              autoload :Crumb
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              include Options
         
     | 
| 
      
 48 
     | 
    
         
            +
              include Users
         
     | 
| 
       41 
49 
     | 
    
         | 
| 
       42 
50 
     | 
    
         
             
              def self.setup
         
     | 
| 
       43 
51 
     | 
    
         
             
                yield self
         
     | 
| 
         @@ -49,9 +57,6 @@ module Redcrumbs 
     | 
|
| 
       49 
57 
     | 
    
         | 
| 
       50 
58 
     | 
    
         
             
              module ClassMethods
         
     | 
| 
       51 
59 
     | 
    
         
             
                def redcrumbed(options = {})
         
     | 
| 
       52 
     | 
    
         
            -
                  
         
     | 
| 
       53 
     | 
    
         
            -
                  include Options
         
     | 
| 
       54 
     | 
    
         
            -
                  include Users
         
     | 
| 
       55 
60 
     | 
    
         
             
                  include Creation
         
     | 
| 
       56 
61 
     | 
    
         | 
| 
       57 
62 
     | 
    
         
             
                  prepare_redcrumbed_options(options)
         
     | 
    
        data/lib/redcrumbs/config.rb
    CHANGED
    
    | 
         @@ -10,14 +10,32 @@ module Redcrumbs 
     | 
|
| 
       10 
10 
     | 
    
         
             
              mattr_accessor :mortality
         
     | 
| 
       11 
11 
     | 
    
         
             
              mattr_accessor :redis
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
      
 13 
     | 
    
         
            +
              mattr_accessor :class_name
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              # This should only be used to load old crumbs from previous versions
         
     | 
| 
      
 17 
     | 
    
         
            +
              # of the gem, in future require an explicit creator/target method to set.
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
       13 
19 
     | 
    
         
             
              @@creator_class_sym ||= :user
         
     | 
| 
       14 
20 
     | 
    
         
             
              @@creator_primary_key ||= 'id'
         
     | 
| 
       15 
     | 
    
         
            -
              @@target_class_sym ||= :user
         
     | 
| 
      
 21 
     | 
    
         
            +
              @@target_class_sym ||= :user 
         
     | 
| 
       16 
22 
     | 
    
         
             
              @@target_primary_key ||= 'id'
         
     | 
| 
       17 
23 
     | 
    
         | 
| 
       18 
24 
     | 
    
         
             
              @@store_creator_attributes ||= []
         
     | 
| 
       19 
25 
     | 
    
         
             
              @@store_target_attributes ||= []
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              # Constantises the class_name attribute, falls back to the Crumb default.
         
     | 
| 
      
 29 
     | 
    
         
            +
              #
         
     | 
| 
      
 30 
     | 
    
         
            +
              def self.crumb_class
         
     | 
| 
      
 31 
     | 
    
         
            +
                if @@class_name and @@class_name.length > 0
         
     | 
| 
      
 32 
     | 
    
         
            +
                  constantize_class_name
         
     | 
| 
      
 33 
     | 
    
         
            +
                else
         
     | 
| 
      
 34 
     | 
    
         
            +
                  Crumb
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
       20 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       21 
39 
     | 
    
         
             
              # Stolen from resque. Thanks!
         
     | 
| 
       22 
40 
     | 
    
         
             
              # Accepts:
         
     | 
| 
       23 
41 
     | 
    
         
             
              #   1. A 'hostname:port' String
         
     | 
| 
         @@ -26,6 +44,7 @@ module Redcrumbs 
     | 
|
| 
       26 
44 
     | 
    
         
             
              #   4. A Redis URL String 'redis://host:port'
         
     | 
| 
       27 
45 
     | 
    
         
             
              #   5. An instance of `Redis`, `Redis::Client`, `Redis::DistRedis`,
         
     | 
| 
       28 
46 
     | 
    
         
             
              #      or `Redis::Namespace`.
         
     | 
| 
      
 47 
     | 
    
         
            +
              #
         
     | 
| 
       29 
48 
     | 
    
         
             
              def self.redis=(server)
         
     | 
| 
       30 
49 
     | 
    
         
             
                case server
         
     | 
| 
       31 
50 
     | 
    
         
             
                when String
         
     | 
| 
         @@ -45,12 +64,43 @@ module Redcrumbs 
     | 
|
| 
       45 
64 
     | 
    
         
             
                else
         
     | 
| 
       46 
65 
     | 
    
         
             
                  @@redis = Redis::Namespace.new(:redcrumbs, :redis => server)
         
     | 
| 
       47 
66 
     | 
    
         
             
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                setup_datamapper!
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
       48 
70 
     | 
    
         
             
                @@redis
         
     | 
| 
       49 
71 
     | 
    
         
             
              end
         
     | 
| 
       50 
72 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
               
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 73 
     | 
    
         
            +
              private
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              # Note: Since it's not possible to access the exact connection the DataMapper adapter
         
     | 
| 
      
 76 
     | 
    
         
            +
              # uses we have to use the @@redis module variable and make sure it's consistent.
         
     | 
| 
      
 77 
     | 
    
         
            +
              #
         
     | 
| 
      
 78 
     | 
    
         
            +
              def self.setup_datamapper!
         
     | 
| 
      
 79 
     | 
    
         
            +
                adapter = DataMapper.setup(:default, 
         
     | 
| 
      
 80 
     | 
    
         
            +
                  { :adapter  => "redis", 
         
     | 
| 
      
 81 
     | 
    
         
            +
                    :host => self.redis.client.host, 
         
     | 
| 
      
 82 
     | 
    
         
            +
                    :port => self.redis.client.port, 
         
     | 
| 
      
 83 
     | 
    
         
            +
                    :password => self.redis.client.password
         
     | 
| 
      
 84 
     | 
    
         
            +
                  })
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                # For supporting namespaces:
         
     | 
| 
      
 87 
     | 
    
         
            +
                #
         
     | 
| 
      
 88 
     | 
    
         
            +
                adapter.resource_naming_convention = lambda do |value|
         
     | 
| 
      
 89 
     | 
    
         
            +
                  inflected_value = DataMapper::Inflector.pluralize(DataMapper::Inflector.underscore(value)).gsub('/', '_')
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  "#{self.redis.namespace}:#{inflected_value}"
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
              end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
              def self.constantize_class_name
         
     | 
| 
      
 96 
     | 
    
         
            +
                klass = @@class_name.to_s.classify.constantize
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                unless klass < Redcrumbs::Crumb
         
     | 
| 
      
 99 
     | 
    
         
            +
                  raise ArgumentError, 'Redcrumbs crumb_class must inherit from Redcrumbs::Crumb'
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                klass
         
     | 
| 
      
 103 
     | 
    
         
            +
              rescue NameError
         
     | 
| 
      
 104 
     | 
    
         
            +
                Crumb
         
     | 
| 
       55 
105 
     | 
    
         
             
              end
         
     | 
| 
       56 
106 
     | 
    
         
             
            end
         
     | 
    
        data/lib/redcrumbs/creation.rb
    CHANGED
    
    | 
         @@ -2,57 +2,70 @@ module Redcrumbs 
     | 
|
| 
       2 
2 
     | 
    
         
             
              module Creation
         
     | 
| 
       3 
3 
     | 
    
         
             
                extend ActiveSupport::Concern
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
                 
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
       7 
     | 
    
         
            -
                     
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 5 
     | 
    
         
            +
                def crumbs
         
     | 
| 
      
 6 
     | 
    
         
            +
                  Redcrumbs.crumb_class.all(
         
     | 
| 
      
 7 
     | 
    
         
            +
                    :subject_type => self.class.to_s, 
         
     | 
| 
      
 8 
     | 
    
         
            +
                    :subject_id => self.id
         
     | 
| 
      
 9 
     | 
    
         
            +
                  )
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
      
 12 
     | 
    
         
            +
                def watched_changes
         
     | 
| 
      
 13 
     | 
    
         
            +
                  changes.slice(*self.class.redcrumbs_options[:only])
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
       13 
15 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                    if store.has_key?(:only)
         
     | 
| 
       17 
     | 
    
         
            -
                      attributes.reject {|k,v| !store[:only].include?(k.to_sym)}
         
     | 
| 
       18 
     | 
    
         
            -
                    elsif store.has_key?(:except)
         
     | 
| 
       19 
     | 
    
         
            -
                      attributes.reject {|k,v| store[:except].include?(k.to_sym)}
         
     | 
| 
       20 
     | 
    
         
            -
                    else
         
     | 
| 
       21 
     | 
    
         
            -
                      {}
         
     | 
| 
       22 
     | 
    
         
            -
                    end
         
     | 
| 
       23 
     | 
    
         
            -
                  end
         
     | 
| 
       24 
     | 
    
         
            -
                  
         
     | 
| 
       25 
     | 
    
         
            -
                  def attributes_from_storeable_methods
         
     | 
| 
       26 
     | 
    
         
            -
                    store = self.class.redcrumbs_options[:store]
         
     | 
| 
       27 
     | 
    
         
            -
                    if store.has_key?(:methods)
         
     | 
| 
       28 
     | 
    
         
            -
                      # get the methods that actually exist on the model
         
     | 
| 
       29 
     | 
    
         
            -
                      methods = methods_from_array(store[:methods])
         
     | 
| 
       30 
     | 
    
         
            -
                      # inject them into a hash with their outcomes as values
         
     | 
| 
       31 
     | 
    
         
            -
                      methods.inject({}) {|h,a| h.merge(a => send(a))}
         
     | 
| 
       32 
     | 
    
         
            -
                    else
         
     | 
| 
       33 
     | 
    
         
            -
                      {}
         
     | 
| 
       34 
     | 
    
         
            -
                    end
         
     | 
| 
       35 
     | 
    
         
            -
                  end
         
     | 
| 
       36 
     | 
    
         
            -
                  
         
     | 
| 
       37 
     | 
    
         
            -
                  def storeable_attributes_and_method_attributes
         
     | 
| 
       38 
     | 
    
         
            -
                    storeable_attributes.merge(attributes_from_storeable_methods)
         
     | 
| 
       39 
     | 
    
         
            -
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                def storable_attributes_keys
         
     | 
| 
      
 17 
     | 
    
         
            +
                  store = self.class.redcrumbs_options[:store]
         
     | 
| 
       40 
18 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
                   
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
                   
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                   
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                   
     | 
| 
       54 
     | 
    
         
            -
                     
     | 
| 
      
 19 
     | 
    
         
            +
                  store[:only] or 
         
     | 
| 
      
 20 
     | 
    
         
            +
                  symbolized_attribute_keys(store[:except]) or
         
     | 
| 
      
 21 
     | 
    
         
            +
                  []
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def storeable_attributes
         
     | 
| 
      
 25 
     | 
    
         
            +
                  attributes.slice *storable_attributes_keys.map(&:to_s)
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def storable_methods_names
         
     | 
| 
      
 29 
     | 
    
         
            +
                  store = self.class.redcrumbs_options[:store]
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  if store[:methods]
         
     | 
| 
      
 32 
     | 
    
         
            +
                    methods.select {|method| store[:methods].include?(method.to_sym)}
         
     | 
| 
      
 33 
     | 
    
         
            +
                  else
         
     | 
| 
      
 34 
     | 
    
         
            +
                    []
         
     | 
| 
       55 
35 
     | 
    
         
             
                  end
         
     | 
| 
       56 
36 
     | 
    
         
             
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
                
         
     | 
| 
      
 38 
     | 
    
         
            +
                # Todo: Fix inconsistent naming; storable vs storeable
         
     | 
| 
      
 39 
     | 
    
         
            +
                def storable_methods
         
     | 
| 
      
 40 
     | 
    
         
            +
                  storable_methods_names.inject({}) {|h, n| h.merge(n.to_s => send(n))}
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
                
         
     | 
| 
      
 43 
     | 
    
         
            +
                def serialized_as_redcrumbs_subject
         
     | 
| 
      
 44 
     | 
    
         
            +
                  storeable_attributes.merge(storable_methods)
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
                
         
     | 
| 
      
 47 
     | 
    
         
            +
                def create_crumb
         
     | 
| 
      
 48 
     | 
    
         
            +
                  n = Redcrumbs.crumb_class.build_with_modifications(self)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  n.save
         
     | 
| 
      
 50 
     | 
    
         
            +
                  n
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
                
         
     | 
| 
      
 53 
     | 
    
         
            +
                # This is called after the record is saved to store the changes on the model, including anything done in before_save validations
         
     | 
| 
      
 54 
     | 
    
         
            +
                def notify_changes
         
     | 
| 
      
 55 
     | 
    
         
            +
                  create_crumb unless watched_changes.empty?
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
                
         
     | 
| 
      
 58 
     | 
    
         
            +
                private
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                def symbolized_attribute_keys(except = [])
         
     | 
| 
      
 61 
     | 
    
         
            +
                  return nil unless except
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  symbolized_attribute_keys = attributes.dup.symbolize_keys!.keys
         
     | 
| 
      
 64 
     | 
    
         
            +
                  symbolized_attribute_keys.reject {|key| except.include?(key)}
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
                
         
     | 
| 
      
 67 
     | 
    
         
            +
                def methods_from_array(array)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  self.class.instance_methods.select {|method| array.include?(method.to_sym)}
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
       57 
70 
     | 
    
         
             
              end
         
     | 
| 
       58 
71 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,100 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dm-core'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'dm-types'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'dm-timestamps'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'dm-redis-adapter'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'redcrumbs/serializable_association'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module Redcrumbs
         
     | 
| 
      
 8 
     | 
    
         
            +
              class Crumb
         
     | 
| 
      
 9 
     | 
    
         
            +
                
         
     | 
| 
      
 10 
     | 
    
         
            +
                include DataMapper::Resource
         
     | 
| 
      
 11 
     | 
    
         
            +
                include Redcrumbs::SerializableAssociation
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                property :id, Serial
         
     | 
| 
      
 14 
     | 
    
         
            +
                property :modifications, Json, :default => "{}", :lazy => false
         
     | 
| 
      
 15 
     | 
    
         
            +
                property :created_at, DateTime
         
     | 
| 
      
 16 
     | 
    
         
            +
                property :updated_at, DateTime
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                DataMapper.finalize
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                after :save, :set_mortality
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                serializable_association :creator
         
     | 
| 
      
 23 
     | 
    
         
            +
                serializable_association :target
         
     | 
| 
      
 24 
     | 
    
         
            +
                serializable_association :subject
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def initialize(params = {})
         
     | 
| 
      
 27 
     | 
    
         
            +
                  self.subject = params[:subject]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  self.modifications = params[:modifications]
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def self.build_with_modifications(subject)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  return if subject.watched_changes.empty?
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  new(:modifications => subject.watched_changes, :subject => subject)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def self.created_by(creator)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  all(:creator_id => creator[Redcrumbs.creator_primary_key]) &
         
     | 
| 
      
 39 
     | 
    
         
            +
                  all(:creator_type => creator.class.name)
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def self.targetted_by(target)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  all(:target_id => target[Redcrumbs.target_primary_key]) &
         
     | 
| 
      
 44 
     | 
    
         
            +
                  all(:target_type => target.class.name)
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                # Overrides the subject setter created by the SerializableAttributes
         
     | 
| 
      
 48 
     | 
    
         
            +
                # module.
         
     | 
| 
      
 49 
     | 
    
         
            +
                #
         
     | 
| 
      
 50 
     | 
    
         
            +
                def subject=(subject)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @subject = subject
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  self.stored_subject = subject ? serialize(:subject, subject) : {}
         
     | 
| 
      
 54 
     | 
    
         
            +
                  self.subject_id = subject ? subject.id : nil
         
     | 
| 
      
 55 
     | 
    
         
            +
                  assign_type_for(:subject, subject)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  self.target  = subject.target  if subject.respond_to?(:target)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  self.creator = subject.creator if subject.respond_to?(:creator)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  subject
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def redis_key
         
     | 
| 
      
 64 
     | 
    
         
            +
                  "redcrumbs_crumbs:#{id}" if id
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                # Designed to mimic ActiveRecord's count. Probably not performant and only should be used for tests really
         
     | 
| 
      
 68 
     | 
    
         
            +
                def self.count
         
     | 
| 
      
 69 
     | 
    
         
            +
                  Redcrumbs.redis.keys("redcrumbs_crumbs:*").size - 8
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                # Expiry
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                def mortal?
         
     | 
| 
      
 75 
     | 
    
         
            +
                  return false if new?
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  time_to_live >= 0
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
                
         
     | 
| 
      
 80 
     | 
    
         
            +
                def time_to_live
         
     | 
| 
      
 81 
     | 
    
         
            +
                  return nil if new?
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  @ttl ||= Redcrumbs.redis.ttl(redis_key)
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                def expires_at
         
     | 
| 
      
 87 
     | 
    
         
            +
                  Time.now + time_to_live if time_to_live
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
                
         
     | 
| 
      
 90 
     | 
    
         
            +
                private
         
     | 
| 
      
 91 
     | 
    
         
            +
                
         
     | 
| 
      
 92 
     | 
    
         
            +
                def set_mortality
         
     | 
| 
      
 93 
     | 
    
         
            +
                  Redcrumbs.redis.expireat(redis_key, expire_from_now.to_i) if Redcrumbs.mortality
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                def expire_from_now
         
     | 
| 
      
 97 
     | 
    
         
            +
                  Time.now + Redcrumbs.mortality
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
              end
         
     | 
| 
      
 100 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/redcrumbs/options.rb
    CHANGED
    
    | 
         @@ -6,6 +6,8 @@ module Redcrumbs 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  # prepare_redcrumbed_options prepares class level options that customise the behaviour of
         
     | 
| 
       7 
7 
     | 
    
         
             
                  # redcrumbed. See documentation for a full explanation of redcrumbed options.
         
     | 
| 
       8 
8 
     | 
    
         
             
                  def prepare_redcrumbed_options(options)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    options.symbolize_keys!
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       9 
11 
     | 
    
         
             
                    defaults = {
         
     | 
| 
       10 
12 
     | 
    
         
             
                      :only => [],
         
     | 
| 
       11 
13 
     | 
    
         
             
                      :store => {}
         
     | 
| 
         @@ -14,13 +16,16 @@ module Redcrumbs 
     | 
|
| 
       14 
16 
     | 
    
         
             
                    options.reverse_merge!(defaults)
         
     | 
| 
       15 
17 
     | 
    
         | 
| 
       16 
18 
     | 
    
         
             
                    options[:only] = Array(options[:only])
         
     | 
| 
       17 
     | 
    
         
            -
                    options[:store] = options[:store]
         
     | 
| 
       18 
19 
     | 
    
         | 
| 
       19 
20 
     | 
    
         
             
                    class_attribute :redcrumbs_options
         
     | 
| 
       20 
     | 
    
         
            -
                    class_attribute :redcrumbs_callback_options
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                    self.redcrumbs_options = options.dup
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    options
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def redcrumbs_callback_options
         
     | 
| 
      
 28 
     | 
    
         
            +
                    redcrumbs_options.slice(:if, :unless)
         
     | 
| 
       24 
29 
     | 
    
         
             
                  end
         
     | 
| 
       25 
30 
     | 
    
         
             
                end
         
     | 
| 
       26 
31 
     | 
    
         
             
              end
         
     |