nested_attribute_reassignable 0.6.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1bda179cf854b25b013cadbbb7838bf11572c4f7
4
+ data.tar.gz: 8b4afb437325e23f618343374d58f9c8a7c57257
5
+ SHA512:
6
+ metadata.gz: 6e0c05e1fce17388ebf90640b94f5785e78c93bcf96f76e8344f27c724bbc093a78fafc6faef9a267f9455da52b2afdced51a243014e518ba284dcd194ed664d
7
+ data.tar.gz: ea6998aa4c6bca31fb52ce89ecd3f97390e6a26e0bc9e6915ace85314de280acd30aaf5bf5d688e3179ff8bc1d3940fec9623a2f31650b9b0dfcff7d57bc4d72
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ .byebug_history
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.swn
12
+ *.swo
13
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Appraisals ADDED
@@ -0,0 +1,7 @@
1
+ appraise "activerecord-4" do
2
+ gem "activerecord", "~> 4.1"
3
+ end
4
+
5
+ appraise "activerecord-5" do
6
+ gem "activerecord", "~> 5.0"
7
+ end
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nested_attribute_reassignable.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'appraisal'
8
+ gem 'pry'
9
+ gem 'pry-byebug'
10
+ gem 'guard-rspec', '~> 4.7'
11
+ end
data/Guardfile ADDED
@@ -0,0 +1,43 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: "bundle exec rspec" do
28
+ require "guard/rspec/dsl"
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+ watch(%r{^spec/(.*)\/?(.*)_spec\.rb$})
31
+
32
+ # Feel free to open issues for suggestions and improvements
33
+
34
+ # RSpec files
35
+ rspec = dsl.rspec
36
+ watch(rspec.spec_helper) { rspec.spec_dir }
37
+ watch(rspec.spec_support) { rspec.spec_dir }
38
+ watch(rspec.spec_files)
39
+
40
+ # Ruby files
41
+ ruby = dsl.ruby
42
+ dsl.watch_spec_files_for(ruby.lib_files)
43
+ end
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # NestedAttributeReassignable
2
+
3
+ [![Build Status](https://travis-ci.org/jsonapi-suite/nested_attribute_reassignable.svg?branch=master)](https://travis-ci.org/jsonapi-suite/nested_attribute_reassignable)
4
+
5
+ Normal `accepts_nested_attributes_for` works find when all objects being
6
+ created are unpersisted. But if the base object is unpersisted and being
7
+ associated to pre-existing records, it will blow up:
8
+
9
+ ```ruby
10
+ pet = Pet.create!(name: 'Spot')
11
+ Person.create(name: 'Joe', pets_attributes: [{ id: pet.id }])
12
+ # => ActiveRecord::RecordNotFound: Couldn't find Pet with ID=1 for Person with ID=
13
+ ```
14
+
15
+ This gem allows you to assign pre-existing records without error
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'nested_attribute_reassignable'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install nested_attribute_reassignable
32
+
33
+ ## Usage
34
+
35
+ * Instead of `accepts_nested_attributes_for` use
36
+ `reassignable_nested_attributes_for`.
37
+ * You can pass nested IDs **or** nested attributes, not both. If you
38
+ pass both, the attributes will be dropped and the nested record will
39
+ not update:
40
+
41
+ ```ruby
42
+ pet = Pet.create!(name: 'Spot')
43
+ person = Person.create(name: 'Joe', pets_attributes: [{ id: pet.id,
44
+ name: 'Elmo' }])
45
+ person.reload.pets.first.id == pet.id # => true
46
+ person.reload.pets.first.name # => 'Spot', not 'Elmo'
47
+ ```
48
+
49
+ `nested_attribute_reassignable` internally calls `accepts_nested_attributes_for`
50
+ with `allow_destroy: true` option.
51
+
52
+ Supports customizing the lookup_key for nested attributes as shown below
53
+
54
+ ```ruby
55
+ class Person < ApplicationRecord
56
+ has_many :pets
57
+ has_many :bills
58
+ has_many :services, through: :bills, through: :destroy
59
+
60
+ reassignable_nested_attributes_for :services, lookup_key: :name
61
+ reassignable_nested_attributes_for :pets, lookup_key: :name
62
+ end
63
+
64
+ rent = Service.create!(name: 'Rent')
65
+ mobile = Service.create!(name: 'Mobile')
66
+ cat = Pet.create!(name: 'Cat')
67
+
68
+ person = Person.create({
69
+ name: 'Joe',
70
+ services_attributes: [{ id: rent.name }]),
71
+ pets_attributes: [{ id: cat.name }])
72
+ }
73
+
74
+ person.reload.bills.first.service_id == rent.id # => true
75
+ person.reload.pets.first.id == cat.id # => true
76
+
77
+ person.update_attributes({
78
+ pets_attributes: [{ id: cat.name, _destroy: true }])
79
+ }
80
+
81
+ #has_many
82
+ person.reload.pets #=> []
83
+ Pet.all #=> [] deletes associated records
84
+
85
+ #has_many => through
86
+ person.update_attributes({
87
+ services_attributes: [{ id: rent.name, _destroy: true }])
88
+ }
89
+
90
+ person.reload.bills #=> []
91
+ Service.all #=> [rent, mobile] won't destroy Service, only the join record
92
+
93
+ person.update_attributes({
94
+ services_attributes: [{ id: rent.name, _delete: true }])
95
+ }
96
+
97
+ person.reload.bills #=> []
98
+ Service.all #=> [rent, mobile] won't delete Service, only the join record
99
+ ```
100
+
101
+
102
+ ### _delete
103
+
104
+ Normal `accepts_nested_attributes_for` accepts a `_destroy` parameter
105
+ for destroying the association. This will destroy the underlying record.
106
+ If you only want to disassociate the record, you can now use `_delete`.
107
+
108
+ ### Running the tests
109
+
110
+ This library uses [appraisal](https://github.com/thoughtbot/appraisal) to test against activerecord >= 4.1. Run
111
+
112
+ ```bash
113
+ $ bin/appraisal activerecord-4 rspec
114
+ $ bin/appraisal activerecord-5 rspec
115
+ ```
116
+
117
+ Or `bin/appraisal rspec` to run against all versions.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/appraisal ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'appraisal' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("appraisal", "appraisal")
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "nested_attribute_reassignable"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require "pathname"
10
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require "rubygems"
14
+ require "bundler/setup"
15
+
16
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://artprod.dev.bloomberg.com/artifactory/api/gems/rubygems/"
4
+
5
+ gem "activerecord", "~> 4.1"
6
+
7
+ group :test do
8
+ gem "appraisal"
9
+ gem "pry"
10
+ gem "pry-byebug"
11
+ gem "guard-rspec", "~> 4.7"
12
+ end
13
+
14
+ gemspec :path => "../"
@@ -0,0 +1,112 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ nested_attribute_reassignable (0.6.1)
5
+ activerecord (>= 4.1, < 6)
6
+
7
+ GEM
8
+ remote: http://artprod.dev.bloomberg.com/artifactory/api/gems/rubygems/
9
+ specs:
10
+ activemodel (4.2.6)
11
+ activesupport (= 4.2.6)
12
+ builder (~> 3.1)
13
+ activerecord (4.2.6)
14
+ activemodel (= 4.2.6)
15
+ activesupport (= 4.2.6)
16
+ arel (~> 6.0)
17
+ activesupport (4.2.6)
18
+ i18n (~> 0.7)
19
+ json (~> 1.7, >= 1.7.7)
20
+ minitest (~> 5.1)
21
+ thread_safe (~> 0.3, >= 0.3.4)
22
+ tzinfo (~> 1.1)
23
+ appraisal (2.1.0)
24
+ bundler
25
+ rake
26
+ thor (>= 0.14.0)
27
+ arel (6.0.3)
28
+ builder (3.2.2)
29
+ byebug (9.0.5)
30
+ coderay (1.1.1)
31
+ database_cleaner (1.5.3)
32
+ diff-lcs (1.2.5)
33
+ ffi (1.9.14)
34
+ formatador (0.2.5)
35
+ guard (2.14.0)
36
+ formatador (>= 0.2.4)
37
+ listen (>= 2.7, < 4.0)
38
+ lumberjack (~> 1.0)
39
+ nenv (~> 0.1)
40
+ notiffany (~> 0.0)
41
+ pry (>= 0.9.12)
42
+ shellany (~> 0.0)
43
+ thor (>= 0.18.1)
44
+ guard-compat (1.2.1)
45
+ guard-rspec (4.7.3)
46
+ guard (~> 2.1)
47
+ guard-compat (~> 1.1)
48
+ rspec (>= 2.99.0, < 4.0)
49
+ i18n (0.7.0)
50
+ json (1.8.3)
51
+ listen (3.1.5)
52
+ rb-fsevent (~> 0.9, >= 0.9.4)
53
+ rb-inotify (~> 0.9, >= 0.9.7)
54
+ ruby_dep (~> 1.2)
55
+ lumberjack (1.0.10)
56
+ method_source (0.8.2)
57
+ minitest (5.9.0)
58
+ nenv (0.3.0)
59
+ notiffany (0.1.1)
60
+ nenv (~> 0.1)
61
+ shellany (~> 0.0)
62
+ pry (0.10.4)
63
+ coderay (~> 1.1.0)
64
+ method_source (~> 0.8.1)
65
+ slop (~> 3.4)
66
+ pry-byebug (3.4.0)
67
+ byebug (~> 9.0)
68
+ pry (~> 0.10)
69
+ rake (10.5.0)
70
+ rb-fsevent (0.9.7)
71
+ rb-inotify (0.9.7)
72
+ ffi (>= 0.5.0)
73
+ rspec (3.5.0)
74
+ rspec-core (~> 3.5.0)
75
+ rspec-expectations (~> 3.5.0)
76
+ rspec-mocks (~> 3.5.0)
77
+ rspec-core (3.5.3)
78
+ rspec-support (~> 3.5.0)
79
+ rspec-expectations (3.5.0)
80
+ diff-lcs (>= 1.2.0, < 2.0)
81
+ rspec-support (~> 3.5.0)
82
+ rspec-mocks (3.5.0)
83
+ diff-lcs (>= 1.2.0, < 2.0)
84
+ rspec-support (~> 3.5.0)
85
+ rspec-support (3.5.0)
86
+ ruby_dep (1.3.1)
87
+ shellany (0.0.1)
88
+ slop (3.6.0)
89
+ sqlite3 (1.3.11)
90
+ thor (0.19.1)
91
+ thread_safe (0.3.5)
92
+ tzinfo (1.2.2)
93
+ thread_safe (~> 0.1)
94
+
95
+ PLATFORMS
96
+ ruby
97
+
98
+ DEPENDENCIES
99
+ activerecord (~> 4.1)
100
+ appraisal
101
+ bundler (~> 1.11)
102
+ database_cleaner
103
+ guard-rspec (~> 4.7)
104
+ nested_attribute_reassignable!
105
+ pry
106
+ pry-byebug
107
+ rake (~> 10.0)
108
+ rspec (~> 3.0)
109
+ sqlite3
110
+
111
+ BUNDLED WITH
112
+ 1.12.5
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://artprod.dev.bloomberg.com/artifactory/api/gems/rubygems/"
4
+
5
+ gem "activerecord", "~> 5.0"
6
+
7
+ group :test do
8
+ gem "appraisal"
9
+ gem "pry"
10
+ gem "pry-byebug"
11
+ gem "guard-rspec", "~> 4.7"
12
+ end
13
+
14
+ gemspec :path => "../"
@@ -0,0 +1,109 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ nested_attribute_reassignable (0.6.1)
5
+ activerecord (>= 4.1, < 6)
6
+
7
+ GEM
8
+ remote: http://artprod.dev.bloomberg.com/artifactory/api/gems/rubygems/
9
+ specs:
10
+ activemodel (5.0.0.1)
11
+ activesupport (= 5.0.0.1)
12
+ activerecord (5.0.0.1)
13
+ activemodel (= 5.0.0.1)
14
+ activesupport (= 5.0.0.1)
15
+ arel (~> 7.0)
16
+ activesupport (5.0.0.1)
17
+ concurrent-ruby (~> 1.0, >= 1.0.2)
18
+ i18n (~> 0.7)
19
+ minitest (~> 5.1)
20
+ tzinfo (~> 1.1)
21
+ appraisal (2.1.0)
22
+ bundler
23
+ rake
24
+ thor (>= 0.14.0)
25
+ arel (7.1.1)
26
+ byebug (9.0.5)
27
+ coderay (1.1.1)
28
+ concurrent-ruby (1.0.2)
29
+ database_cleaner (1.5.3)
30
+ diff-lcs (1.2.5)
31
+ ffi (1.9.14)
32
+ formatador (0.2.5)
33
+ guard (2.14.0)
34
+ formatador (>= 0.2.4)
35
+ listen (>= 2.7, < 4.0)
36
+ lumberjack (~> 1.0)
37
+ nenv (~> 0.1)
38
+ notiffany (~> 0.0)
39
+ pry (>= 0.9.12)
40
+ shellany (~> 0.0)
41
+ thor (>= 0.18.1)
42
+ guard-compat (1.2.1)
43
+ guard-rspec (4.7.3)
44
+ guard (~> 2.1)
45
+ guard-compat (~> 1.1)
46
+ rspec (>= 2.99.0, < 4.0)
47
+ i18n (0.7.0)
48
+ listen (3.1.5)
49
+ rb-fsevent (~> 0.9, >= 0.9.4)
50
+ rb-inotify (~> 0.9, >= 0.9.7)
51
+ ruby_dep (~> 1.2)
52
+ lumberjack (1.0.10)
53
+ method_source (0.8.2)
54
+ minitest (5.9.0)
55
+ nenv (0.3.0)
56
+ notiffany (0.1.1)
57
+ nenv (~> 0.1)
58
+ shellany (~> 0.0)
59
+ pry (0.10.4)
60
+ coderay (~> 1.1.0)
61
+ method_source (~> 0.8.1)
62
+ slop (~> 3.4)
63
+ pry-byebug (3.4.0)
64
+ byebug (~> 9.0)
65
+ pry (~> 0.10)
66
+ rake (10.5.0)
67
+ rb-fsevent (0.9.7)
68
+ rb-inotify (0.9.7)
69
+ ffi (>= 0.5.0)
70
+ rspec (3.5.0)
71
+ rspec-core (~> 3.5.0)
72
+ rspec-expectations (~> 3.5.0)
73
+ rspec-mocks (~> 3.5.0)
74
+ rspec-core (3.5.3)
75
+ rspec-support (~> 3.5.0)
76
+ rspec-expectations (3.5.0)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.5.0)
79
+ rspec-mocks (3.5.0)
80
+ diff-lcs (>= 1.2.0, < 2.0)
81
+ rspec-support (~> 3.5.0)
82
+ rspec-support (3.5.0)
83
+ ruby_dep (1.3.1)
84
+ shellany (0.0.1)
85
+ slop (3.6.0)
86
+ sqlite3 (1.3.11)
87
+ thor (0.19.1)
88
+ thread_safe (0.3.5)
89
+ tzinfo (1.2.2)
90
+ thread_safe (~> 0.1)
91
+
92
+ PLATFORMS
93
+ ruby
94
+
95
+ DEPENDENCIES
96
+ activerecord (~> 5.0)
97
+ appraisal
98
+ bundler (~> 1.11)
99
+ database_cleaner
100
+ guard-rspec (~> 4.7)
101
+ nested_attribute_reassignable!
102
+ pry
103
+ pry-byebug
104
+ rake (~> 10.0)
105
+ rspec (~> 3.0)
106
+ sqlite3
107
+
108
+ BUNDLED WITH
109
+ 1.12.5
@@ -0,0 +1,136 @@
1
+ require "nested_attribute_reassignable/version"
2
+ require "active_support/concern"
3
+
4
+ module NestedAttributeReassignable
5
+ class RelationExists < StandardError
6
+ def initialize(model, relation)
7
+ @model = model.class.name.pluralize.underscore
8
+ @relation = relation
9
+ end
10
+
11
+ def message
12
+ <<-STR
13
+ Relation '#{@relation}' already exists on '#{@model}' object but attributes were passed with no id.
14
+
15
+ It is invalid to create a new '#{@relation}' relation when one already exists, as it would leave orphaned records. Update the existing record instead.
16
+ STR
17
+ end
18
+ end
19
+
20
+ class Helper
21
+ def self.has_delete_flag?(hash)
22
+ truthy?(hash, :_delete)
23
+ end
24
+
25
+ def self.has_destroy_flag?(hash)
26
+ truthy?(hash, :_destroy)
27
+ end
28
+
29
+ def self.truthy?(hash, key)
30
+ if defined?(Rails) && Rails::VERSION::MAJOR == 5
31
+ ActiveRecord::Type::Boolean.new.cast(hash[key])
32
+ else
33
+ value = hash[key]
34
+ [true, 1, '1', 'true'].include?(value)
35
+ end
36
+ end
37
+
38
+ def self.symbolize_keys!(attributes)
39
+ if attributes.is_a?(Array)
40
+ return unless attributes[0].respond_to?(:symbolize_keys!)
41
+ attributes.each { |a| a.symbolize_keys! }
42
+ else
43
+ return unless attributes.respond_to?(:symbolize_keys!)
44
+ attributes.symbolize_keys!
45
+ end
46
+ end
47
+ end
48
+
49
+ extend ActiveSupport::Concern
50
+
51
+ # Yes, this could use refactoring love, I do not
52
+ # have time right now D:
53
+ # Just go by the tests.
54
+ module ClassMethods
55
+ def reassignable_nested_attributes_for(association_name, *args)
56
+ options = args.extract_options!.symbolize_keys
57
+ options.update({ :allow_destroy => true })
58
+
59
+ accepts_nested_attributes_for(association_name, options.except(:lookup_key))
60
+
61
+ define_method "#{association_name}_attributes=" do |attributes|
62
+ reflection_klass = self.class._reflect_on_association(association_name)
63
+ association_klass = reflection_klass.klass
64
+ association = association(association_name)
65
+ lookup_key = options[:lookup_key] || association.klass.primary_key
66
+
67
+ Helper.symbolize_keys!(attributes)
68
+
69
+ if attributes.is_a?(Array)
70
+ id_attribute_sets = attributes.select { |a| a.has_key?(:id) }
71
+
72
+ ids = attributes.map { |a| a[:id] }
73
+ children = association_klass.where(lookup_key => ids)
74
+
75
+ # If we're deleting or destroying, we want to validate the record in question
76
+ # is actually part of this relationship
77
+ if id_attribute_sets.any? { |set| Helper.has_destroy_flag?(set) || Helper.has_delete_flag?(set) }
78
+ existing_associated = association.scope.where(lookup_key => ids)
79
+ end
80
+
81
+ id_attribute_sets.each do |id_attributes|
82
+ if existing_record = children.find { |c| c.send(lookup_key).to_s == id_attributes[:id].to_s }
83
+ if Helper.has_destroy_flag?(id_attributes)
84
+ if record = existing_associated.find { |e| e.send(lookup_key).to_s == id_attributes[:id].to_s }
85
+ record.mark_for_destruction
86
+ association.add_to_target(record, :skip_callbacks)
87
+ else
88
+ raise_nested_attributes_record_not_found!(association_name, id_attributes[:id])
89
+ end
90
+ elsif Helper.has_delete_flag?(id_attributes)
91
+ if record = existing_associated.find { |e| e.send(lookup_key).to_s == id_attributes[:id].to_s }
92
+ association.add_to_target(record, :skip_callbacks)
93
+ send(association_name).delete(record)
94
+ else
95
+ raise_nested_attributes_record_not_found!(association_name, id_attributes[:id])
96
+ end
97
+ else
98
+ id_attributes[lookup_key] = id_attributes[:id]
99
+ existing_record.assign_attributes(id_attributes.except(:id))
100
+ self.send(association_name).concat(existing_record)
101
+ end
102
+ else
103
+ raise_nested_attributes_record_not_found!(association_name, id_attributes[:id])
104
+ end
105
+ end
106
+ non_id_attribute_sets = attributes.reject { |a| a.has_key?(:id) }
107
+ non_id_attribute_sets.each do |non_id_attributes|
108
+ self.send(association_name).build(non_id_attributes)
109
+ end
110
+ self.association(association_name).loaded!
111
+ else
112
+ if attributes[:id]
113
+ if Helper.has_destroy_flag?(attributes)
114
+ self.send(association_name).mark_for_destruction
115
+ elsif Helper.has_delete_flag?(attributes)
116
+ send("#{association_name}=", nil)
117
+ elsif existing_record = association_klass.find_by(lookup_key => attributes[:id])
118
+ attributes[lookup_key] = attributes.delete(:id)
119
+ existing_record.assign_attributes(attributes)
120
+ self.send("#{association_name}=", existing_record)
121
+ else
122
+ raise_nested_attributes_record_not_found!(association_name, attributes[:id])
123
+ end
124
+ else
125
+ reflection = self.class._reflect_on_association(association_name)
126
+ if reflection.has_one? and send(association_name).present?
127
+ raise RelationExists.new(self, association_name)
128
+ else
129
+ super(attributes)
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,3 @@
1
+ module NestedAttributeReassignable
2
+ VERSION = "0.6.2"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nested_attribute_reassignable/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "nested_attribute_reassignable"
8
+ spec.version = NestedAttributeReassignable::VERSION
9
+ spec.authors = ["Lee Richmond"]
10
+ spec.email = ["lrichmond1@bloomberg.net"]
11
+
12
+ spec.summary = %q{Allows accepts_nested_attributes_for to accept preexisting records}
13
+ spec.description = %q{Have an unpersisted base object, and nested_attributes_for already-persisted assocations}
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency 'activerecord', [">= 4.1", "< 6"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.11"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_development_dependency "database_cleaner"
26
+ spec.add_development_dependency "sqlite3"
27
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nested_attribute_reassignable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.2
5
+ platform: ruby
6
+ authors:
7
+ - Lee Richmond
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.11'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.11'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: database_cleaner
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: sqlite3
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ description: Have an unpersisted base object, and nested_attributes_for already-persisted
104
+ assocations
105
+ email:
106
+ - lrichmond1@bloomberg.net
107
+ executables: []
108
+ extensions: []
109
+ extra_rdoc_files: []
110
+ files:
111
+ - ".gitignore"
112
+ - ".rspec"
113
+ - ".travis.yml"
114
+ - Appraisals
115
+ - Gemfile
116
+ - Guardfile
117
+ - README.md
118
+ - Rakefile
119
+ - bin/appraisal
120
+ - bin/console
121
+ - bin/rspec
122
+ - bin/setup
123
+ - gemfiles/activerecord_4.gemfile
124
+ - gemfiles/activerecord_4.gemfile.lock
125
+ - gemfiles/activerecord_5.gemfile
126
+ - gemfiles/activerecord_5.gemfile.lock
127
+ - lib/nested_attribute_reassignable.rb
128
+ - lib/nested_attribute_reassignable/version.rb
129
+ - nested_attribute_reassignable.gemspec
130
+ homepage:
131
+ licenses: []
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 2.4.5.1
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Allows accepts_nested_attributes_for to accept preexisting records
153
+ test_files: []
154
+ has_rdoc: