activehistory 0.1 → 0.2
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 +4 -4
- data/Gemfile.lock +5 -2
- data/activehistory.gemspec +2 -1
- data/lib/activehistory.rb +17 -4
- data/lib/activehistory/action.rb +5 -3
- data/lib/activehistory/adapters/active_record.rb +329 -105
- data/lib/activehistory/connection.rb +11 -23
- data/lib/activehistory/event.rb +79 -25
- data/lib/activehistory/version.rb +2 -2
- data/test/active_record_adapter/association_test/belongs_to_association_test.rb +46 -38
- data/test/active_record_adapter/association_test/has_and_belongs_to_many_test.rb +96 -63
- data/test/active_record_adapter/association_test/has_many_association_test.rb +166 -27
- data/test/active_record_adapter/association_test/has_one_association_test.rb +36 -40
- data/test/active_record_adapter/create_test.rb +10 -14
- data/test/active_record_adapter/destroy_test.rb +4 -6
- data/test/active_record_adapter/event_test.rb +33 -7
- data/test/active_record_adapter/save_test.rb +8 -12
- data/test/activehistory_test.rb +96 -0
- data/test/models.rb +1 -1
- data/test/test_helper.rb +12 -1
- metadata +17 -2
| @@ -8,19 +8,16 @@ class ActiveHistory::Connection | |
| 8 8 | 
             
              def initialize(config)
         | 
| 9 9 | 
             
                if config[:url]
         | 
| 10 10 | 
             
                  uri = URI.parse(config.delete(:url))
         | 
| 11 | 
            -
                  config[:api_key] | 
| 12 | 
            -
                  config[:host] | 
| 13 | 
            -
                  config[:port] | 
| 14 | 
            -
                  config[:ssl] | 
| 11 | 
            +
                  config[:api_key]  ||= (uri.user ? CGI.unescape(uri.user) : nil)
         | 
| 12 | 
            +
                  config[:host]     ||= uri.host
         | 
| 13 | 
            +
                  config[:port]     ||= uri.port
         | 
| 14 | 
            +
                  config[:ssl]      ||= (uri.scheme == 'https')
         | 
| 15 15 | 
             
                end
         | 
| 16 16 |  | 
| 17 17 | 
             
                [:api_key, :host, :port, :ssl, :user_agent].each do |key|
         | 
| 18 18 | 
             
                  self.instance_variable_set(:"@#{key}", config[key])
         | 
| 19 19 | 
             
                end
         | 
| 20 20 |  | 
| 21 | 
            -
                @connection = Net::HTTP.new(host, port)
         | 
| 22 | 
            -
                @connection.use_ssl = ssl
         | 
| 23 | 
            -
             | 
| 24 21 | 
             
                true
         | 
| 25 22 | 
             
              end
         | 
| 26 23 |  | 
| @@ -61,27 +58,18 @@ class ActiveHistory::Connection | |
| 61 58 | 
             
                return_value = nil
         | 
| 62 59 | 
             
                retry_count = 0
         | 
| 63 60 | 
             
                begin
         | 
| 64 | 
            -
                   | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
                    end
         | 
| 68 | 
            -
             | 
| 61 | 
            +
                  connection = Net::HTTP.new(host, port)
         | 
| 62 | 
            +
                  connection.use_ssl = ssl
         | 
| 63 | 
            +
                  connection.request(request) do |response|
         | 
| 69 64 | 
             
                    validate_response_code(response)
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                    # Get the cookies
         | 
| 72 | 
            -
                    response.each_header do |key, value|
         | 
| 73 | 
            -
                      if key.downcase == 'set-cookie' && Thread.current[:sunstone_cookie_store]
         | 
| 74 | 
            -
                        Thread.current[:sunstone_cookie_store].set_cookie(request_uri, value)
         | 
| 75 | 
            -
                      end
         | 
| 76 | 
            -
                    end
         | 
| 77 | 
            -
             | 
| 65 | 
            +
                    
         | 
| 78 66 | 
             
                    if block_given?
         | 
| 79 | 
            -
                      return_value =yield(response)
         | 
| 67 | 
            +
                      return_value = yield(response)
         | 
| 80 68 | 
             
                    else
         | 
| 81 | 
            -
                      return_value =response
         | 
| 69 | 
            +
                      return_value = response
         | 
| 82 70 | 
             
                    end
         | 
| 83 71 | 
             
                  end
         | 
| 84 | 
            -
                rescue  | 
| 72 | 
            +
                rescue ActiveHistory::Exception::ServiceUnavailable
         | 
| 85 73 | 
             
                  retry_count += 1
         | 
| 86 74 | 
             
                  retry_count == 1 ? retry : raise
         | 
| 87 75 | 
             
                end
         | 
    
        data/lib/activehistory/event.rb
    CHANGED
    
    | @@ -1,50 +1,104 @@ | |
| 1 | 
            +
            require 'globalid'
         | 
| 2 | 
            +
            require 'securerandom'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            GlobalID::Locator.use :activehistory do |gid|
         | 
| 5 | 
            +
              ActiveHistory::Event.new({ id: gid.model_id })
         | 
| 6 | 
            +
            end
         | 
| 7 | 
            +
             | 
| 1 8 | 
             
            class ActiveHistory::Event
         | 
| 2 | 
            -
              
         | 
| 3 | 
            -
             | 
| 9 | 
            +
              include GlobalID::Identification
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              attr_accessor :id, :ip, :user_agent, :session_id, :metadata, :timestamp, :performed_by_id, :performed_by_type, :actions
         | 
| 4 12 |  | 
| 5 13 | 
             
              def initialize(attrs={})
         | 
| 6 | 
            -
                 | 
| 7 | 
            -
             | 
| 8 | 
            -
                 | 
| 9 | 
            -
             | 
| 14 | 
            +
                attrs.each do |k,v|
         | 
| 15 | 
            +
                  self.send("#{k}=", v)
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                if id
         | 
| 19 | 
            +
                  @persisted = true
         | 
| 20 | 
            +
                else
         | 
| 21 | 
            +
                  @persisted = false
         | 
| 22 | 
            +
                  @id ||= SecureRandom.uuid
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                @actions ||= []
         | 
| 26 | 
            +
                @timestamp ||= Time.now
         | 
| 10 27 | 
             
              end
         | 
| 11 28 |  | 
| 29 | 
            +
              def persisted?
         | 
| 30 | 
            +
                @persisted
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 12 33 | 
             
              def action!(action)
         | 
| 13 34 | 
             
                action = ActiveHistory::Action.new(action)
         | 
| 14 35 | 
             
                @actions << action
         | 
| 15 36 | 
             
                action
         | 
| 16 37 | 
             
              end
         | 
| 17 38 |  | 
| 18 | 
            -
              def action_for(id)
         | 
| 19 | 
            -
                 | 
| 39 | 
            +
              def action_for(type, id, new_options=nil)
         | 
| 40 | 
            +
                type = type.base_class.model_name.name if !type.is_a?(String)
         | 
| 41 | 
            +
                action = @actions.find { |a| a.subject_type.to_s == type.to_s && a.subject_id.to_s == id.to_s }
         | 
| 42 | 
            +
                
         | 
| 43 | 
            +
                if new_options
         | 
| 44 | 
            +
                  action || action!({ subject_type: type, subject_id: id, type: :update }.merge(new_options))
         | 
| 45 | 
            +
                else
         | 
| 46 | 
            +
                  action
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def self.create!(attrs={})
         | 
| 51 | 
            +
                event = self.new(attrs)
         | 
| 52 | 
            +
                event.save!
         | 
| 53 | 
            +
                event
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
                
         | 
| 56 | 
            +
              def save!
         | 
| 57 | 
            +
                persisted? ? _update : _create
         | 
| 20 58 | 
             
              end
         | 
| 21 59 |  | 
| 22 | 
            -
              def  | 
| 23 | 
            -
                 | 
| 60 | 
            +
              def _update
         | 
| 61 | 
            +
                return if actions.empty?
         | 
| 62 | 
            +
                ActiveHistory.connection.post('/actions', {
         | 
| 63 | 
            +
                  actions: actions.as_json.map{|json| json[:event_id] = id; json}
         | 
| 64 | 
            +
                })
         | 
| 65 | 
            +
                @actions = []
         | 
| 24 66 | 
             
              end
         | 
| 25 67 |  | 
| 26 | 
            -
              def  | 
| 27 | 
            -
                return if @actions.empty?
         | 
| 28 | 
            -
                
         | 
| 68 | 
            +
              def _create
         | 
| 29 69 | 
             
                ActiveHistory.connection.post('/events', self.as_json)
         | 
| 70 | 
            +
                @actions = []
         | 
| 71 | 
            +
                @persisted = true
         | 
| 30 72 | 
             
              end
         | 
| 31 | 
            -
             | 
| 73 | 
            +
             | 
| 32 74 | 
             
              def as_json
         | 
| 33 75 | 
             
                {
         | 
| 34 | 
            -
                   | 
| 35 | 
            -
                   | 
| 36 | 
            -
                   | 
| 37 | 
            -
                   | 
| 38 | 
            -
                   | 
| 39 | 
            -
                   | 
| 40 | 
            -
                   | 
| 41 | 
            -
                   | 
| 42 | 
            -
                   | 
| 76 | 
            +
                  id:                   id,
         | 
| 77 | 
            +
                  ip:                   ip,
         | 
| 78 | 
            +
                  user_agent:           user_agent,
         | 
| 79 | 
            +
                  session_id:           session_id,
         | 
| 80 | 
            +
                  metadata:             metadata,
         | 
| 81 | 
            +
                  performed_by_type:    performed_by_type,
         | 
| 82 | 
            +
                  performed_by_id:      performed_by_id,
         | 
| 83 | 
            +
                  timestamp:            timestamp.utc.iso8601(3),
         | 
| 84 | 
            +
                  actions:              actions.as_json
         | 
| 43 85 | 
             
                }
         | 
| 44 86 | 
             
              end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
              def to_gid_param(options={})
         | 
| 89 | 
            +
                to_global_id(options).to_param
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              def to_global_id(options={})
         | 
| 93 | 
            +
                @global_id ||= GlobalID.create(self, { app: :activehistory }.merge(options))
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
              
         | 
| 96 | 
            +
              def to_sgid_param(options={})
         | 
| 97 | 
            +
                to_signed_global_id(options).to_param
         | 
| 98 | 
            +
              end
         | 
| 45 99 |  | 
| 46 | 
            -
              def  | 
| 47 | 
            -
             | 
| 100 | 
            +
              def to_signed_global_id(options={})
         | 
| 101 | 
            +
                 SignedGlobalID.create(self, { app: :activehistory }.merge(options))
         | 
| 48 102 | 
             
              end
         | 
| 49 103 |  | 
| 50 104 | 
             
            end
         | 
| @@ -1,3 +1,3 @@ | |
| 1 1 | 
             
            module ActiveHistory
         | 
| 2 | 
            -
              VERSION = '0. | 
| 3 | 
            -
            end
         | 
| 2 | 
            +
              VERSION = '0.2'
         | 
| 3 | 
            +
            end
         | 
| @@ -13,70 +13,78 @@ class BelongsToAssociationTest < ActiveSupport::TestCase | |
| 13 13 |  | 
| 14 14 | 
             
                @target = travel_to(@time) { create(:photo, account: @model) }
         | 
| 15 15 |  | 
| 16 | 
            -
                assert_posted("/events") do | 
| 17 | 
            -
                   | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 16 | 
            +
                assert_posted("/events") do
         | 
| 17 | 
            +
                  assert_action_for @model, {
         | 
| 18 | 
            +
                    diff: { photo_ids: [[], [@target.id]] },
         | 
| 19 | 
            +
                    timestamp: @time.iso8601(3),
         | 
| 20 | 
            +
                    type: 'update',
         | 
| 21 | 
            +
                    subject_type: "Account",
         | 
| 22 | 
            +
                    subject_id: @model.id,
         | 
| 23 | 
            +
                  }
         | 
| 23 24 | 
             
                end
         | 
| 24 25 | 
             
              end
         | 
| 25 | 
            -
             | 
| 26 | 
            +
             | 
| 26 27 | 
             
              test 'TargetModel.update removes from association' do
         | 
| 27 28 | 
             
                @model = create(:account)
         | 
| 28 29 | 
             
                @target = create(:photo, account: @model)
         | 
| 29 30 | 
             
                WebMock::RequestRegistry.instance.reset!
         | 
| 30 | 
            -
             | 
| 31 | 
            +
             | 
| 31 32 | 
             
                travel_to(@time) { @target.update(account: nil) }
         | 
| 32 | 
            -
                
         | 
| 33 | 
            -
                assert_posted("/events") do |req|
         | 
| 34 | 
            -
                  req_data = JSON.parse(req.body)
         | 
| 35 | 
            -
                  assert_equal 2, req_data['actions'].size
         | 
| 36 33 |  | 
| 37 | 
            -
             | 
| 38 | 
            -
                   | 
| 39 | 
            -
             | 
| 34 | 
            +
                assert_posted("/events") do
         | 
| 35 | 
            +
                  assert_action_for @model, {
         | 
| 36 | 
            +
                    diff: { photo_ids: [[@target.id], []] },
         | 
| 37 | 
            +
                    timestamp: @time.iso8601(3),
         | 
| 38 | 
            +
                    type: 'update',
         | 
| 39 | 
            +
                    subject_type: "Account",
         | 
| 40 | 
            +
                    subject_id: @model.id,
         | 
| 41 | 
            +
                  }
         | 
| 40 42 | 
             
                end
         | 
| 41 43 | 
             
              end
         | 
| 42 | 
            -
             | 
| 44 | 
            +
             | 
| 43 45 | 
             
              test 'TargetModel.update changes association' do
         | 
| 44 46 | 
             
                @model1 = create(:account)
         | 
| 45 47 | 
             
                @model2 = create(:account)
         | 
| 46 48 | 
             
                @target = create(:photo, account: @model1)
         | 
| 47 49 | 
             
                WebMock::RequestRegistry.instance.reset!
         | 
| 48 | 
            -
             | 
| 50 | 
            +
             | 
| 49 51 | 
             
                travel_to(@time) { @target.update(account: @model2) }
         | 
| 50 | 
            -
                
         | 
| 51 | 
            -
                assert_posted("/events") do |req|
         | 
| 52 | 
            -
                  req_data = JSON.parse(req.body)
         | 
| 53 | 
            -
                  assert_equal 3, req_data['actions'].size
         | 
| 54 52 |  | 
| 55 | 
            -
             | 
| 56 | 
            -
                   | 
| 57 | 
            -
             | 
| 53 | 
            +
                assert_posted("/events") do
         | 
| 54 | 
            +
                  assert_action_for @model1, {
         | 
| 55 | 
            +
                    diff: { photo_ids: [[@target.id], []] },
         | 
| 56 | 
            +
                    timestamp: @time.iso8601(3),
         | 
| 57 | 
            +
                    type: 'update',
         | 
| 58 | 
            +
                    subject_type: "Account",
         | 
| 59 | 
            +
                    subject_id: @model1.id,
         | 
| 60 | 
            +
                  }
         | 
| 58 61 |  | 
| 59 | 
            -
                   | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            +
                  assert_action_for @model2, {
         | 
| 63 | 
            +
                    diff: { photo_ids: [[], [@target.id]] },
         | 
| 64 | 
            +
                    timestamp: @time.iso8601(3),
         | 
| 65 | 
            +
                    type: 'update',
         | 
| 66 | 
            +
                    subject_type: "Account",
         | 
| 67 | 
            +
                    subject_id: @model2.id,
         | 
| 68 | 
            +
                  }
         | 
| 62 69 | 
             
                end
         | 
| 63 70 | 
             
              end
         | 
| 64 | 
            -
             | 
| 71 | 
            +
             | 
| 65 72 | 
             
              test 'TargetModel.destroy removes from association' do
         | 
| 66 73 | 
             
                @model = create(:account)
         | 
| 67 74 | 
             
                @target = create(:photo, account: @model)
         | 
| 68 75 | 
             
                WebMock::RequestRegistry.instance.reset!
         | 
| 69 | 
            -
             | 
| 76 | 
            +
             | 
| 70 77 | 
             
                travel_to(@time) { @target.destroy }
         | 
| 71 | 
            -
                
         | 
| 72 | 
            -
                assert_posted("/events") do |req|
         | 
| 73 | 
            -
                  req_data = JSON.parse(req.body)
         | 
| 74 | 
            -
                  assert_equal 2, req_data['actions'].size
         | 
| 75 78 |  | 
| 76 | 
            -
             | 
| 77 | 
            -
                   | 
| 78 | 
            -
             | 
| 79 | 
            +
                assert_posted("/events") do
         | 
| 80 | 
            +
                  assert_action_for @model, {
         | 
| 81 | 
            +
                    diff: { photo_ids: [[@target.id], []] },
         | 
| 82 | 
            +
                    timestamp: @time.iso8601(3),
         | 
| 83 | 
            +
                    type: 'update',
         | 
| 84 | 
            +
                    subject_type: "Account",
         | 
| 85 | 
            +
                    subject_id: @model.id,
         | 
| 86 | 
            +
                  }
         | 
| 79 87 | 
             
                end
         | 
| 80 88 | 
             
              end
         | 
| 81 | 
            -
             | 
| 89 | 
            +
             | 
| 82 90 | 
             
            end
         | 
| @@ -12,57 +12,54 @@ class HasAndBelongsToManyAssociationTest < ActiveSupport::TestCase | |
| 12 12 | 
             
                WebMock::RequestRegistry.instance.reset!
         | 
| 13 13 |  | 
| 14 14 | 
             
                @region = travel_to(@time) { create(:region, properties: [@property]) }
         | 
| 15 | 
            -
                 | 
| 16 | 
            -
             | 
| 17 | 
            -
                   | 
| 18 | 
            -
             | 
| 19 | 
            -
                  assert_equal req_data['actions'][0], {
         | 
| 15 | 
            +
                
         | 
| 16 | 
            +
                assert_posted("/events") do
         | 
| 17 | 
            +
                  assert_action_for @region, {
         | 
| 20 18 | 
             
                    diff: {
         | 
| 21 19 | 
             
                      id: [nil, @region.id],
         | 
| 22 20 | 
             
                      name: [nil, @region.name],
         | 
| 23 21 | 
             
                      property_ids: [[], [@property.id]]
         | 
| 24 22 | 
             
                    },
         | 
| 25 | 
            -
                     | 
| 23 | 
            +
                    subject_type: "Region",
         | 
| 24 | 
            +
                    subject_id: @region.id,
         | 
| 26 25 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 27 26 | 
             
                    type: 'create'
         | 
| 28 | 
            -
                  } | 
| 29 | 
            -
             | 
| 30 | 
            -
                   | 
| 27 | 
            +
                  }
         | 
| 28 | 
            +
                  
         | 
| 29 | 
            +
                  assert_action_for @property, {
         | 
| 31 30 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 32 31 | 
             
                    type: 'update',
         | 
| 33 | 
            -
                     | 
| 32 | 
            +
                    subject_type: "Property",
         | 
| 33 | 
            +
                    subject_id: @property.id,
         | 
| 34 34 | 
             
                    diff: {
         | 
| 35 35 | 
             
                      region_ids: [[], [@region.id]]
         | 
| 36 36 | 
             
                    }
         | 
| 37 | 
            -
                  } | 
| 37 | 
            +
                  }
         | 
| 38 38 | 
             
                end
         | 
| 39 39 | 
             
              end
         | 
| 40 40 |  | 
| 41 41 | 
             
              test '::create with new has_and_belongs_to_many association' do
         | 
| 42 | 
            -
                WebMock::RequestRegistry.instance.reset!
         | 
| 43 | 
            -
             | 
| 44 42 | 
             
                @region = travel_to(@time) { create(:region, properties: [build(:property)]) }
         | 
| 45 43 | 
             
                @property = @region.properties.first
         | 
| 46 44 |  | 
| 47 | 
            -
                assert_posted("/events") do | 
| 48 | 
            -
                   | 
| 49 | 
            -
                  assert_equal 2, req_data['actions'].size
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                  assert_equal req_data['actions'][0], {
         | 
| 45 | 
            +
                assert_posted("/events") do
         | 
| 46 | 
            +
                  assert_action_for @region, {
         | 
| 52 47 | 
             
                    diff: {
         | 
| 53 48 | 
             
                      id: [nil, @region.id],
         | 
| 54 49 | 
             
                      name: [nil, @region.name],
         | 
| 55 50 | 
             
                      property_ids: [[], [@property.id]]
         | 
| 56 51 | 
             
                    },
         | 
| 57 | 
            -
                     | 
| 52 | 
            +
                    subject_type: "Region",
         | 
| 53 | 
            +
                    subject_id: @region.id,
         | 
| 58 54 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 59 55 | 
             
                    type: 'create'
         | 
| 60 | 
            -
                  } | 
| 56 | 
            +
                  }
         | 
| 61 57 |  | 
| 62 | 
            -
                   | 
| 58 | 
            +
                  assert_action_for @property, {
         | 
| 63 59 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 64 60 | 
             
                    type: 'create',
         | 
| 65 | 
            -
                     | 
| 61 | 
            +
                    subject_type: "Property",
         | 
| 62 | 
            +
                    subject_id: @property.id,
         | 
| 66 63 | 
             
                    diff: {
         | 
| 67 64 | 
             
                      id: [nil, @property.id],
         | 
| 68 65 | 
             
                      name: [nil, @property.name],
         | 
| @@ -74,7 +71,7 @@ class HasAndBelongsToManyAssociationTest < ActiveSupport::TestCase | |
| 74 71 | 
             
                      active: [nil, @property.active],
         | 
| 75 72 | 
             
                      region_ids: [[], [@region.id]]
         | 
| 76 73 | 
             
                    }
         | 
| 77 | 
            -
                  } | 
| 74 | 
            +
                  }
         | 
| 78 75 | 
             
                end
         | 
| 79 76 | 
             
              end
         | 
| 80 77 |  | 
| @@ -85,27 +82,26 @@ class HasAndBelongsToManyAssociationTest < ActiveSupport::TestCase | |
| 85 82 |  | 
| 86 83 | 
             
                travel_to(@time) { @region.update(properties: [@property]) }
         | 
| 87 84 |  | 
| 88 | 
            -
                assert_posted("/events") do | 
| 89 | 
            -
                   | 
| 90 | 
            -
                  assert_equal 2, req_data['actions'].size
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                  assert_equal req_data['actions'][0], {
         | 
| 85 | 
            +
                assert_posted("/events") do
         | 
| 86 | 
            +
                  assert_action_for @region, {
         | 
| 93 87 | 
             
                    diff: {
         | 
| 94 88 | 
             
                      property_ids: [[], [@property.id]]
         | 
| 95 89 | 
             
                    },
         | 
| 96 | 
            -
                     | 
| 90 | 
            +
                    subject_type: "Region",
         | 
| 91 | 
            +
                    subject_id: @region.id,
         | 
| 97 92 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 98 93 | 
             
                    type: 'update'
         | 
| 99 | 
            -
                  } | 
| 94 | 
            +
                  }
         | 
| 100 95 |  | 
| 101 | 
            -
                   | 
| 96 | 
            +
                  assert_action_for @property, {
         | 
| 102 97 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 103 98 | 
             
                    type: 'update',
         | 
| 104 | 
            -
                     | 
| 99 | 
            +
                    subject_type: "Property",
         | 
| 100 | 
            +
                    subject_id: @property.id,
         | 
| 105 101 | 
             
                    diff: {
         | 
| 106 102 | 
             
                      region_ids: [[], [@region.id]]
         | 
| 107 103 | 
             
                    }
         | 
| 108 | 
            -
                  } | 
| 104 | 
            +
                  }
         | 
| 109 105 | 
             
                end
         | 
| 110 106 | 
             
              end
         | 
| 111 107 |  | 
| @@ -116,14 +112,12 @@ class HasAndBelongsToManyAssociationTest < ActiveSupport::TestCase | |
| 116 112 | 
             
                travel_to(@time) { @region.update(properties: [build(:property)]) }
         | 
| 117 113 | 
             
                @property = @region.properties.first
         | 
| 118 114 |  | 
| 119 | 
            -
                assert_posted("/events") do | 
| 120 | 
            -
                   | 
| 121 | 
            -
                  assert_equal 2, req_data['actions'].size
         | 
| 122 | 
            -
             | 
| 123 | 
            -
                  assert_equal req_data['actions'][0], {
         | 
| 115 | 
            +
                assert_posted("/events") do
         | 
| 116 | 
            +
                  assert_action_for @property, {
         | 
| 124 117 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 125 118 | 
             
                    type: 'create',
         | 
| 126 | 
            -
                     | 
| 119 | 
            +
                    subject_type: "Property",
         | 
| 120 | 
            +
                    subject_id: @property.id,
         | 
| 127 121 | 
             
                    diff: {
         | 
| 128 122 | 
             
                      id: [nil, @property.id],
         | 
| 129 123 | 
             
                      name: [nil, @property.name],
         | 
| @@ -135,16 +129,17 @@ class HasAndBelongsToManyAssociationTest < ActiveSupport::TestCase | |
| 135 129 | 
             
                      active: [nil, @property.active],
         | 
| 136 130 | 
             
                      region_ids: [[], [@region.id]]
         | 
| 137 131 | 
             
                    }
         | 
| 138 | 
            -
                  } | 
| 132 | 
            +
                  }
         | 
| 139 133 |  | 
| 140 | 
            -
                   | 
| 134 | 
            +
                  assert_action_for @region, {
         | 
| 141 135 | 
             
                    diff: {
         | 
| 142 136 | 
             
                      property_ids: [[], [@property.id]]
         | 
| 143 137 | 
             
                    },
         | 
| 144 | 
            -
                     | 
| 138 | 
            +
                    subject_type: "Region",
         | 
| 139 | 
            +
                    subject_id: @region.id,
         | 
| 145 140 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 146 141 | 
             
                    type: 'update'
         | 
| 147 | 
            -
                  } | 
| 142 | 
            +
                  }
         | 
| 148 143 | 
             
                end
         | 
| 149 144 | 
             
              end
         | 
| 150 145 |  | 
| @@ -152,30 +147,70 @@ class HasAndBelongsToManyAssociationTest < ActiveSupport::TestCase | |
| 152 147 | 
             
                @property = create(:property)
         | 
| 153 148 | 
             
                @region = create(:region, properties: [@property])
         | 
| 154 149 | 
             
                WebMock::RequestRegistry.instance.reset!
         | 
| 155 | 
            -
             | 
| 150 | 
            +
                
         | 
| 156 151 | 
             
                travel_to(@time) { @region.update(properties: []) }
         | 
| 157 152 |  | 
| 158 | 
            -
                assert_posted("/events") do | 
| 159 | 
            -
                   | 
| 160 | 
            -
                  assert_equal 2, req_data['actions'].size
         | 
| 161 | 
            -
             | 
| 162 | 
            -
                  assert_equal req_data['actions'][0], {
         | 
| 153 | 
            +
                assert_posted("/events") do
         | 
| 154 | 
            +
                  assert_action_for @region, {
         | 
| 163 155 | 
             
                    diff: {
         | 
| 164 156 | 
             
                      property_ids: [[@property.id], []]
         | 
| 165 157 | 
             
                    },
         | 
| 166 | 
            -
                     | 
| 158 | 
            +
                    subject_type: "Region",
         | 
| 159 | 
            +
                    subject_id: @region.id,
         | 
| 167 160 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 168 161 | 
             
                    type: 'update'
         | 
| 169 | 
            -
                  } | 
| 162 | 
            +
                  }
         | 
| 170 163 |  | 
| 171 | 
            -
                   | 
| 164 | 
            +
                  assert_action_for @property, {
         | 
| 172 165 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 173 166 | 
             
                    type: 'update',
         | 
| 174 | 
            -
                     | 
| 167 | 
            +
                    subject_type: "Property",
         | 
| 168 | 
            +
                    subject_id: @property.id,
         | 
| 175 169 | 
             
                    diff: {
         | 
| 176 170 | 
             
                      region_ids: [[@region.id], []]
         | 
| 177 171 | 
             
                    }
         | 
| 178 | 
            -
                  } | 
| 172 | 
            +
                  }
         | 
| 173 | 
            +
                end
         | 
| 174 | 
            +
              end
         | 
| 175 | 
            +
              
         | 
| 176 | 
            +
              test '::update with replacing has_and_belongs_to_many association' do
         | 
| 177 | 
            +
                @property1 = create(:property)
         | 
| 178 | 
            +
                @property2 = create(:property)
         | 
| 179 | 
            +
                @region = create(:region, properties: [@property1])
         | 
| 180 | 
            +
                WebMock::RequestRegistry.instance.reset!
         | 
| 181 | 
            +
                
         | 
| 182 | 
            +
                travel_to(@time) { @region.update(properties: [@property2]) }
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                assert_posted("/events") do
         | 
| 185 | 
            +
                  assert_action_for @region, {
         | 
| 186 | 
            +
                    diff: {
         | 
| 187 | 
            +
                      property_ids: [[@property1.id], [@property2.id]]
         | 
| 188 | 
            +
                    },
         | 
| 189 | 
            +
                    subject_type: "Region",
         | 
| 190 | 
            +
                    subject_id: @region.id,
         | 
| 191 | 
            +
                    timestamp: @time.iso8601(3),
         | 
| 192 | 
            +
                    type: 'update'
         | 
| 193 | 
            +
                  }
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                  assert_action_for @property1, {
         | 
| 196 | 
            +
                    timestamp: @time.iso8601(3),
         | 
| 197 | 
            +
                    type: 'update',
         | 
| 198 | 
            +
                    subject_type: "Property",
         | 
| 199 | 
            +
                    subject_id: @property1.id,
         | 
| 200 | 
            +
                    diff: {
         | 
| 201 | 
            +
                      region_ids: [[@region.id], []]
         | 
| 202 | 
            +
                    }
         | 
| 203 | 
            +
                  }
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                  assert_action_for @property2, {
         | 
| 206 | 
            +
                    timestamp: @time.iso8601(3),
         | 
| 207 | 
            +
                    type: 'update',
         | 
| 208 | 
            +
                    subject_type: "Property",
         | 
| 209 | 
            +
                    subject_id: @property2.id,
         | 
| 210 | 
            +
                    diff: {
         | 
| 211 | 
            +
                      region_ids: [[], [@region.id]]
         | 
| 212 | 
            +
                    }
         | 
| 213 | 
            +
                  }
         | 
| 179 214 | 
             
                end
         | 
| 180 215 | 
             
              end
         | 
| 181 216 |  | 
| @@ -186,25 +221,24 @@ class HasAndBelongsToManyAssociationTest < ActiveSupport::TestCase | |
| 186 221 |  | 
| 187 222 | 
             
                travel_to(@time) { @region.destroy }
         | 
| 188 223 |  | 
| 189 | 
            -
                assert_posted("/events") do | 
| 190 | 
            -
                   | 
| 191 | 
            -
                  assert_equal 2, req_data['actions'].size
         | 
| 192 | 
            -
             | 
| 193 | 
            -
                  assert_equal req_data['actions'][0], {
         | 
| 224 | 
            +
                assert_posted("/events") do
         | 
| 225 | 
            +
                  assert_action_for @region, {
         | 
| 194 226 | 
             
                    diff: {
         | 
| 195 227 | 
             
                      id: [@region.id, nil],
         | 
| 196 228 | 
             
                      name: [@region.name, nil],
         | 
| 197 229 | 
             
                      property_ids: [[@property.id], []]
         | 
| 198 230 | 
             
                    },
         | 
| 199 | 
            -
                     | 
| 231 | 
            +
                    subject_type: "Region",
         | 
| 232 | 
            +
                    subject_id: @region.id,
         | 
| 200 233 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 201 234 | 
             
                    type: 'destroy'
         | 
| 202 235 | 
             
                  }.as_json
         | 
| 203 236 |  | 
| 204 | 
            -
                   | 
| 237 | 
            +
                  assert_action_for @property, {
         | 
| 205 238 | 
             
                    timestamp: @time.iso8601(3),
         | 
| 206 239 | 
             
                    type: 'update',
         | 
| 207 | 
            -
                     | 
| 240 | 
            +
                    subject_type: "Property",
         | 
| 241 | 
            +
                    subject_id: @property.id,
         | 
| 208 242 | 
             
                    diff: {
         | 
| 209 243 | 
             
                      region_ids: [[@region.id], []]
         | 
| 210 244 | 
             
                    }
         | 
| @@ -212,7 +246,6 @@ class HasAndBelongsToManyAssociationTest < ActiveSupport::TestCase | |
| 212 246 | 
             
                end
         | 
| 213 247 | 
             
              end
         | 
| 214 248 |  | 
| 215 | 
            -
             | 
| 216 249 | 
             
              test 'has_and_belongs_to_many <<'
         | 
| 217 250 | 
             
              test 'has_and_belongs_to_many.delete'
         | 
| 218 251 | 
             
              test 'has_and_belongs_to_many.destroy'
         |