mcfly 0.0.7 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ -- Add constraint to make sure o_user_id is set iff obsoleted_dt is
2
+ -- not infinity (i.e. object is obsoleted).
3
+ ALTER TABLE "%{table}" ADD CONSTRAINT check_o_user
4
+ CHECK ((obsoleted_dt = 'Infinity') = (o_user_id IS NULL));
@@ -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 "obsoleted_dt" = 'now()', "o_user_id" = whodunnit WHERE id = OLD.id;
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;
@@ -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
@@ -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 = 'now()';
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));
@@ -1,11 +1,26 @@
1
1
  class McflyMigration < ActiveRecord::Migration
2
- INSERT_TRIG, UPDATE_TRIG, UPDATE_APPEND_ONLY_TRIG, DELETE_TRIG =
3
- %w{insert_trig update_trig update_append_only_trig delete_trig}.map { |f|
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
- self.class::TRIGS.each {|sql| execute sql % {table: table_name}}
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
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Mcfly
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.10"
3
3
  end
data/lib/mcfly.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'mcfly/migration'
2
2
  require 'mcfly/has_mcfly'
3
3
  require 'mcfly/controller'
4
+ require 'mcfly/version'
4
5
  require 'active_support'
5
6
 
6
7
  module Mcfly
data/mcfly.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.require_paths = ["lib"]
17
17
 
18
- s.add_dependency "rails", "~> 3.2.11"
18
+ s.add_dependency "rails", "~> 3.2.13"
19
19
  s.add_dependency "pg"
20
20
 
21
21
  # FIXME: Delorean is added here for historical reasons. It should
@@ -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.7
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-09-10 00:00:00.000000000 Z
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.11
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.11
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: []