mcfly 0.0.7 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/mcfly/constraint.sql +4 -0
- data/lib/mcfly/delete_trig.sql +4 -1
- data/lib/mcfly/has_mcfly.rb +30 -6
- data/lib/mcfly/insert_trig.sql +1 -6
- data/lib/mcfly/migration.rb +18 -3
- data/lib/mcfly/update_append_only_trig.sql +1 -1
- data/lib/mcfly/update_trig.sql +2 -7
- data/lib/mcfly/version.rb +1 -1
- data/lib/mcfly.rb +1 -0
- data/mcfly.gemspec +1 -1
- data/spec/no_trans_spec.rb +45 -0
- metadata +6 -4
data/lib/mcfly/delete_trig.sql
CHANGED
@@ -4,6 +4,7 @@ AS $$
|
|
4
4
|
|
5
5
|
DECLARE
|
6
6
|
whodunnit int;
|
7
|
+
now timestamp;
|
7
8
|
|
8
9
|
BEGIN
|
9
10
|
IF OLD.obsoleted_dt <> 'infinity' THEN
|
@@ -12,8 +13,10 @@ BEGIN
|
|
12
13
|
|
13
14
|
SHOW mcfly.whodunnit INTO whodunnit;
|
14
15
|
|
16
|
+
now = now();
|
17
|
+
|
15
18
|
UPDATE "%{table}"
|
16
|
-
SET
|
19
|
+
SET obsoleted_dt = now, o_user_id = whodunnit WHERE id = OLD.id;
|
17
20
|
|
18
21
|
RETURN NULL; -- the row is not actually deleted
|
19
22
|
END;
|
data/lib/mcfly/has_mcfly.rb
CHANGED
@@ -25,14 +25,10 @@ module Mcfly
|
|
25
25
|
|
26
26
|
module ClassMethods
|
27
27
|
def has_mcfly(options = {})
|
28
|
-
# FIXME: this methods gets a append_only option sometimes. It
|
29
|
-
# needs to add model level validations which prevent update
|
30
|
-
# when this option is present. Note that we need to allow
|
31
|
-
# delete. Deletion of Mcfly objects obsoletes them by setting
|
32
|
-
# obsoleted_dt.
|
33
|
-
|
34
28
|
send :include, InstanceMethods
|
29
|
+
|
35
30
|
before_validation :record_validation
|
31
|
+
before_destroy :allow_destroy if options[:append_only]
|
36
32
|
|
37
33
|
# FIXME: :created_dt should also be readonly. However, we set
|
38
34
|
# it for debugging purposes. Should consider making this
|
@@ -77,6 +73,19 @@ module Mcfly
|
|
77
73
|
def mcfly_belongs_to(name, options = {})
|
78
74
|
validates_with Mcfly::Model::AssociationValidator, field: name
|
79
75
|
belongs_to(name, options)
|
76
|
+
|
77
|
+
# Store child associations for the parent category
|
78
|
+
# e.g. if HedgeCost is adding a belong_to assoc to HedgeCostCategory
|
79
|
+
# then add HedgeCost and FK to the @@associations array
|
80
|
+
self.reflect_on_all_associations.each do |a|
|
81
|
+
if a.name == name
|
82
|
+
a.klass.class_variable_set(:@@associations, []) unless
|
83
|
+
a.klass.class_variable_defined?(:@@associations)
|
84
|
+
|
85
|
+
a.klass.class_variable_get(:@@associations) <<
|
86
|
+
[a.active_record, a.foreign_key]
|
87
|
+
end
|
88
|
+
end
|
80
89
|
end
|
81
90
|
|
82
91
|
end
|
@@ -87,8 +96,23 @@ module Mcfly
|
|
87
96
|
self.user_id = Mcfly.whodunnit.try(:id)
|
88
97
|
self.obsoleted_dt ||= 'infinity'
|
89
98
|
end
|
99
|
+
end
|
90
100
|
|
101
|
+
def allow_destroy
|
102
|
+
# checks against registered associations
|
103
|
+
if self.class.class_variable_defined?(:@@associations)
|
104
|
+
self.class.class_variable_get(:@@associations).each do |klass, fk|
|
105
|
+
self.errors.add :base,
|
106
|
+
"#{self.class.name.demodulize} cannot be deleted " +
|
107
|
+
"because #{klass.name.demodulize} records exist" if
|
108
|
+
klass.where("obsoleted_dt = 'infinity' and
|
109
|
+
#{fk} = ?", self.id).count > 0
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
self.errors.blank?
|
91
114
|
end
|
115
|
+
|
92
116
|
end
|
93
117
|
|
94
118
|
end
|
data/lib/mcfly/insert_trig.sql
CHANGED
@@ -15,7 +15,7 @@ BEGIN
|
|
15
15
|
-- is only useful for debugging. Consider removing the surronding
|
16
16
|
-- IF for production version.
|
17
17
|
IF NEW.created_dt IS NULL THEN
|
18
|
-
NEW.created_dt =
|
18
|
+
NEW.created_dt = now();
|
19
19
|
END IF;
|
20
20
|
|
21
21
|
RETURN NEW;
|
@@ -25,8 +25,3 @@ $$ LANGUAGE plpgsql;
|
|
25
25
|
DROP TRIGGER IF EXISTS %{table}_insert ON %{table};
|
26
26
|
CREATE TRIGGER "%{table}_insert" BEFORE INSERT ON "%{table}" FOR EACH ROW
|
27
27
|
EXECUTE PROCEDURE "%{table}_insert"();
|
28
|
-
|
29
|
-
-- Add constraint to make sure o_user_id is set iff obsoleted_dt is
|
30
|
-
-- not infinity (i.e. object is obsoleted).
|
31
|
-
ALTER TABLE "%{table}" ADD CONSTRAINT check_o_user
|
32
|
-
CHECK ((obsoleted_dt = 'Infinity') = (o_user_id IS NULL));
|
data/lib/mcfly/migration.rb
CHANGED
@@ -1,11 +1,26 @@
|
|
1
1
|
class McflyMigration < ActiveRecord::Migration
|
2
|
-
INSERT_TRIG, UPDATE_TRIG, UPDATE_APPEND_ONLY_TRIG, DELETE_TRIG =
|
3
|
-
%w{
|
2
|
+
INSERT_TRIG, UPDATE_TRIG, UPDATE_APPEND_ONLY_TRIG, DELETE_TRIG, CONSTRAINT =
|
3
|
+
%w{
|
4
|
+
insert_trig
|
5
|
+
update_trig
|
6
|
+
update_append_only_trig
|
7
|
+
delete_trig
|
8
|
+
constraint
|
9
|
+
}.map { |f|
|
4
10
|
File.read(File.dirname(__FILE__) + "/#{f}.sql")
|
5
11
|
}
|
6
12
|
|
7
13
|
TRIGS = [INSERT_TRIG, UPDATE_TRIG, DELETE_TRIG]
|
8
14
|
|
15
|
+
def add_sql(table_name, include_const)
|
16
|
+
sql_list = self.class::TRIGS +
|
17
|
+
(include_const ? [self.class::CONSTRAINT] : [])
|
18
|
+
|
19
|
+
sql_list.each { |sql|
|
20
|
+
execute sql % {table: table_name}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
9
24
|
def create_table(table_name, options = {}, &block)
|
10
25
|
super { |t|
|
11
26
|
t.integer :group_id, null: false
|
@@ -18,7 +33,7 @@ class McflyMigration < ActiveRecord::Migration
|
|
18
33
|
block.call(t)
|
19
34
|
}
|
20
35
|
|
21
|
-
|
36
|
+
add_sql(table_name, true)
|
22
37
|
end
|
23
38
|
end
|
24
39
|
|
@@ -12,8 +12,8 @@ BEGIN
|
|
12
12
|
-- obsoleted. We return the OLD row so that other field updates are
|
13
13
|
-- ignored. This is used by DELETE.
|
14
14
|
IF NEW.obsoleted_dt <> 'infinity' THEN
|
15
|
-
OLD.obsoleted_dt = NEW.obsoleted_dt;
|
16
15
|
OLD.o_user_id = NEW.o_user_id;
|
16
|
+
OLD.obsoleted_dt = NEW.obsoleted_dt;
|
17
17
|
return OLD;
|
18
18
|
END IF;
|
19
19
|
|
data/lib/mcfly/update_trig.sql
CHANGED
@@ -4,7 +4,6 @@ AS $$
|
|
4
4
|
DECLARE
|
5
5
|
rec "%{table}";
|
6
6
|
new_id INT4;
|
7
|
-
now timestamp;
|
8
7
|
whodunnit int;
|
9
8
|
|
10
9
|
BEGIN
|
@@ -27,10 +26,6 @@ BEGIN
|
|
27
26
|
-- new_id is a new primary key that we'll use for the obsoleted row.
|
28
27
|
SELECT nextval('"%{table}_id_seq"') INTO new_id;
|
29
28
|
|
30
|
-
-- not sure if PGSQL will return the same value for now() in the
|
31
|
-
-- same transaction. So, use the same variable to be sure.
|
32
|
-
now = 'now()';
|
33
|
-
|
34
29
|
rec.id = new_id;
|
35
30
|
rec.group_id = NEW.id;
|
36
31
|
rec.o_user_id = NEW.user_id;
|
@@ -42,8 +37,8 @@ BEGIN
|
|
42
37
|
IF NEW.created_dt = OLD.created_dt THEN
|
43
38
|
-- Set the modified row's created_dt. The obsoleted_dt field was
|
44
39
|
-- already infinity, so we don't need to set it.
|
45
|
-
NEW.created_dt = now;
|
46
|
-
rec.obsoleted_dt = now;
|
40
|
+
NEW.created_dt = now();
|
41
|
+
rec.obsoleted_dt = now();
|
47
42
|
ELSE
|
48
43
|
IF NEW.created_dt <= OLD.created_dt THEN
|
49
44
|
RAISE EXCEPTION 'new created_dt must be greater than old';
|
data/lib/mcfly/version.rb
CHANGED
data/lib/mcfly.rb
CHANGED
data/mcfly.gemspec
CHANGED
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Mcfly" do
|
4
|
+
self.use_transactional_fixtures = false
|
5
|
+
|
6
|
+
after(:each) do
|
7
|
+
ActiveRecord::Base.connection.execute("TRUNCATE security_instruments;")
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
Mcfly.whodunnit = TestUser.new(10)
|
12
|
+
|
13
|
+
@dts = ['2001-01-01', '2001-01-05', '2001-01-10']
|
14
|
+
|
15
|
+
@sis = [
|
16
|
+
["FN Fix-30 MBS", "A"],
|
17
|
+
["FN Fix-30 Cash", "A"],
|
18
|
+
["FN Fix-30 MBS Blend", "A"],
|
19
|
+
["FN Fix-30 HB MBS", "A"],
|
20
|
+
]
|
21
|
+
|
22
|
+
@sis.each_with_index { |(name, sc), i|
|
23
|
+
si = SecurityInstrument.new(name: name, settlement_class: sc)
|
24
|
+
si.created_dt = @dts[i % @dts.length]
|
25
|
+
si.save!
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
it "deleted append-only records should have reasonable obsoleted_dt" do
|
30
|
+
@sis[0..3].each { |name, st|
|
31
|
+
osi = SecurityInstrument.find_by_name(name)
|
32
|
+
osi.destroy
|
33
|
+
# p ActiveRecord::Base.connection.execute("select now();").first
|
34
|
+
sleep(2)
|
35
|
+
}
|
36
|
+
t = Time.now
|
37
|
+
|
38
|
+
deltas = @sis[0..3].map { |name, st|
|
39
|
+
(t - SecurityInstrument.find_by_name(name).obsoleted_dt).round
|
40
|
+
}
|
41
|
+
|
42
|
+
deltas.should == [8, 6, 4, 2]
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mcfly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 3.2.
|
21
|
+
version: 3.2.13
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 3.2.
|
29
|
+
version: 3.2.13
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: pg
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- README.md
|
106
106
|
- Rakefile
|
107
107
|
- lib/mcfly.rb
|
108
|
+
- lib/mcfly/constraint.sql
|
108
109
|
- lib/mcfly/controller.rb
|
109
110
|
- lib/mcfly/delete_trig.sql
|
110
111
|
- lib/mcfly/has_mcfly.rb
|
@@ -154,6 +155,7 @@ files:
|
|
154
155
|
- spec/dummy/public/favicon.ico
|
155
156
|
- spec/dummy/script/rails
|
156
157
|
- spec/model_spec.rb
|
158
|
+
- spec/no_trans_spec.rb
|
157
159
|
- spec/spec_helper.rb
|
158
160
|
homepage: https://github.com/arman000/mcfly
|
159
161
|
licenses: []
|