embeds_many 0.2.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba71f51a1cc397e9552e7df72bd48f850f6b94f1
4
- data.tar.gz: d2a3a9e1d58f919d9151203c1a742f2cb30046be
3
+ metadata.gz: 3283d364282e4492da35bb5ab7ceb436641cebc1
4
+ data.tar.gz: e3b66d7c419d5c3ddd44c3d190a6d96b5ef74757
5
5
  SHA512:
6
- metadata.gz: 6f28e15a662b00e961dd31a3fc2cd3dafc0e02d4542c7f70244d6338dac273122e518de8a15098f1e63ba2f1230fc15c6d0a3baa91f5df8434310c2c3c04babb
7
- data.tar.gz: d348e086153200946b6e644ca9c047c1f2784f7770ecf9a0361b3ce68061638ef4416351b09ed145b07dd66d7e3f9ef0d7712b7ceeb0b054ffaff9711d3cba1b
6
+ metadata.gz: deca54390c5ed088b1150c68563c3116809161022f8f608aebba17d8cae0b6c96668be0f5581f3e07f0104812d0cfc10e8d18c7008518cbf4e8f82ac9b3ba759
7
+ data.tar.gz: 30904b474ae33b80e9aa54f5e431bea1c8cacea9af7f3100950df920411d92601251d38e669e7d9818ce7e1b719a4ce3ef912dfcadcadadff968936a9783ce5c
@@ -102,6 +102,22 @@ end
102
102
  @tag.destroy
103
103
  ```
104
104
 
105
+ Embedded record are automatically saved on parent save. But invalid embedded records are not auto saved.
106
+
107
+ ``` ruby
108
+ # auto save on parent save
109
+ user = User.new(name: 'test')
110
+ user.tags.new(tag_params)
111
+
112
+ user.save
113
+
114
+ # auto save on parent update
115
+ @tag = user.tags.find(params['id'])
116
+ @tag.color = 'red'
117
+
118
+ user.update(name: 'bob')
119
+ ```
120
+
105
121
  ## Development
106
122
 
107
123
  All pull requests are welcome.
@@ -10,6 +10,8 @@ module EmbedsMany
10
10
  instance_variable_set("@#{field}_collection", ChildrenCollection.new(self, field, child_klass))
11
11
  end
12
12
 
13
+ before_save { |parent| parent.send(field).before_parent_save }
14
+
13
15
  child_klass.class_eval(&block) if block
14
16
  end
15
17
  end
@@ -22,7 +22,7 @@ module EmbedsMany
22
22
 
23
23
  class UniquenessValidator < ActiveModel::EachValidator
24
24
  def validate_each(record, attribute, value)
25
- if record.exists_in_parent? {|item| item.key?(attribute.to_s) && item[attribute.to_s] == value && record.id.to_s != item['id'] }
25
+ if record.exists_in_parent? {|item| item.key?(attribute.to_s) && item[attribute.to_s] == value && record.id.to_s != item['id'].to_s }
26
26
  record.errors.add attribute, "#{value} is already taken"
27
27
  end
28
28
  end
@@ -48,11 +48,15 @@ module EmbedsMany
48
48
  def save
49
49
  return false unless self.valid?
50
50
 
51
+ @operation_pending = true
52
+
51
53
  if new_record?
52
54
  save_new_record!
53
55
  else
54
56
  save_existing_record!
55
57
  end
58
+ ensure
59
+ @operation_pending = false
56
60
  end
57
61
 
58
62
  def destroy
@@ -61,12 +65,18 @@ module EmbedsMany
61
65
 
62
66
  parent.read_attribute(field_name).delete_if {|t| t['id'] == self.id}
63
67
 
64
- if parent.save
68
+ @operation_pending = true
69
+
70
+ if parent.update(field_name => parent.read_attribute(field_name))
71
+ parent.send(field_name).child_destroyed(self)
72
+
65
73
  true
66
74
  else
67
75
  parent.send "#{field_name}=", parent.send("#{field_name}_was")
68
76
  false
69
77
  end
78
+ ensure
79
+ @operation_pending = false
70
80
  end
71
81
 
72
82
  def update(attrs)
@@ -83,17 +93,39 @@ module EmbedsMany
83
93
  parent.read_attribute(field_name).any? &block
84
94
  end
85
95
 
96
+ def before_parent_save
97
+ return if @operation_pending or !self.valid?
98
+
99
+ if new_record?
100
+ fill_parent_field_new_record!
101
+ else
102
+ fill_parent_field_existing_record!
103
+ end
104
+ end
105
+
86
106
  private
87
107
 
88
- def save_new_record!
108
+ def fill_parent_field_new_record!
89
109
  @attributes[:id] = generate_id!
90
110
 
91
111
  # tell rails the field will change
92
112
  parent.send "#{field_name}_will_change!"
93
113
 
94
114
  parent.read_attribute(field_name) << @attributes.to_hash
115
+ end
95
116
 
96
- if parent.save
117
+ def fill_parent_field_existing_record!
118
+ # tell rails the field will change
119
+ parent.send "#{field_name}_will_change!"
120
+
121
+ record = parent.read_attribute(field_name).detect {|t| t['id'].to_i == self.id.to_i }
122
+ record.merge!(@attributes)
123
+ end
124
+
125
+ def save_new_record!
126
+ fill_parent_field_new_record!
127
+
128
+ if parent.update(field_name => parent.read_attribute(field_name))
97
129
  true
98
130
  else
99
131
  @attributes.id = nil
@@ -104,13 +136,9 @@ module EmbedsMany
104
136
  end
105
137
 
106
138
  def save_existing_record!
107
- # tell rails the field will change
108
- parent.send "#{field_name}_will_change!"
109
-
110
- record = parent.read_attribute(field_name).detect {|t| t['id'].to_i == self.id.to_i }
111
- record.merge!(@attributes)
139
+ fill_parent_field_existing_record!
112
140
 
113
- if parent.save
141
+ if parent.update(field_name => parent.read_attribute(field_name))
114
142
  true
115
143
  else
116
144
  # restore old value
@@ -1,7 +1,10 @@
1
1
  module EmbedsMany
2
2
  class ChildrenCollection
3
3
  def new(attrs={})
4
- @child_klass.new(attrs.merge(parent: @obj))
4
+ record = @child_klass.new(attrs.merge(parent: @obj))
5
+ @created_instances << record
6
+
7
+ record
5
8
  end
6
9
 
7
10
  def create(attrs={})
@@ -9,6 +12,8 @@ module EmbedsMany
9
12
 
10
13
  record.save
11
14
 
15
+ @created_instances << record
16
+
12
17
  record
13
18
  end
14
19
 
@@ -16,12 +21,18 @@ module EmbedsMany
16
21
  @obj = obj
17
22
  @field = field
18
23
  @child_klass = child_klass
24
+ @created_instances = []
19
25
  end
20
26
 
21
27
  def find(id)
22
28
  attrs = @obj.read_attribute(@field).find {|child| child['id'].to_i == id.to_i }
23
29
 
24
- attrs && @child_klass.new(attrs.merge(parent: @obj))
30
+ if attrs
31
+ record = @child_klass.new(attrs.merge(parent: @obj))
32
+ @created_instances << record
33
+
34
+ record
35
+ end
25
36
  end
26
37
 
27
38
  # all records
@@ -31,9 +42,19 @@ module EmbedsMany
31
42
  end
32
43
  end
33
44
 
45
+ # called before parent save
46
+ def before_parent_save
47
+ @created_instances.map(&:before_parent_save)
48
+ end
49
+
50
+ # child destroyed
51
+ def child_destroyed(child)
52
+ @created_instances.delete(child)
53
+ end
54
+
34
55
  # pass unhandled message to children array
35
56
  def method_missing(symbol, *args, &block)
36
57
  all.send(symbol, *args, &block)
37
58
  end
38
59
  end
39
- end
60
+ end
@@ -1,3 +1,3 @@
1
1
  module EmbedsMany
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -44,25 +44,92 @@ describe User do
44
44
  tag.save.should be_false
45
45
  tag.errors[:color].should_not be_empty
46
46
  end
47
+
48
+ it "should auto save new tags on parent update" do
49
+ tag = user.tags.new(name: 'bug', color: 'red')
50
+
51
+ user.update(name: 'liufengyun')
52
+
53
+ user.reload.tags.any? {|t| t.name == 'bug'}.should be_true
54
+ end
55
+
56
+ it "should not auto save invalid new tags on parent update" do
57
+ tag = user.tags.new(name: 'bug')
58
+
59
+ user.save
60
+
61
+ user.reload.tags.any? {|t| t.name == 'bug'}.should be_false
62
+ end
63
+
64
+ it "should auto save new tags on parent create" do
65
+ new_user = User.new(name: 'user')
66
+ tag = new_user.tags.new(name: 'bug', color: 'red')
67
+
68
+ new_user.save
69
+
70
+ new_user.reload.tags.any? {|t| t.name == 'bug'}.should be_true
71
+ end
72
+
73
+ it "should not auto save invalid new tags on parent create" do
74
+ new_user = User.new(name: 'user')
75
+ tag = new_user.tags.new(name: 'bug')
76
+
77
+ new_user.save
78
+
79
+ new_user.reload.tags.any? {|t| t.name == 'bug'}.should be_false
80
+ end
47
81
  end
48
82
 
83
+ describe "update" do
84
+ it "should be able to update record" do
85
+ tag = user.tags.create(name: 'bug', color: 'red')
86
+
87
+ user.reload.tags.any? {|t| t.name == 'bug'}.should be_true
49
88
 
50
- it "should be able to update record" do
51
- tag = user.tags.create(name: 'bug', color: 'red')
89
+ tag.update(color: 'yellow').should be_true
90
+
91
+ user.reload.tags.any? {|t| t.color == 'yellow'}.should be_true
92
+ end
93
+
94
+ it "should auto save tag on parent update" do
95
+ tag = user.tags.create(name: 'bug', color: 'red')
96
+ tag.color = 'yellow'
97
+
98
+ user.update(name: 'liufengyun')
99
+
100
+ user.reload.tags.any? {|t| t.color == 'yellow'}.should be_true
101
+ end
52
102
 
53
- user.reload.tags.any? {|t| t.name == 'bug'}.should be_true
103
+ it "should not auto save invalid tag on parent save" do
104
+ tag = user.tags.create(name: 'bug', color: 'red')
54
105
 
55
- tag.update(color: 'yellow').should be_true
106
+ tag.color = 'yellow'
107
+ tag.name = ''
56
108
 
57
- user.reload.tags.any? {|t| t.color == 'yellow'}.should be_true
109
+ user.update(name: 'liufengyun')
110
+
111
+ user.reload.tags.any? {|t| t.color == 'yellow'}.should be_false
112
+ end
58
113
  end
59
114
 
60
- it "should be able to destroy" do
61
- tag = user.tags.create(name: 'bug', color: 'red')
115
+ describe "destroy" do
116
+ it "should be able to destroy" do
117
+ tag = user.tags.create(name: 'bug', color: 'red')
118
+
119
+ tag.destroy.should be_true
120
+
121
+ user.reload.tags.any? {|t| t.name == 'bug'}.should be_false
122
+ end
123
+
124
+ it "should not be saved on parent save after destroy" do
125
+ tag = user.tags.create(name: 'bug', color: 'red')
126
+ tag.destroy
62
127
 
63
- tag.destroy.should be_true
128
+ user.update(name: 'liufengyun')
64
129
 
65
- user.reload.tags.any? {|t| t.name == 'bug'}.should be_false
130
+ user.reload.tags.any? {|t| t.color == 'yellow'}.should be_false
131
+ end
66
132
  end
133
+
67
134
  end
68
135
  end
metadata CHANGED
@@ -1,83 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embeds_many
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - liufengyun
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-29 00:00:00.000000000 Z
11
+ date: 2014-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 4.0.4
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 4.0.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: database_cleaner
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pg
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  description: EmbedsMany allows programmers to work with embedded records the same
@@ -88,9 +88,9 @@ executables: []
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
- - .gitignore
92
- - .rspec
93
- - .travis.yml
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
94
  - Gemfile
95
95
  - LICENSE
96
96
  - README.markdown
@@ -114,17 +114,17 @@ require_paths:
114
114
  - lib
115
115
  required_ruby_version: !ruby/object:Gem::Requirement
116
116
  requirements:
117
- - - '>='
117
+ - - ">="
118
118
  - !ruby/object:Gem::Version
119
119
  version: '0'
120
120
  required_rubygems_version: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '>='
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  requirements: []
126
126
  rubyforge_project:
127
- rubygems_version: 2.0.3
127
+ rubygems_version: 2.2.2
128
128
  signing_key:
129
129
  specification_version: 4
130
130
  summary: Embedded records based on the power of PostgreSQL's hstore and array