mcfly 0.0.7 → 0.0.10
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.
- 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: []
|