Skip to content

Commit

Permalink
Mark record as changed, if a deferred association is updated. This al…
Browse files Browse the repository at this point in the history
…lows us to use the :autosave option as well

Bump version to 0.6.0
  • Loading branch information
Martin Körner committed May 6, 2016
1 parent 82c7610 commit 1767316
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 13 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
0.6.0
=====
* setting an association will now result in model.changed? to be true.
Also model.changes will include the old attribute key with the old value.
This allows the :autosave option on associated objects to work correctly.

0.5.8
=====
* fix preloading of HABTM associations in ActiveRecord >= 4.0
Expand Down
25 changes: 25 additions & 0 deletions bin/run_all_specs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/sh

DIR=`pwd`

if [[ $DIR == *bin ]]; then
cd ..
fi

echo "Your ruby version: "
echo `ruby -v`

ls -c gemfiles/*.gemfile | while read line
do

echo "Run Spec for gemfile: $line"
export BUNDLE_GEMFILE=$line
bundle install
bundle exec rspec
echo ""
echo ""
echo ""
echo "========="
done

unset BUNDLE_GEMFILE
5 changes: 3 additions & 2 deletions deferred_associations.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

Gem::Specification.new do |s|
s.name = 'deferred_associations'
s.version = '0.5.8'
s.version = '0.6.0'

s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
s.authors = ['Martin Koerner', 'Tyler Rick', 'Alessio Caiazza']
s.date = '2016-03-08'
s.date = '2016-05-06'
s.description = "Makes ActiveRecord defer/postpone saving the records you add to an habtm (has_and_belongs_to_many) or has_many\n association until you call model.save, allowing validation in the style of normal attributes. Additionally you\n can check inside before_save filters, if the association was altered."
s.email = '[email protected]'
s.licenses = 'MIT'
Expand All @@ -20,6 +20,7 @@ Gem::Specification.new do |s|
'VERSION',
'deferred_associations.gemspec',
'init.rb',
'bin/run_all_specs',
'lib/array_to_association_wrapper.rb',
'lib/deferred_associations.rb',
'lib/has_and_belongs_to_many_with_deferred_save.rb',
Expand Down
1 change: 1 addition & 0 deletions lib/has_and_belongs_to_many_with_deferred_save.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def has_and_belongs_to_many_with_deferred_save(*args)
define_method "#{collection_name}_with_deferred_save=" do |collection|
# puts "has_and_belongs_to_many_with_deferred_save: #{collection_name} = #{collection.collect(&:id).join(',')}"
send "unsaved_#{collection_name}=", collection
attribute_will_change!(collection_singular_ids)
end

define_method "#{collection_name}_with_deferred_save" do |*method_args|
Expand Down
16 changes: 8 additions & 8 deletions lib/has_many_with_deferred_save.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Associations
module ClassMethods
def has_many_with_deferred_save(*args)
collection_name = args[0].to_s
collection_singular_ids = "#{collection_name.singularize}_ids"

return if method_defined?("#{collection_name}_with_deferred_save")

Expand All @@ -14,18 +15,19 @@ def has_many_with_deferred_save(*args)

after_save "hmwds_update_#{collection_name}"

define_obj_setter collection_name
define_obj_setter collection_name, collection_singular_ids
define_obj_getter collection_name
define_id_setter collection_name
define_id_getter collection_name
define_id_setter collection_name, collection_singular_ids
define_id_getter collection_name, collection_singular_ids

define_update_method collection_name
define_reload_method collection_name
end

def define_obj_setter(collection_name)
def define_obj_setter(collection_name, collection_singular_ids)
define_method("#{collection_name}_with_deferred_save=") do |objs|
instance_variable_set "@hmwds_temp_#{collection_name}", objs || []
attribute_will_change!(collection_singular_ids)
end

method_name = "#{collection_name}="
Expand Down Expand Up @@ -56,10 +58,9 @@ def define_obj_getter(collection_name)
alias_method_chain collection_name, :deferred_save
end

def define_id_setter(collection_name)
def define_id_setter(collection_name, collection_singular_ids)
# only needed for ActiveRecord >= 3.0
if ActiveRecord::VERSION::STRING >= '3'
collection_singular_ids = "#{collection_name.singularize}_ids"
define_method "#{collection_singular_ids}_with_deferred_save=" do |ids|
ids = Array.wrap(ids).reject(&:blank?)
new_values = send("#{collection_name}_without_deferred_save").klass.find(ids)
Expand All @@ -69,8 +70,7 @@ def define_id_setter(collection_name)
end
end

def define_id_getter(collection_name)
collection_singular_ids = "#{collection_name.singularize}_ids"
def define_id_getter(collection_name, collection_singular_ids)
define_method "#{collection_singular_ids}_with_deferred_save" do
send(collection_name).map { |e| e[:id] }
end
Expand Down
5 changes: 5 additions & 0 deletions spec/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@
t.column 'name', :string
t.column 'table_id', :integer
end

create_table 'windows', force: true do |t|
t.column 'name', :string
t.column 'room_id', :integer
end
end
19 changes: 19 additions & 0 deletions spec/has_and_belongs_to_many_with_deferred_save_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,23 @@
expect(@door.rooms.include?(@rooms[1])).to be false
end
end

describe 'tables' do
before :all do
@table1 = Table.create(name: 'Table1', room_id: Room.create(name: 'Kitchen').id)
@table2 = Table.create(name: 'Table2', room_id: Room.create(name: 'Dining room').id)
@doors = [Door.create(name: 'Door1'), Door.create(name: 'Door2')]
end

it 'saves doors of associated room, if table gets saved' do
@table1.room_with_autosave.doors = @doors
@table2.room_with_autosave.door_ids = [@doors.first.id]
expect(@table1.room_with_autosave).to be_changed
expect(@table2.room_with_autosave).to be_changed
@table1.save!
@table2.save!
expect(@table1.room.doors).to eq @doors
expect(@table2.room.doors).to eq [@doors.first]
end
end
end
19 changes: 19 additions & 0 deletions spec/has_many_with_deferred_save_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,23 @@
expect { Marshal.dump(Room.new.chairs) }.not_to raise_exception
end
end

describe 'with autosave option' do
before :all do
@table3 = Table.create(name: 'Table3', room_id: Room.create(name: 'Kitchen').id)
@table4 = Table.create(name: 'Table4', room_id: Room.create(name: 'Dining room').id)
@windows = [Window.create(name: 'South'), Window.create(name: 'West'), Window.create(name: 'East')]
end

it 'saves windows of associated room, if table gets saved' do
@table3.room_with_autosave.windows = [@windows.first, @windows.second]
@table4.room_with_autosave.window_ids = [@windows.third.id]
expect(@table3.room_with_autosave).to be_changed
expect(@table4.room_with_autosave).to be_changed
@table3.save!
@table4.save!
expect(@table3.room.windows).to eq [@windows.first, @windows.second]
expect(@table4.room.windows).to eq [@windows.third]
end
end
end
5 changes: 2 additions & 3 deletions spec/models/room.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ class Room < ActiveRecord::Base
has_and_belongs_to_many :people2, class_name: 'Person'
has_and_belongs_to_many_with_deferred_save :doors

has_many_with_deferred_save :windows
has_many_with_deferred_save :tables
has_many_with_deferred_save :chairs, through: :tables # TODO: test compatibility with through associations

has_and_belongs_to_many_with_deferred_save :doors
has_many_with_deferred_save :tables

before_save :diff_after_module

validate :people_count

def people_count
errors.add :people, 'This room has reached its maximum occupancy' if people.size > maximum_occupancy
errors.add :people, 'This room has reached its maximum occupancy' if maximum_occupancy && people.size > maximum_occupancy
end

# Just in case they try to bypass our new accessor and call people_without_deferred_save directly...
Expand Down
1 change: 1 addition & 0 deletions spec/models/table.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Table < ActiveRecord::Base

belongs_to :room
belongs_to :room_with_autosave, class_name: 'Room', autosave: true, foreign_key: 'room_id'
has_many_with_deferred_save :chairs

end
4 changes: 4 additions & 0 deletions spec/models/window.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Window < ActiveRecord::Base

belongs_to :room
end

0 comments on commit 1767316

Please sign in to comment.