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.
@@ -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: []