low_card_tables 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d4726bf4540f04b58951e79af67b8285c96dd72a
4
- data.tar.gz: 7441e6bc347ab79f261d2c8f0f7750ffc158d7f9
3
+ metadata.gz: 930fa1a9dafbd8e32e72961f024beb1754c13254
4
+ data.tar.gz: 034fc44ed55f2ede181f09edaf7e7d27b494fe5d
5
5
  SHA512:
6
- metadata.gz: 4bfb76855df51f47da3d892abfb1902f28ad4e28fdba754f16c2b575459e31c0b4ce07edac02a5695e8806bbb9fc7e7fc0c3076239f707dacd8a5069496f63cf
7
- data.tar.gz: 443b4f355b6e785909d5984fe74257c28564b8f0efe63163c87410f0c14fe7c9ae364e80f63ed046e35a65afa7b287ad9c5adddcfcbfe37399775659bbfbbe22
6
+ metadata.gz: 5013b79fe4902ec65b05914c92b33c6330873144bfff2bf73eb0710fc0efde02f41d16d15afa2bd81bf9cf22053ca66de28d6a38e38cc17606ec9b83ef7b59c6
7
+ data.tar.gz: 38ae46cc876311d8e15b6a6616439955d2d696b7e1e72b9f23790e8eb79fcce3b1f3a0f31e15b604d7824b58365da71c6efc5341bc7a0d438a6e5784232ec3b1
@@ -1,9 +1,10 @@
1
1
  rvm:
2
2
  - "1.8.7"
3
- - "1.9.3"
4
- - "2.0.0"
5
- - "2.1.2"
6
- - "jruby-1.7.15"
3
+ - "1.9.3-p551"
4
+ - "2.0.0-p598"
5
+ - "2.1.5"
6
+ - "2.2.0"
7
+ - "jruby-1.7.19"
7
8
  env:
8
9
  # Sadly, Travis seems to have a version of SQLite < 3.7.11 installed on many of its workers;
9
10
  # this prevents activerecord-import from working, since those versions of the SQLite engine
@@ -15,65 +16,88 @@ env:
15
16
  - LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
16
17
  - LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
17
18
  # - LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
18
- - LOW_CARD_TABLES_AR_TEST_VERSION=3.2.19 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
19
- - LOW_CARD_TABLES_AR_TEST_VERSION=3.2.19 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
20
- # - LOW_CARD_TABLES_AR_TEST_VERSION=3.2.19 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
21
- - LOW_CARD_TABLES_AR_TEST_VERSION=4.0.10 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
22
- - LOW_CARD_TABLES_AR_TEST_VERSION=4.0.10 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
23
- # - LOW_CARD_TABLES_AR_TEST_VERSION=4.0.10 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
24
- - LOW_CARD_TABLES_AR_TEST_VERSION=4.1.6 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
25
- - LOW_CARD_TABLES_AR_TEST_VERSION=4.1.6 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
26
- # - LOW_CARD_TABLES_AR_TEST_VERSION=4.1.6 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
19
+ - LOW_CARD_TABLES_AR_TEST_VERSION=3.2.21 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
20
+ - LOW_CARD_TABLES_AR_TEST_VERSION=3.2.21 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
21
+ # - LOW_CARD_TABLES_AR_TEST_VERSION=3.2.21 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
22
+ - LOW_CARD_TABLES_AR_TEST_VERSION=4.0.13 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
23
+ - LOW_CARD_TABLES_AR_TEST_VERSION=4.0.13 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
24
+ # - LOW_CARD_TABLES_AR_TEST_VERSION=4.0.13 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
25
+ - LOW_CARD_TABLES_AR_TEST_VERSION=4.1.9 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
26
+ - LOW_CARD_TABLES_AR_TEST_VERSION=4.1.9 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
27
+ # - LOW_CARD_TABLES_AR_TEST_VERSION=4.1.9 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
28
+ - LOW_CARD_TABLES_AR_TEST_VERSION=4.2.0 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
29
+ - LOW_CARD_TABLES_AR_TEST_VERSION=4.2.0 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
30
+ # - LOW_CARD_TABLES_AR_TEST_VERSION=4.2.0 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
27
31
  before_script:
28
- - export JRUBY_OPTS="-J-Xmx256m -J-Xms256m $JRUBY_OPTS"
32
+ - export JRUBY_OPTS="-J-Xmx512m -J-Xms512m $JRUBY_OPTS"
29
33
  - mysql -e 'create database myapp_test;'
30
34
  - psql -c 'create database myapp_test;' -U postgres
31
35
  matrix:
32
36
  exclude:
33
37
  # ActiveRecord 4.x doesn't support Ruby 1.8.7
34
38
  - rvm: 1.8.7
35
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.10 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
39
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.13 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
36
40
  - rvm: 1.8.7
37
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.10 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
41
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.13 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
38
42
  - rvm: 1.8.7
39
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.10 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
43
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.13 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
40
44
  - rvm: 1.8.7
41
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.6 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
45
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.9 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
42
46
  - rvm: 1.8.7
43
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.6 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
47
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.9 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
44
48
  - rvm: 1.8.7
45
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.6 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
49
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.9 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
50
+ - rvm: 1.8.7
51
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.2.0 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
52
+ - rvm: 1.8.7
53
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.2.0 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
54
+ - rvm: 1.8.7
55
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.2.0 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
46
56
  # There's a bug in ActiveRecord 3.1.x that makes it incompatible with Ruby 2.x
47
- - rvm: 2.0.0
57
+ - rvm: 2.0.0-p598
48
58
  env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
49
- - rvm: 2.0.0
59
+ - rvm: 2.0.0-p598
50
60
  env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
51
- - rvm: 2.0.0
61
+ - rvm: 2.0.0-p598
52
62
  env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
53
- - rvm: 2.1.2
63
+ - rvm: 2.1.5
54
64
  env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
55
- - rvm: 2.1.2
65
+ - rvm: 2.1.5
56
66
  env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
57
- - rvm: 2.1.2
67
+ - rvm: 2.1.5
58
68
  env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
59
- # The activerecord-import gem currently doesn't support JRuby JDBC adapters with anything but MySQL
60
- - rvm: jruby-1.7.15
61
- env: LOW_CARD_TABLES_AR_TEST_VERSION=3.0.20 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
62
- - rvm: jruby-1.7.15
69
+ - rvm: 2.2.0
70
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
71
+ - rvm: 2.2.0
63
72
  env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
64
- - rvm: jruby-1.7.15
65
- env: LOW_CARD_TABLES_AR_TEST_VERSION=3.2.19 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
66
- - rvm: jruby-1.7.15
67
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.10 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
68
- - rvm: jruby-1.7.15
69
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.6 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
70
- - rvm: jruby-1.7.15
71
- env: LOW_CARD_TABLES_AR_TEST_VERSION=3.0.20 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
72
- - rvm: jruby-1.7.15
73
+ - rvm: 2.2.0
73
74
  env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
74
- - rvm: jruby-1.7.15
75
- env: LOW_CARD_TABLES_AR_TEST_VERSION=3.2.19 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
76
- - rvm: jruby-1.7.15
77
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.10 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
78
- - rvm: jruby-1.7.15
79
- env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.6 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
75
+ # The activerecord-import gem currently doesn't support JRuby JDBC adapters with anything but MySQL
76
+ # - rvm: jruby-1.7.15
77
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=3.0.20 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
78
+ # - rvm: jruby-1.7.15
79
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
80
+ # - rvm: jruby-1.7.15
81
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=3.2.21 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
82
+ # - rvm: jruby-1.7.15
83
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.13 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
84
+ # - rvm: jruby-1.7.15
85
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.9 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
86
+ # - rvm: jruby-1.7.15
87
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=3.0.20 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
88
+ # - rvm: jruby-1.7.15
89
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=3.1.12 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
90
+ # - rvm: jruby-1.7.15
91
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=3.2.21 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
92
+ # - rvm: jruby-1.7.15
93
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=4.0.13 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
94
+ # - rvm: jruby-1.7.15
95
+ # env: LOW_CARD_TABLES_AR_TEST_VERSION=4.1.9 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=sqlite
96
+
97
+ allow_failures:
98
+ # As of this writing (2015-02-18), activerecord-jdbc doesn't support ActiveRecord 4.2.x
99
+ # completely.
100
+ - rvm: jruby-1.7.19
101
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.2.0 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=mysql
102
+ - rvm: jruby-1.7.19
103
+ env: LOW_CARD_TABLES_AR_TEST_VERSION=4.2.0 LOW_CARD_TABLES_TRAVIS_CI_DATABASE_TYPE=postgres
data/CHANGES.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # `low_card_tables` Changelog
2
2
 
3
+ ## 1.1.0, 2015-02-18
4
+
5
+ * The [single-table inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Single+table+inheritance) type-discrimination column &mdash; by default called `type` &mdash; can now be part of a low-cardinality table itself. This elegantly allows you to use STI without consuming the very large amounts of space required by Rails' default implementation, where it stores the name of the class in every single row.
6
+ * Fixed issues where you couldn't use `low_card_tables` with an ActiveRecord class that was at anything but the leaf of a [single-table inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Single+table+inheritance) hierarchy.
7
+ * Ensured that you can query a column containing a string or symbol with a query that's a string or a symbol, and it all works fine.
8
+ * Added support for `WHERE` clauses on a low-card table containing an array (`.where(:my_low_card_column => [ :foo, :bar ])`).
9
+ * Added support for ActiveRecord 4.2.x, and bumped versions on the CI configuration.
10
+
3
11
  ## 1.0.3, 2014-09-22
4
12
 
5
13
  * Fixed an issue where, if a table that owned a low-card table was declared a namespace (_e.g._, `module Foo; class Bar < ActiveRecord::Base; has_low_card_table :status; end; end`), the call to `has_low_card_table` would fail with an error (trying to call `+` on `nil`).
data/Gemfile CHANGED
@@ -15,3 +15,8 @@ end
15
15
  if version_spec
16
16
  gem("activerecord", version_spec)
17
17
  end
18
+
19
+ # gem 'pry'
20
+ # gem 'pry-byebug'
21
+ # gem 'pry-rescue'
22
+ # gem 'pry-stack_explorer'
@@ -60,6 +60,18 @@ module LowCardTables
60
60
  extend ActiveSupport::Concern
61
61
 
62
62
 
63
+ def ensure_proper_type
64
+ if (assn = self.class.association_for_inheritance_column)
65
+ superclass = self.class.superclass
66
+ unless (superclass == ::ActiveRecord::Base) || (superclass.abstract_class?)
67
+ lco = send(assn.association_name)
68
+ lco.send(:write_attribute, self.class.inheritance_column, self.class.sti_name)
69
+ end
70
+ else
71
+ super
72
+ end
73
+ end
74
+
63
75
  module ClassMethods
64
76
  # Several methods go straight to the LowCardAssociationsManager.
65
77
  delegate :has_low_card_table, :_low_card_association, :_low_card_update_collapsed_rows, :low_card_value_collapsing_update_scheme, :to => :_low_card_associations_manager
@@ -70,6 +82,94 @@ module LowCardTables
70
82
  true
71
83
  end
72
84
 
85
+ def association_for_inheritance_column
86
+ _low_card_associations_manager.association_containing_method_named(inheritance_column)
87
+ end
88
+
89
+ def descends_from_active_record?
90
+ out = super
91
+
92
+ if out && (self != ::ActiveRecord::Base) && (! superclass.abstract_class?) && (superclass != ::ActiveRecord::Base)
93
+ out = false if association_for_inheritance_column
94
+ end
95
+
96
+ out
97
+ end
98
+
99
+ def type_condition(table = arel_table)
100
+ if (association = association_for_inheritance_column)
101
+ sti_names = ([self] + descendants).map { |model| model.sti_name }
102
+
103
+ ids = association.low_card_class.low_card_ids_matching(inheritance_column => sti_names).to_a
104
+ table[association.foreign_key_column_name].in(ids)
105
+ else
106
+ if ::ActiveRecord::Base.method(:type_condition).arity.abs >= 1
107
+ super
108
+ else
109
+ super()
110
+ end
111
+ end
112
+ end
113
+
114
+ def discriminate_class_for_record(record, call_super = true)
115
+ if (! record[inheritance_column])
116
+ if (association = association_for_inheritance_column)
117
+ foreign_key = record[association.foreign_key_column_name]
118
+
119
+ foreign_key = association.low_card_class.low_card_type_cast_id(foreign_key)
120
+ low_card_row = association.low_card_class.low_card_row_for_id(foreign_key)
121
+ type = low_card_row.send(inheritance_column)
122
+
123
+ return _low_card_find_sti_class(type) if type
124
+ end
125
+ end
126
+
127
+ super(record) if call_super
128
+ end
129
+
130
+ if ::LowCardTables::VersionSupport.sti_uses_discriminate_class_for_record?
131
+ def _low_card_find_sti_class(type)
132
+ find_sti_class(type)
133
+ end
134
+ else
135
+ def _low_card_find_sti_class(type_name)
136
+ return self if type_name.blank?
137
+
138
+ begin
139
+ if store_full_sti_class
140
+ ActiveSupport::Dependencies.constantize(type_name)
141
+ else
142
+ compute_type(type_name)
143
+ end
144
+ rescue NameError
145
+ raise ::ActiveRecord::SubclassNotFound,
146
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
147
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
148
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
149
+ "or overwrite #{name}.inheritance_column to use another column for that information."
150
+ end
151
+ end
152
+
153
+ def instantiate(record)
154
+ sti_class = discriminate_class_for_record(record, false)
155
+ if sti_class
156
+ record_id = sti_class.primary_key && record[sti_class.primary_key]
157
+
158
+ if defined?(::ActiveRecord::IdentityMap) && ::ActiveRecord::IdentityMap.enabled? && record_id
159
+ instance = use_identity_map(sti_class, record_id, record)
160
+ else
161
+ instance = sti_class.allocate
162
+ instance.init_with('attributes' => record)
163
+ instance
164
+ end
165
+
166
+ instance
167
+ else
168
+ super
169
+ end
170
+ end
171
+ end
172
+
73
173
  # The LowCardAssociationsManager keeps track of which low-card tables this table refers to; see its
74
174
  # documentation for more information.
75
175
  def _low_card_associations_manager
@@ -45,6 +45,13 @@ module LowCardTables
45
45
  @model_class._low_card_dynamic_method_manager.sync_methods!
46
46
  end
47
47
 
48
+ def association_containing_method_named(method_name)
49
+ out = @associations.detect do |low_card_association|
50
+ low_card_association.class_method_name_to_low_card_method_name_map[method_name]
51
+ end
52
+ out || superclass_low_card_associations_manager.try(:association_containing_method_named, method_name)
53
+ end
54
+
48
55
  # Called when someone has called ::ActiveRecord::Base#reset_column_information on the low-card model in question.
49
56
  # This simply tells the LowCardDynamicMethodManager to sync the methods on this model class, thus updating the
50
57
  # set of delegated methods to match the new columns.
@@ -60,7 +67,9 @@ module LowCardTables
60
67
 
61
68
  # Just like _low_card_association, but returns nil when an association is not found, rather than raising an error.
62
69
  def maybe_low_card_association(name)
63
- @associations.detect { |a| a.association_name.to_s.strip.downcase == name.to_s.strip.downcase }
70
+ out = @associations.detect { |a| a.association_name.to_s.strip.downcase == name.to_s.strip.downcase }
71
+ out ||= superclass_low_card_associations_manager.maybe_low_card_association(name) if superclass_low_card_associations_manager
72
+ out
64
73
  end
65
74
 
66
75
  # Updates all foreign keys that the given model_instance has to their correct values, given the set of attributes
@@ -71,6 +80,10 @@ module LowCardTables
71
80
  @associations.each do |association|
72
81
  association.update_foreign_key!(model_instance)
73
82
  end
83
+
84
+ if superclass_low_card_associations_manager
85
+ superclass_low_card_associations_manager.low_card_update_foreign_keys!(model_instance)
86
+ end
74
87
  end
75
88
 
76
89
  DEFAULT_COLLAPSING_UPDATE_VALUE = 10_000
@@ -113,6 +126,20 @@ module LowCardTables
113
126
  end
114
127
  end
115
128
 
129
+ # Returns the LowCardAssociationsManager for the model class's superclass, if there is one. This is used for
130
+ # supporting STI.
131
+ def superclass_low_card_associations_manager
132
+ @superclass_low_card_associations_manager ||= begin
133
+ model_superclass = @model_class.superclass
134
+ if model_superclass.respond_to?(:_low_card_associations_manager)
135
+ model_superclass._low_card_associations_manager
136
+ else
137
+ :none
138
+ end
139
+ end
140
+ @superclass_low_card_associations_manager unless @superclass_low_card_associations_manager == :none
141
+ end
142
+
116
143
  private
117
144
  # Makes sure that +model_instance+ is an instance of the model class this LowCardAssociationsManager is for.
118
145
  def ensure_correct_class!(model_instance)
@@ -32,7 +32,11 @@ module LowCardTables
32
32
 
33
33
  method_data = @method_delegation_map[method_name.to_s]
34
34
  unless method_data
35
- raise NameError, "Whoa -- we're trying to call a delegated low-card method #{method_name.inspect} on #{object}, of class #{object.class}, but somehow the LowCardDynamicMethodManager has no knowledge of that method?!? We know about: #{@method_delegation_map.keys.sort.inspect}"
35
+ if superclass_low_card_dynamic_method_manager
36
+ return superclass_low_card_dynamic_method_manager.run_low_card_method(object, method_name, args)
37
+ else
38
+ raise NameError, "Whoa -- we're trying to call a delegated low-card method #{method_name.inspect} on #{object}, of class #{object.class}, but somehow the LowCardDynamicMethodManager has no knowledge of that method?!? We know about: #{@method_delegation_map.keys.sort.inspect}"
39
+ end
36
40
  end
37
41
 
38
42
  (association, association_method_name) = method_data
@@ -53,6 +57,18 @@ module LowCardTables
53
57
  end
54
58
  end
55
59
 
60
+ # Returns the effective association and method that we delegate the given method to. This is very close to
61
+ # identical to @method_delegation_map; the only difference is that it delegates to the +model_class+'s superclass
62
+ # as appropriate, too.
63
+ def effective_method_delegation_for(method_name)
64
+ method_name = method_name.to_s
65
+ out = @method_delegation_map[method_name]
66
+ if (! out) && superclass_low_card_dynamic_method_manager
67
+ out = superclass_low_card_dynamic_method_manager.effective_method_delegation_for(method_name)
68
+ end
69
+ out
70
+ end
71
+
56
72
  # Given a base ::ActiveRecord::Relation scope (which can of course just be a model class itself), and a set of
57
73
  # query constraints as passed into ::ActiveRecord::Relation#where (which must be a Hash -- for the other forms
58
74
  # of #where, our override of ::ActiveRecord::Relation#where doesn't call this method but just passes through to
@@ -83,7 +99,7 @@ module LowCardTables
83
99
  # to that association; the constraints in the Hash use key names that are the actual low-card column names
84
100
  # (i.e., we translate them from whatever delegated method names were present in the referring class)
85
101
  query_hash.each do |query_key, query_value|
86
- low_card_delegation = @method_delegation_map[query_key.to_s]
102
+ low_card_delegation = effective_method_delegation_for(query_key)
87
103
 
88
104
  # Does this constraint even mention a low-card column or association name?
89
105
  if low_card_delegation
@@ -192,6 +208,20 @@ yourself, using #{association.low_card_class.name}#ids_matching.}
192
208
  @model_class._low_card_associations_manager.associations
193
209
  end
194
210
 
211
+ # Returns the LowCardDynamicMethodManager for the model class's superclass, if there is one. This is used for
212
+ # supporting STI.
213
+ def superclass_low_card_dynamic_method_manager
214
+ @superclass_low_card_dynamic_method_manager ||= begin
215
+ model_superclass = @model_class.superclass
216
+ if model_superclass.respond_to?(:_low_card_dynamic_method_manager)
217
+ model_superclass._low_card_dynamic_method_manager
218
+ else
219
+ :none
220
+ end
221
+ end
222
+ @superclass_low_card_dynamic_method_manager unless @superclass_low_card_dynamic_method_manager == :none
223
+ end
224
+
195
225
  # Makes sure the given object is an instance of the class we're handling dynamic methods for.
196
226
  def ensure_correct_class!(object)
197
227
  unless object.kind_of?(@model_class)
@@ -19,6 +19,7 @@ module LowCardTables
19
19
  # Set up cache-policy inheritance -- see HasCacheExpiration for more details.
20
20
  included do
21
21
  low_card_cache_policy_inherits_from ::LowCardTables
22
+ self.inheritance_column = '_sti_on_low_card_tables_should_never_be_used'
22
23
  end
23
24
 
24
25
  # This method is a critical entry point from the rest of the low-card system. For example, given our usual
@@ -57,7 +58,25 @@ module LowCardTables
57
58
  # instead of #_low_card_row_matches_any_hash? or #_low_card_row_matches_hash?, if its semantics work better for
58
59
  # your purposes, since its behavior will affect those methods as well.
59
60
  def _low_card_column_matches?(key, value)
60
- self[key.to_s] == value
61
+ my_value = self[key.to_s]
62
+
63
+ if value.kind_of?(Array)
64
+ if my_value && my_value.kind_of?(Symbol)
65
+ my_value = my_value.to_s
66
+ end
67
+
68
+ value = value.map { |m| if m.kind_of?(Symbol) then m.to_s else m end }
69
+ value.include?(my_value)
70
+ else
71
+ value_sym = value.kind_of?(Symbol)
72
+ my_value_sym = my_value.kind_of?(Symbol)
73
+
74
+ if (value_sym != my_value_sym) && value && my_value
75
+ my_value.to_s.eql?(value.to_s)
76
+ else
77
+ my_value.eql?(value)
78
+ end
79
+ end
61
80
  end
62
81
 
63
82
  # This method is called from methods like #low_card_rows_matching, when passed a block -- its job is simply to
@@ -173,7 +192,7 @@ on this object, and it will save, but make sure you understand ALL the implicati
173
192
  # Gems or by client code, since many of the names are pretty generic (e.g., +all_rows+).
174
193
  [ :all_rows, :row_for_id, :rows_for_ids, :rows_matching, :ids_matching, :find_ids_for, :find_or_create_ids_for,
175
194
  :find_rows_for, :find_or_create_rows_for, :flush_cache!, :referring_models, :value_column_names, :referred_to_by,
176
- :collapse_rows_and_update_referrers!, :ensure_has_unique_index!, :remove_unique_index! ].each do |delegated_method_name|
195
+ :collapse_rows_and_update_referrers!, :ensure_has_unique_index!, :remove_unique_index!, :type_cast_id ].each do |delegated_method_name|
177
196
  define_method("low_card_#{delegated_method_name}") do |*args|
178
197
  _low_card_row_manager.send(delegated_method_name, *args)
179
198
  end
@@ -115,6 +115,15 @@ module LowCardTables
115
115
  rows_for_ids(id)
116
116
  end
117
117
 
118
+ def type_cast_id(id)
119
+ column = @low_card_model.columns_hash[@low_card_model.primary_key]
120
+ if column.respond_to?(:type_cast_from_database)
121
+ column.type_cast_from_database(id)
122
+ else
123
+ column.type_cast(id)
124
+ end
125
+ end
126
+
118
127
  # Given a single Hash specifying zero or more constraints for low-card rows (i.e., mapping zero or more columns
119
128
  # of the low-card table to specific values for those columns), returns a (possibly empty) Array of IDs of
120
129
  # low-card rows that match those constraints.
@@ -327,7 +336,6 @@ but we got back these rows:
327
336
  column_name = column.name.to_s.strip.downcase
328
337
 
329
338
  use = true
330
- use = false if column.primary
331
339
  use = false if column_names_to_skip.include?(column_name)
332
340
  use
333
341
  end
@@ -531,6 +539,13 @@ equivalent of 'LOCK TABLE'(s) in your database.}
531
539
  # metadata and shouldn't play a direct role in the low-card system).
532
540
  def column_names_to_skip
533
541
  @column_names_to_skip ||= begin
542
+ primary_keys = if @low_card_model.respond_to?(:primary_keys)
543
+ Array(@low_card_model.primary_keys)
544
+ else
545
+ Array(@low_card_model.primary_key)
546
+ end
547
+
548
+ primary_keys +
534
549
  COLUMN_NAMES_TO_ALWAYS_SKIP +
535
550
  Array(@low_card_model.low_card_options[:exclude_column_names] || [ ]).map { |n| n.to_s.strip.downcase }
536
551
  end
@@ -587,7 +602,9 @@ equivalent of 'LOCK TABLE'(s) in your database.}
587
602
  missing = missing.select do |missing_column_name|
588
603
  column = @low_card_model.columns.detect { |c| c.name.to_s.strip.downcase == missing_column_name.to_s.strip.downcase }
589
604
  if column && column.default
590
- hash[column.name] = column.default
605
+ the_default = column.default
606
+ the_default = column.type_cast_from_database(the_default) if column.respond_to?(:type_cast_from_database)
607
+ hash[column.name] = the_default
591
608
  false
592
609
  else
593
610
  true
@@ -1,4 +1,4 @@
1
1
  # Defines the current version of +low_card_tables+.
2
2
  module LowCardTables
3
- VERSION = "1.0.3"
3
+ VERSION = "1.1.0"
4
4
  end
@@ -23,6 +23,14 @@ module LowCardTables
23
23
  (::ActiveRecord::VERSION::MAJOR <= 3 && ::ActiveRecord::VERSION::MINOR == 0)
24
24
  end
25
25
 
26
+ def sti_uses_discriminate_class_for_record?
27
+ ::ActiveRecord::VERSION::MAJOR >= 4
28
+ end
29
+
30
+ def allows_static_scopes?
31
+ (::ActiveRecord::VERSION::MAJOR < 4) || (::ActiveRecord::VERSION::MINOR < 2)
32
+ end
33
+
26
34
  # Define a default scope on the class in question. This is only actually used from our specs.
27
35
  def define_default_scope(klass, conditions)
28
36
  if default_scopes_accept_a_block?
@@ -21,12 +21,6 @@ Gem::Specification.new do |s|
21
21
  s.add_development_dependency "rake"
22
22
  s.add_development_dependency "rspec", "~> 2.14"
23
23
 
24
- if (RUBY_VERSION =~ /^1\.9\./ || RUBY_VERSION =~ /^2\.0\./) && ((! defined?(RUBY_ENGINE)) || (RUBY_ENGINE != 'jruby'))
25
- s.add_development_dependency "pry"
26
- s.add_development_dependency "pry-debugger"
27
- s.add_development_dependency "pry-stack_explorer"
28
- end
29
-
30
24
  ar_version = ENV['LOW_CARD_TABLES_AR_TEST_VERSION']
31
25
  ar_version = ar_version.strip if ar_version
32
26
 
@@ -44,6 +38,7 @@ Gem::Specification.new do |s|
44
38
 
45
39
  ar_import_version = case ar_version
46
40
  when nil then nil
41
+ when /^4\.2\./ then '~> 0.7.0'
47
42
  when 'master', /^4\.0\./, /^4\.1\./ then '~> 0.4.1'
48
43
  when /^3\.0\./ then '~> 0.2.11'
49
44
  when /^3\.1\./, /^3\.2\./ then '~> 0.3.1'
@@ -56,13 +51,21 @@ Gem::Specification.new do |s|
56
51
  s.add_dependency("activerecord-import")
57
52
  end
58
53
 
54
+ # i18n released an 0.7.0 that's incompatible with Ruby 1.8.
55
+ if RUBY_VERSION =~ /^1\.8\./
56
+ s.add_development_dependency 'i18n', '< 0.7.0'
57
+ end
58
+
59
59
  require File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'low_card_tables', 'helpers', 'database_helper'))
60
60
  database_gem_name = LowCardTables::Helpers::DatabaseHelper.maybe_database_gem_name
61
61
 
62
62
  # Ugh. Later versions of the 'mysql2' gem are incompatible with AR 3.0.x; so, here, we explicitly trap that case
63
63
  # and use an earlier version of that Gem.
64
64
  if database_gem_name && database_gem_name == 'mysql2' && ar_version && ar_version =~ /^3\.0\./
65
- s.add_development_dependency('mysql2', '~> 0.2.0')
65
+ s.add_development_dependency(database_gem_name, '~> 0.2.0')
66
+ # The 'pg' gem removed Ruby 1.8 compatibility as of 0.18.
67
+ elsif database_gem_name && database_gem_name == 'pg' && RUBY_VERSION =~ /^1\.8\./
68
+ s.add_development_dependency(database_gem_name, '< 0.18.0')
66
69
  else
67
70
  s.add_development_dependency(database_gem_name)
68
71
  end
@@ -79,6 +79,28 @@ module LowCardTables
79
79
  :encoding => 'utf8'
80
80
  }
81
81
  }
82
+ when 'postgres', 'postgresql'
83
+ {
84
+ :require => 'activerecord-jdbcpostgresql-adapter',
85
+ :database_gem_name => 'activerecord-jdbcpostgresql-adapter',
86
+ :config => {
87
+ :adapter => 'postgresql',
88
+ :database => 'myapp_test',
89
+ :username => 'travis',
90
+ :encoding => 'utf8'
91
+ }
92
+ }
93
+ when 'sqlite'
94
+ {
95
+ :require => 'activerecord-jdbcsqlite3-adapter',
96
+ :database_gem_name => 'activerecord-jdbcsqlite3-adapter',
97
+ :config => {
98
+ :adapter => 'jdbcsqlite3',
99
+ :database => 'myapp_test',
100
+ :username => 'travis',
101
+ :encoding => 'utf8'
102
+ }
103
+ }
82
104
  when '', nil then nil
83
105
  else
84
106
  raise "Unknown Travis CI database type: #{dbtype.inspect}"
@@ -129,7 +151,7 @@ module LowCardTables
129
151
  end
130
152
 
131
153
  def invalid_config_file!
132
- raise Errno::ENOENT, %{In order to run specs for LowCardTables, you need to create a file at:
154
+ raise InvalidDatabaseConfigurationError, %{In order to run specs for LowCardTables, you need to create a file at:
133
155
 
134
156
  #{config_file_path}
135
157
 
@@ -16,12 +16,13 @@ module LowCardTables
16
16
  LowCardTables::VersionSupport.clear_schema_cache!(::ActiveRecord::Base)
17
17
  end
18
18
 
19
- def define_model_class(name, table_name, &block)
20
- model_class = Class.new(::ActiveRecord::Base)
19
+ def define_model_class(name, table_name, options = { }, &block)
20
+ superclass = options[:superclass] || ::ActiveRecord::Base
21
+ model_class = Class.new(superclass)
21
22
  ::Object.send(:remove_const, name) if ::Object.const_defined?(name)
22
23
  ::Object.const_set(name, model_class)
23
- model_class.table_name = table_name
24
- model_class.class_eval(&block)
24
+ model_class.table_name = table_name if table_name
25
+ model_class.class_eval(&block) if block
25
26
  end
26
27
 
27
28
  def create_standard_system_spec_tables!
@@ -64,6 +64,20 @@ describe "LowCardTables basic operations" do
64
64
  user1_v2.donation_level.should == 3
65
65
  end
66
66
 
67
+ it "should let you set options via a scope, and create those correctly in #new" do
68
+ pending "This does not work yet, and it will require a major refactor of low_card_tables"
69
+ scope = User.where(:deleted => false, :deceased => true, :gender => 'male', :donation_level => 7)
70
+ user2 = scope.new
71
+ user2.name = 'User2'
72
+ user2.save!
73
+
74
+ user2_again = User.where(:name => 'User2').first
75
+ expect(user2_again.deleted).to be_false
76
+ expect(user2_again.deceased).to be_true
77
+ expect(user2_again.gender).to eq('male')
78
+ expect(user2_again.donation_level).to eq(7)
79
+ end
80
+
67
81
  it "should expose a low-card row, but not with an ID, when read in from the DB" do
68
82
  @user1.status.should be
69
83
  @user1.status.id.should_not be
@@ -101,9 +101,9 @@ describe "LowCardTables association options" do
101
101
  user1.deceased = false
102
102
  user1.gender = 'female'
103
103
 
104
- start_time = Time.now.to_i
104
+ start_time = Time.now.to_f.floor
105
105
  user1.save!
106
- end_time = Time.now.to_i
106
+ end_time = Time.now.to_f.ceil
107
107
 
108
108
  ::UserStatusExcludeCreatedUpdated.count.should == 1
109
109
  ::UserStatusExcludeCreatedUpdated.first.created_at.to_i.should >= start_time
@@ -126,6 +126,7 @@ describe "LowCardTables association options" do
126
126
  ::UserStatusExcludeCreatedUpdated.first.updated_at.to_i.should >= start_time
127
127
  ::UserStatusExcludeCreatedUpdated.first.updated_at.to_i.should <= end_time
128
128
  end
129
+
129
130
  context "with standard setup" do
130
131
  before :each do
131
132
  create_standard_system_spec_tables!
@@ -55,6 +55,17 @@ describe "LowCardTables query support" do
55
55
  check_user_ids(::User.where(:deleted => false, :name => 'User2'), [ ])
56
56
  end
57
57
 
58
+ it "should allow 'where' clauses that use an array" do
59
+ check_user_ids(::User.where(:donation_level => [ 8, 10 ]), [ @user1, @user2, @user3, @user4, @user5 ])
60
+ end
61
+
62
+ it "should allow 'where' clauses to use a symbol or a string, and work fine either way" do
63
+ check_user_ids(::User.where(:gender => :male), [ @user4 ])
64
+ check_user_ids(::User.where(:gender => 'male'), [ @user4 ])
65
+ check_user_ids(::User.where(:gender => [ :male, :female ]), [ @user1, @user2, @user3, @user4, @user5 ])
66
+ check_user_ids(::User.where(:gender => [ 'male', 'female' ]), [ @user1, @user2, @user3, @user4, @user5 ])
67
+ end
68
+
58
69
  it "should allow 'where' clauses that use delegated properties with differently-prefixed names directly" do
59
70
  define_model_class(:User2, :lctables_spec_users) { has_low_card_table :status, :prefix => :foo, :class => ::UserStatus, :foreign_key => :user_status_id }
60
71
 
@@ -114,29 +125,31 @@ describe "LowCardTables query support" do
114
125
  check_user_ids(::User.bar, [ @user1, @user2, @user3, @user5, @user6 ])
115
126
  end
116
127
 
117
- it "should blow up if you constrain on low-card foreign keys in a static scope" do
118
- lambda do
119
- LowCardTables::VersionSupport.define_default_scope(::User, nil)
120
- class ::User < ::ActiveRecord::Base
121
- scope :foo, where(:deleted => false)
122
- end
123
- end.should raise_error(LowCardTables::Errors::LowCardStaticScopeError, /user_status_id/mi)
128
+ if ::LowCardTables::VersionSupport.allows_static_scopes?
129
+ it "should blow up if you constrain on low-card foreign keys in a static scope" do
130
+ lambda do
131
+ LowCardTables::VersionSupport.define_default_scope(::User, nil)
132
+ class ::User < ::ActiveRecord::Base
133
+ scope :foo, where(:deleted => false)
134
+ end
135
+ end.should raise_error(LowCardTables::Errors::LowCardStaticScopeError, /user_status_id/mi)
136
+
137
+ lambda do
138
+ LowCardTables::VersionSupport.define_default_scope(::User, nil)
139
+ class ::User < ::ActiveRecord::Base
140
+ scope :foo, where(:user_status_id => ::UserStatus.low_card_ids_matching(:deleted => false))
141
+ end
142
+ end.should raise_error(LowCardTables::Errors::LowCardStaticScopeError, /user_status_id/mi)
143
+ end
144
+
124
145
 
125
- lambda do
146
+ it "should not blow up if you constrain on other things in a static scope" do
126
147
  LowCardTables::VersionSupport.define_default_scope(::User, nil)
127
148
  class ::User < ::ActiveRecord::Base
128
- scope :foo, where(:user_status_id => ::UserStatus.low_card_ids_matching(:deleted => false))
149
+ scope :foo, where(:name => %w{foo bar})
129
150
  end
130
- end.should raise_error(LowCardTables::Errors::LowCardStaticScopeError, /user_status_id/mi)
131
- end
132
-
133
151
 
134
- it "should not blow up if you constrain on other things in a static scope" do
135
- LowCardTables::VersionSupport.define_default_scope(::User, nil)
136
- class ::User < ::ActiveRecord::Base
137
- scope :foo, where(:name => %w{foo bar})
152
+ ::User.first.should be
138
153
  end
139
-
140
- ::User.first.should be
141
154
  end
142
155
  end
@@ -0,0 +1,178 @@
1
+ require 'low_card_tables'
2
+ require 'low_card_tables/helpers/database_helper'
3
+ require 'low_card_tables/helpers/system_helpers'
4
+
5
+ describe "LowCardTables STI support" do
6
+ include LowCardTables::Helpers::SystemHelpers
7
+
8
+ before :each do
9
+ @dh = LowCardTables::Helpers::DatabaseHelper.new
10
+ @dh.setup_activerecord!
11
+ end
12
+
13
+ context "with the 'type' column defined in a low-card table" do
14
+ before :each do
15
+ migrate do
16
+ drop_table :lctables_spec_account_statuses rescue nil
17
+ create_table :lctables_spec_account_statuses, :low_card => true do |t|
18
+ t.boolean :deleted, :null => false
19
+ t.string :type
20
+ t.integer :account_level
21
+ end
22
+
23
+ drop_table :lctables_spec_accounts rescue nil
24
+ create_table :lctables_spec_accounts do |t|
25
+ t.string :name, :null => false
26
+ t.integer :account_status_id, :null => false, :limit => 2
27
+ end
28
+ end
29
+
30
+ define_model_class(:AccountStatus, 'lctables_spec_account_statuses') { is_low_card_table }
31
+ define_model_class(:Account, 'lctables_spec_accounts') { has_low_card_table :status; self.inheritance_column = :type }
32
+ define_model_class(:AdminAccount, nil, :superclass => ::Account)
33
+ define_model_class(:AccountStatusBackdoor, 'lctables_spec_account_statuses') { self.inheritance_column = :_disabled_ }
34
+ end
35
+
36
+ it "should work normally" do
37
+ account1 = ::Account.new
38
+ account1.name = "account 1"
39
+ account1.deleted = false
40
+ account1.account_level = 10
41
+ account1.save!
42
+
43
+ account2 = ::AdminAccount.new
44
+ account2.name = "account 2"
45
+ account2.deleted = true
46
+ account2.account_level = 20
47
+ account2.save!
48
+
49
+ account1_again = ::Account.find(account1.id)
50
+ expect(account1_again.class).to eq(::Account)
51
+ expect(account1_again.name).to eq('account 1')
52
+ expect(account1_again.deleted).to eq(false)
53
+ expect(account1_again.account_level).to eq(10)
54
+
55
+ account2_again = ::Account.find(account2.id)
56
+ expect(account2_again.class).to eq(::AdminAccount)
57
+ expect(account2_again.name).to eq('account 2')
58
+ expect(account2_again.deleted).to eq(true)
59
+ expect(account2_again.account_level).to eq(20)
60
+
61
+ all_accounts = ::Account.all.to_a
62
+ expect(all_accounts.length).to eq(2)
63
+ account1 = all_accounts.detect { |a| a.id == account1.id }
64
+ account2 = all_accounts.detect { |a| a.id == account2.id }
65
+
66
+ admin_accounts = ::AdminAccount.all.to_a
67
+ expect(admin_accounts.length).to eq(1)
68
+ expect(admin_accounts.first.id).to eq(account2.id)
69
+
70
+ expect(account1.class.name).to eq('Account')
71
+ expect(account2.class.name).to eq('AdminAccount')
72
+
73
+ expect(::AccountStatusBackdoor.count).to eq(2)
74
+ asid = account1.account_status_id
75
+ status1 = ::AccountStatusBackdoor.find(asid)
76
+ expect(status1.deleted).to eq(false)
77
+ expect(status1.type).to eq(nil)
78
+ expect(status1.account_level).to eq(10)
79
+ status2 = ::AccountStatusBackdoor.find(account2.account_status_id)
80
+ expect(status2.deleted).to eq(true)
81
+ expect(status2.type).to eq('AdminAccount')
82
+ expect(status2.account_level).to eq(20)
83
+ end
84
+ end
85
+
86
+ context "with a normal table that uses STI, and has a low-card table" do
87
+ before :each do
88
+ migrate do
89
+ drop_table :lctables_spec_account_statuses rescue nil
90
+ create_table :lctables_spec_account_statuses, :low_card => true do |t|
91
+ t.boolean :deleted, :null => false
92
+ t.integer :account_level
93
+ end
94
+
95
+ drop_table :lctables_spec_accounts rescue nil
96
+ create_table :lctables_spec_accounts do |t|
97
+ t.string :name, :null => false
98
+ t.string :type
99
+ t.integer :account_status_id, :null => false, :limit => 2
100
+ end
101
+ end
102
+
103
+ define_model_class(:AccountStatus, 'lctables_spec_account_statuses') { is_low_card_table }
104
+ define_model_class(:Account, 'lctables_spec_accounts') { has_low_card_table :status; self.inheritance_column = :type }
105
+ define_model_class(:AccountStatusBackdoor, 'lctables_spec_account_statuses') { }
106
+ end
107
+
108
+ it "should work normally with the base table" do
109
+ account1 = ::Account.new
110
+ account1.name = "account 1"
111
+ account1.deleted = false
112
+ account1.account_level = 10
113
+ account1.save!
114
+
115
+ account2 = ::Account.new
116
+ account2.name = "account 2"
117
+ account2.deleted = true
118
+ account2.account_level = 20
119
+ account2.save!
120
+
121
+ account1_again = ::Account.find(account1.id)
122
+ expect(account1_again.name).to eq('account 1')
123
+ expect(account1_again.deleted).to eq(false)
124
+ expect(account1_again.account_level).to eq(10)
125
+
126
+ account2_again = ::Account.find(account2.id)
127
+ expect(account2_again.name).to eq('account 2')
128
+ expect(account2_again.deleted).to eq(true)
129
+ expect(account2_again.account_level).to eq(20)
130
+
131
+ expect(::AccountStatusBackdoor.count).to eq(2)
132
+ status1 = ::AccountStatusBackdoor.find(account1.account_status_id)
133
+ expect(status1.deleted).to eq(false)
134
+ expect(status1.account_level).to eq(10)
135
+ status2 = ::AccountStatusBackdoor.find(account2.account_status_id)
136
+ expect(status2.deleted).to eq(true)
137
+ expect(status2.account_level).to eq(20)
138
+ end
139
+
140
+ it "should work normally with the base table and a derived table" do
141
+ define_model_class(:AdminAccount, nil, :superclass => ::Account)
142
+
143
+ account1 = ::Account.new
144
+ account1.name = "account 1"
145
+ account1.deleted = false
146
+ account1.account_level = 10
147
+ account1.save!
148
+
149
+ account2 = ::AdminAccount.new
150
+ account2.name = "account 2"
151
+ account2.deleted = true
152
+ account2.account_level = 20
153
+ account2.save!
154
+
155
+ account1_again = ::Account.find(account1.id)
156
+ expect(account1_again.class).to eq(::Account)
157
+ expect(account1_again.name).to eq('account 1')
158
+ expect(account1_again.deleted).to eq(false)
159
+ expect(account1_again.account_level).to eq(10)
160
+
161
+ account2_again = ::Account.find(account2.id)
162
+ expect(account2_again.class.name).to eq('AdminAccount')
163
+ expect(account2_again.name).to eq('account 2')
164
+ expect(account2_again.deleted).to eq(true)
165
+ expect(account2_again.account_level).to eq(20)
166
+
167
+ expect(::AccountStatusBackdoor.count).to eq(2)
168
+ status1 = ::AccountStatusBackdoor.find(account1.account_status_id)
169
+ expect(status1.deleted).to eq(false)
170
+ expect(status1.account_level).to eq(10)
171
+ status2 = ::AccountStatusBackdoor.find(account2.account_status_id)
172
+ expect(status2.deleted).to eq(true)
173
+ expect(status2.account_level).to eq(20)
174
+
175
+ expect(::AdminAccount.where(:deleted => true).first.id).to eq(account2.id)
176
+ end
177
+ end
178
+ end
@@ -4,6 +4,7 @@ describe LowCardTables::ActiveRecord::Base do
4
4
  before :each do
5
5
  @klass = Class.new
6
6
  @klass.send(:include, LowCardTables::ActiveRecord::Base)
7
+ allow(@klass).to receive(:inheritance_column=).with('_sti_on_low_card_tables_should_never_be_used')
7
8
  end
8
9
 
9
10
  it "should include LowCardTables::LowCardTable::Base appropriately and respond to #is_low_card_table? appropriately" do
@@ -2,7 +2,7 @@ require 'low_card_tables'
2
2
 
3
3
  describe LowCardTables::HasLowCardTable::LowCardDynamicMethodManager do
4
4
  before :each do
5
- @model_class = double("model_class")
5
+ @model_class = double("model_class", :superclass => ::Object)
6
6
  @lcam = double("low_card_associations_manager")
7
7
  allow(@model_class).to receive(:_low_card_associations_manager).and_return(@lcam)
8
8
 
@@ -3,6 +3,7 @@ require 'low_card_tables'
3
3
  describe LowCardTables::LowCardTable::Base do
4
4
  before :each do
5
5
  @test_class = Class.new
6
+ allow(@test_class).to receive(:inheritance_column=).with('_sti_on_low_card_tables_should_never_be_used')
6
7
  @test_class.send(:include, LowCardTables::LowCardTable::Base)
7
8
  end
8
9
 
@@ -112,6 +113,7 @@ describe LowCardTables::LowCardTable::Base do
112
113
  end
113
114
 
114
115
  test_class = Class.new
116
+ allow(test_class).to receive(:inheritance_column=).with('_sti_on_low_card_tables_should_never_be_used')
115
117
  test_class.send(:extend, mod)
116
118
  test_class.send(:include, LowCardTables::LowCardTable::Base)
117
119
 
@@ -163,6 +165,7 @@ ensure_has_unique_index! remove_unique_index!}.each do |method_name|
163
165
 
164
166
  # We need a new class -- because we need to make sure our module gets in there first
165
167
  @test_class = Class.new
168
+ allow(@test_class).to receive(:inheritance_column=).with('_sti_on_low_card_tables_should_never_be_used')
166
169
  @test_class.send(:include, @save_mod)
167
170
  @test_class.send(:include, LowCardTables::LowCardTable::Base)
168
171
 
@@ -88,20 +88,18 @@ describe LowCardTables::LowCardTable::RowManager do
88
88
  allow(@low_card_model).to receive(:reset_column_information)
89
89
  allow(@low_card_model).to receive(:table_exists?).and_return(true)
90
90
  allow(@low_card_model).to receive(:table_name).and_return("thetablename")
91
+ allow(@low_card_model).to receive(:primary_key).and_return('id')
91
92
 
92
93
  @table_unique_index = double("table_unique_index")
93
94
  allow(LowCardTables::LowCardTable::TableUniqueIndex).to receive(:new).with(@low_card_model).and_return(@table_unique_index)
94
95
 
95
96
  @column_id = double("column_id")
96
97
  allow(@column_id).to receive(:name).and_return("id")
97
- allow(@column_id).to receive(:primary).and_return(true)
98
98
  @column_foo = double("column_foo")
99
99
  allow(@column_foo).to receive(:name).and_return("foo")
100
- allow(@column_foo).to receive(:primary).and_return(false)
101
100
  allow(@column_foo).to receive(:default).and_return(nil)
102
101
  @column_bar = double("column_bar")
103
102
  allow(@column_bar).to receive(:name).and_return("bar")
104
- allow(@column_bar).to receive(:primary).and_return(false)
105
103
  allow(@column_bar).to receive(:default).and_return('yohoho')
106
104
  allow(@low_card_model).to receive(:columns).and_return([ @column_id, @column_foo, @column_bar ])
107
105
 
@@ -332,7 +330,6 @@ describe LowCardTables::LowCardTable::RowManager do
332
330
  it "should accept hashes containing valid data, if it isn't found the first time through" do
333
331
  column_baz = double("column_bar")
334
332
  allow(column_baz).to receive(:name).and_return("baz")
335
- allow(column_baz).to receive(:primary).and_return(false)
336
333
 
337
334
  columns_to_return = [
338
335
  [ @column_id, @column_foo, @column_bar ],
@@ -865,15 +862,12 @@ describe LowCardTables::LowCardTable::RowManager do
865
862
 
866
863
  it "should exclude primary keys, created/updated_at, and options-specified column names" do
867
864
  column_created_at = double("column_created_at")
868
- allow(column_created_at).to receive(:primary).and_return(false)
869
865
  allow(column_created_at).to receive(:name).and_return("created_at")
870
866
 
871
867
  column_updated_at = double("column_updated_at")
872
- allow(column_updated_at).to receive(:primary).and_return(false)
873
868
  allow(column_updated_at).to receive(:name).and_return("updated_at")
874
869
 
875
870
  column_skipped = double("column_skipped")
876
- allow(column_skipped).to receive(:primary).and_return(false)
877
871
  allow(column_skipped).to receive(:name).and_return("FooFle")
878
872
 
879
873
  columns = [ @column_id, @column_foo, @column_bar, @column_created_at, @column_updated_at, @column_skipped ]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: low_card_tables
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Geweke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-22 00:00:00.000000000 Z
11
+ date: 2015-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -169,6 +169,7 @@ files:
169
169
  - spec/low_card_tables/system/namespaced_models_system_spec.rb
170
170
  - spec/low_card_tables/system/options_system_spec.rb
171
171
  - spec/low_card_tables/system/queries_system_spec.rb
172
+ - spec/low_card_tables/system/sti_system_spec.rb
172
173
  - spec/low_card_tables/system/validations_system_spec.rb
173
174
  - spec/low_card_tables/unit/active_record/base_spec.rb
174
175
  - spec/low_card_tables/unit/active_record/migrations_spec.rb
@@ -209,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
210
  version: '0'
210
211
  requirements: []
211
212
  rubyforge_project:
212
- rubygems_version: 2.2.2
213
+ rubygems_version: 2.4.5
213
214
  signing_key:
214
215
  specification_version: 4
215
216
  summary: '"Bitfields for ActiveRecord": instead of storing multiple columns with low
@@ -228,6 +229,7 @@ test_files:
228
229
  - spec/low_card_tables/system/namespaced_models_system_spec.rb
229
230
  - spec/low_card_tables/system/options_system_spec.rb
230
231
  - spec/low_card_tables/system/queries_system_spec.rb
232
+ - spec/low_card_tables/system/sti_system_spec.rb
231
233
  - spec/low_card_tables/system/validations_system_spec.rb
232
234
  - spec/low_card_tables/unit/active_record/base_spec.rb
233
235
  - spec/low_card_tables/unit/active_record/migrations_spec.rb