sneaky-save 0.1.0 → 0.1.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 +13 -5
- data/.travis.yml +15 -4
- data/gemfiles/{rails_3.2 → rails_3_2} +0 -0
- data/gemfiles/rails_5 +7 -0
- data/lib/sneaky-save.rb +59 -46
- data/sneaky-save.gemspec +3 -3
- data/spec/lib/sneaky_save_spec.rb +55 -33
- data/spec/spec_helper.rb +17 -7
- metadata +14 -13
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjU4Y2NiYjczZmQwNTMxMTk1Y2IzMzRmZTk2MzIwOTFlN2RlNDc3MA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OWZkMzVhZjE2NjdjY2Q5ZGJiMWZiMDFhYjhmOTI0NmRjNTMwNzBkNA==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZGY1MzIzN2QyZDlhZDk2ODI0ZWQxMTc2N2MyN2VmMGRkODUzZDAyNGJjY2Fj
|
10
|
+
ZWQ3MzcyMmI1ZDI4ZWNiZDk4ZWViYTM3MjFhZjA2NmQ2MzQ0OTViZjFlNDlk
|
11
|
+
NTU3MDUxMTAwOWEwZTAxYTEzNTgwNTQ1NzZlZjRkOTc2NjFkODg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Mzg5ZTFjZGNkMTVmM2E4OWYzNTNmMzkyZjMyZjliZWVlMGI0MGY3YzBmN2E1
|
14
|
+
ZGI3ZWJiMmE0MzJkZDUwNTkwYmIyZjU5MzFhMjk1YTJjNDg5M2I3YzFmZjlh
|
15
|
+
YjNlYmViMDRlZTQxZTc4NmM1MGQwNWVmNzIzMTMwZTViNDY5MzM=
|
data/.travis.yml
CHANGED
@@ -1,9 +1,20 @@
|
|
1
|
+
script: bundle exec rake -t
|
1
2
|
language: ruby
|
2
3
|
rvm:
|
3
|
-
- "
|
4
|
-
- "2.
|
5
|
-
|
4
|
+
- "2.2.2"
|
5
|
+
- "2.3.1"
|
6
|
+
matrix:
|
7
|
+
include:
|
8
|
+
- rvm: 1.9.3
|
9
|
+
gemfile: gemfiles/rails_3_2
|
10
|
+
- rvm: 2.0.0
|
11
|
+
gemfile: gemfiles/rails_3_2
|
12
|
+
- rvm: 2.1.2
|
13
|
+
gemfile: gemfiles/rails_3_2
|
14
|
+
- rvm: 2.2.0
|
15
|
+
gemfile: gemfiles/rails_3_2
|
6
16
|
gemfile:
|
7
17
|
- Gemfile
|
18
|
+
- gemfiles/rails_3_2
|
8
19
|
- gemfiles/rails_4
|
9
|
-
- gemfiles/
|
20
|
+
- gemfiles/rails_5
|
File without changes
|
data/gemfiles/rails_5
ADDED
data/lib/sneaky-save.rb
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
#++
|
5
5
|
module SneakySave
|
6
6
|
|
7
|
-
# Saves record without running callbacks/validations.
|
8
|
-
# Returns true if
|
7
|
+
# Saves the record without running callbacks/validations.
|
8
|
+
# Returns true if the record is changed.
|
9
9
|
# @note - Does not reload updated record by default.
|
10
10
|
# - Does not save associated collections.
|
11
11
|
# - Saves only belongs_to relations.
|
@@ -19,71 +19,84 @@ module SneakySave
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
# Saves
|
22
|
+
# Saves record without running callbacks/validations.
|
23
|
+
# @see ActiveRecord::Base#sneaky_save
|
23
24
|
# @return [true] if save was successful.
|
24
|
-
# @raise [ActiveRecord::StatementInvalid] if
|
25
|
+
# @raise [ActiveRecord::StatementInvalid] if saving failed.
|
25
26
|
def sneaky_save!
|
26
27
|
sneaky_create_or_update
|
27
28
|
end
|
28
29
|
|
29
30
|
protected
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
def sneaky_create_or_update
|
33
|
+
new_record? ? sneaky_create : sneaky_update
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
self.id = sneaky_connection.next_sequence_value(self.class.sequence_name)
|
40
|
-
end
|
36
|
+
# Performs INSERT query without running any callbacks
|
37
|
+
# @return [false, true]
|
38
|
+
def sneaky_create
|
39
|
+
prefetch_pk_allowed = sneaky_connection.prefetch_primary_key?(self.class.table_name)
|
41
40
|
|
42
|
-
|
41
|
+
if id.nil? && prefetch_pk_allowed
|
42
|
+
self.id = sneaky_connection.next_sequence_value(self.class.sequence_name)
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
if self.id.nil? && !sneaky_connection.prefetch_primary_key?(self.class.table_name)
|
46
|
-
attributes_values.reject! { |key,_| key.name == 'id' }
|
47
|
-
end
|
45
|
+
attributes_values = sneaky_attributes_values
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
# Remove the id field for databases like Postgres
|
48
|
+
# which fail with id passed as NULL
|
49
|
+
if id.nil? && !prefetch_pk_allowed
|
50
|
+
attributes_values.reject! { |key, _| key.name == 'id' }
|
51
|
+
end
|
54
52
|
|
55
|
-
|
56
|
-
|
53
|
+
if attributes_values.empty?
|
54
|
+
new_id = self.class.unscoped.insert(sneaky_connection.empty_insert_statement_value)
|
55
|
+
else
|
56
|
+
new_id = self.class.unscoped.insert(attributes_values)
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
@new_record = false
|
60
|
+
!!(self.id ||= new_id)
|
61
|
+
end
|
62
62
|
|
63
|
-
|
64
|
-
|
63
|
+
# Performs update query without running callbacks
|
64
|
+
# @return [false, true]
|
65
|
+
def sneaky_update
|
66
|
+
return true if changes.empty?
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
pk = self.class.primary_key
|
69
|
+
original_id = changed_attributes.key?(pk) ? changes[pk].first : send(pk)
|
70
|
+
|
71
|
+
!self.class.where(pk => original_id).
|
72
|
+
update_all(sneaky_update_fields).zero?
|
73
|
+
end
|
74
|
+
|
75
|
+
def sneaky_attributes_values
|
76
|
+
if sneaky_new_rails?
|
77
|
+
send :arel_attributes_with_values_for_create, attribute_names
|
78
|
+
else
|
79
|
+
send :arel_attributes_values
|
70
80
|
end
|
81
|
+
end
|
71
82
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
else
|
76
|
-
send :arel_attributes_values
|
77
|
-
end
|
83
|
+
def sneaky_update_fields
|
84
|
+
changes.keys.each_with_object({}) do |field, result|
|
85
|
+
result[field] = read_attribute(field)
|
78
86
|
end
|
87
|
+
end
|
79
88
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
89
|
+
def sneaky_connection
|
90
|
+
if sneaky_new_rails?
|
91
|
+
self.class.connection
|
92
|
+
else
|
93
|
+
connection
|
86
94
|
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def sneaky_new_rails?
|
98
|
+
ActiveRecord::VERSION::STRING.to_i > 3
|
99
|
+
end
|
87
100
|
end
|
88
101
|
|
89
102
|
ActiveRecord::Base.send :include, SneakySave
|
data/sneaky-save.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{sneaky-save}
|
5
|
-
s.version = '0.1.
|
5
|
+
s.version = '0.1.2'
|
6
6
|
|
7
|
-
s.date = %q{
|
7
|
+
s.date = %q{2016-08-06}
|
8
8
|
s.authors = ["Sergei Zinin (einzige)"]
|
9
|
-
s.email = %q{
|
9
|
+
s.email = %q{szinin@gmail.com}
|
10
10
|
s.homepage = %q{http://github.com/einzige/sneaky-save}
|
11
11
|
|
12
12
|
s.licenses = ["MIT"]
|
@@ -1,88 +1,110 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe SneakySave, use_connection: true do
|
4
|
-
context
|
5
|
-
subject { Fake.new(name:
|
4
|
+
context "new record" do
|
5
|
+
subject { Fake.new(name: "test") }
|
6
6
|
|
7
|
-
describe
|
8
|
-
it
|
7
|
+
describe "#sneaky_save" do
|
8
|
+
it "returns true if everything is good" do
|
9
9
|
expect(subject.sneaky_save).to eq(true)
|
10
10
|
end
|
11
11
|
|
12
|
-
it
|
12
|
+
it "inserts the new record" do
|
13
13
|
allow(subject).to receive(:sneaky_create).once.and_return(true)
|
14
14
|
expect(subject.sneaky_save).to eq(true)
|
15
15
|
end
|
16
16
|
|
17
|
-
it
|
18
|
-
subject.name =
|
17
|
+
it "stores attributes in database" do
|
18
|
+
subject.name = "test"
|
19
19
|
expect(subject.sneaky_save).to eq(true)
|
20
20
|
subject.reload
|
21
|
-
expect(subject.name).to eq(
|
21
|
+
expect(subject.name).to eq("test")
|
22
22
|
end
|
23
23
|
|
24
|
-
it
|
25
|
-
Fake.
|
24
|
+
it "does not call any callback" do
|
25
|
+
expect_any_instance_of(Fake).not_to receive(:before_save_callback)
|
26
26
|
subject.sneaky_save
|
27
27
|
end
|
28
28
|
|
29
|
-
it
|
29
|
+
it "does not call validations" do
|
30
30
|
expect_any_instance_of(Fake).to_not receive(:valid?)
|
31
31
|
subject.sneaky_save
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
describe
|
36
|
-
it
|
35
|
+
describe "#sneaky_save!" do
|
36
|
+
it "raises an exception when insert fails" do
|
37
37
|
subject.name = nil
|
38
38
|
expect { subject.sneaky_save! }.to raise_error(ActiveRecord::StatementInvalid)
|
39
39
|
end
|
40
|
+
|
41
|
+
context "associations" do
|
42
|
+
let(:belonger) { Belonger.create! }
|
43
|
+
|
44
|
+
it "stores associations" do
|
45
|
+
subject.belonger = belonger
|
46
|
+
subject.sneaky_save!
|
47
|
+
subject.reload
|
48
|
+
expect(subject.belonger_id).to eq(belonger.id)
|
49
|
+
end
|
50
|
+
end
|
40
51
|
end
|
41
52
|
end
|
42
53
|
|
43
|
-
context
|
44
|
-
subject { Fake.create!
|
54
|
+
context "existing record" do
|
55
|
+
subject { Fake.create!(name: "test") }
|
45
56
|
|
46
|
-
describe
|
47
|
-
context
|
48
|
-
it
|
49
|
-
Fake.
|
57
|
+
describe "#sneaky_save" do
|
58
|
+
context "record is not changed" do
|
59
|
+
it "does nothing" do
|
60
|
+
expect(Fake).not_to receive(:update_all)
|
50
61
|
expect(subject.sneaky_save).to eq(true)
|
51
62
|
end
|
52
63
|
end
|
53
64
|
|
54
|
-
context
|
55
|
-
it
|
56
|
-
subject.
|
57
|
-
subject.name =
|
65
|
+
context "record is changed" do
|
66
|
+
it "updates attributes" do
|
67
|
+
expect(subject).to receive(:sneaky_update).once.and_return true
|
68
|
+
subject.name = "new name"
|
58
69
|
expect(subject.sneaky_save).to eq(true)
|
59
70
|
end
|
60
71
|
|
61
|
-
it
|
62
|
-
subject.name =
|
72
|
+
it "stores attributes in database" do
|
73
|
+
subject.name = "new name"
|
63
74
|
expect(subject.sneaky_save).to eq(true)
|
64
75
|
subject.reload
|
65
|
-
expect(subject.name).to eq(
|
76
|
+
expect(subject.name).to eq("new name")
|
66
77
|
expect(subject.sneaky_save).to eq(true)
|
67
78
|
end
|
68
79
|
|
69
|
-
it
|
70
|
-
subject.
|
80
|
+
it "does not call any callback" do
|
81
|
+
expect(subject).not_to receive(:before_save_callback)
|
71
82
|
subject.sneaky_save
|
72
83
|
end
|
73
84
|
|
74
|
-
it
|
75
|
-
subject.
|
85
|
+
it "does not call validations" do
|
86
|
+
expect(subject).not_to receive(:valid?)
|
76
87
|
subject.sneaky_save
|
77
88
|
end
|
78
89
|
end
|
79
90
|
end
|
80
91
|
|
81
|
-
describe
|
82
|
-
it
|
92
|
+
describe "#sneaky_save!" do
|
93
|
+
it "raises an exception when update fails" do
|
83
94
|
subject.name = nil
|
84
95
|
expect { subject.sneaky_save! }.to raise_error(ActiveRecord::StatementInvalid)
|
85
96
|
end
|
97
|
+
|
98
|
+
context "associations" do
|
99
|
+
let(:belonger) { Belonger.create! }
|
100
|
+
|
101
|
+
it "stores associations" do
|
102
|
+
subject.belonger = belonger
|
103
|
+
subject.sneaky_save!
|
104
|
+
subject.reload
|
105
|
+
expect(subject.belonger_id).to eq(belonger.id)
|
106
|
+
end
|
107
|
+
end
|
86
108
|
end
|
87
109
|
end
|
88
110
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,23 +1,33 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "active_record"
|
2
|
+
require "sqlite3"
|
3
|
+
require "sneaky-save"
|
4
4
|
|
5
|
-
shared_context
|
6
|
-
ActiveRecord::Base.establish_connection(adapter:
|
5
|
+
shared_context "use connection", use_connection: true do
|
6
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
7
7
|
|
8
8
|
ActiveRecord::Schema.define do
|
9
|
-
create_table
|
9
|
+
create_table "fakes" do |table|
|
10
10
|
table.column :name, :string, null: false
|
11
|
+
table.column :belonger_id, :integer
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table "belongers" do |table|
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
18
|
+
class Belonger < ActiveRecord::Base
|
19
|
+
has_many :fakes
|
20
|
+
end
|
21
|
+
|
14
22
|
class Fake < ActiveRecord::Base
|
15
23
|
validates :name, presence: true
|
16
24
|
|
17
25
|
before_save :before_save_callback
|
18
26
|
|
27
|
+
belongs_to :belonger
|
28
|
+
|
19
29
|
def before_save_callback
|
20
|
-
|
30
|
+
"BEFORE SAVE CALLED"
|
21
31
|
end
|
22
32
|
end
|
23
33
|
end
|
metadata
CHANGED
@@ -1,59 +1,60 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sneaky-save
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergei Zinin (einzige)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-08-06 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: 3.2.0
|
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: 3.2.0
|
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
|
description: ActiveRecord extension. Allows to save record without calling callbacks
|
42
42
|
and validations.
|
43
|
-
email:
|
43
|
+
email: szinin@gmail.com
|
44
44
|
executables: []
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files:
|
47
47
|
- README.md
|
48
48
|
files:
|
49
|
-
-
|
50
|
-
-
|
49
|
+
- .gitignore
|
50
|
+
- .travis.yml
|
51
51
|
- Gemfile
|
52
52
|
- README.md
|
53
53
|
- Rakefile
|
54
54
|
- VERSION
|
55
|
-
- gemfiles/
|
55
|
+
- gemfiles/rails_3_2
|
56
56
|
- gemfiles/rails_4
|
57
|
+
- gemfiles/rails_5
|
57
58
|
- lib/sneaky-save.rb
|
58
59
|
- sneaky-save.gemspec
|
59
60
|
- spec/lib/sneaky_save_spec.rb
|
@@ -68,17 +69,17 @@ require_paths:
|
|
68
69
|
- lib
|
69
70
|
required_ruby_version: !ruby/object:Gem::Requirement
|
70
71
|
requirements:
|
71
|
-
- -
|
72
|
+
- - ! '>='
|
72
73
|
- !ruby/object:Gem::Version
|
73
74
|
version: '0'
|
74
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
76
|
requirements:
|
76
|
-
- -
|
77
|
+
- - ! '>='
|
77
78
|
- !ruby/object:Gem::Version
|
78
79
|
version: '0'
|
79
80
|
requirements: []
|
80
81
|
rubyforge_project:
|
81
|
-
rubygems_version: 2.
|
82
|
+
rubygems_version: 2.1.5
|
82
83
|
signing_key:
|
83
84
|
specification_version: 4
|
84
85
|
summary: Allows to save record without calling callbacks and validations.
|