flag_shih_tzu 0.3.2 → 0.3.4

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 39037feb3a3b52c19a177cfdd173334d4f94d841
4
+ data.tar.gz: f5f735bb7b3cea9470b224fbc4dc43661b883bf4
5
+ SHA512:
6
+ metadata.gz: d50b56dc9b3217ca2766f0795f92846ad0a88356ff38a3f3153283ac5f65c220d0be7f1a7a4c22a46d2538e45d2106b9b1d8df15e28a73ada846cc7d199753ed
7
+ data.tar.gz: 198b808f2f9004c159620075678939ff19efdb383c30a349d5940e68c39e250f159e2f05c73bb347e68e12ad5eb7c69b3b0d9ca525f6c9fed252f501727a0cd3
data/CHANGELOG CHANGED
@@ -1,3 +1,18 @@
1
+ Version 0.3.3 - JUN.20.2013
2
+ * Allow non sequential flag numbers by Thomas Jachmann
3
+ * Report correct source location for class_evaled methods. by Sebastian Korfmann
4
+ * Implemented chained_flags_with, which allows optimizing the bit search by Tatsuhiko Miyagawa
5
+ * [bugfix] flag_options[colmn][:column] is symbol, it causes error undefined method `length' for nil:NilClass by Artem Pisarev
6
+ * Validator raises an error if the validated column is not a flags column. by David DIDIER
7
+ * Allow multiple include by Peter Goldstein
8
+ * fix a deprecation warning in rails4 by Mose
9
+ * Add flag_keys convenience method. by Keith Pitty
10
+ * [bugfix] Column names provided as symbols fully work now, as they are converted to strings by Peter Boling
11
+ * [bugfix issues/28] Since 0.3.0 flags no longer work in a class using an alternative database connection by Peter Boling
12
+ * [bugfix issues/7] Breaks db:create rake task by Peter Boling
13
+ * convenience methods now have default parameter so `all_flags` works with arity 0. by Peter Boling
14
+ * Many more tests, including arity tests by Peter Boling
15
+
1
16
  Version 0.3.2 - NOV.06.2012
2
17
 
3
18
  * Adds skip column check option :check_for_column - from arturaz
data/README.rdoc CHANGED
@@ -1,23 +1,4 @@
1
- == Change of Ownership and 0.3.X Release Notes
2
-
3
- FlagShihTzu was originally a {XING AG}[http://www.xing.com/] project. {Peter Boling}[http://peterboling.com] was a long time contributor and watcher of the project.
4
- In September 2012 XING transferred ownership of the project to Peter Boling. Peter Boling had been maintaining a
5
- fork with extended capabilities. These additional features become a part of the 0.3 line. The 0.2 line of the gem will
6
- remain true to XING's original. The 0.3 line aims to maintain complete parity and compatibility with XING's original as
7
- well. I will continue to monitor other forks for original ideas and improvements. Pull requests are welcome, but please
8
- rebase your work onto the current master to make integration easier.
9
-
10
- Some new things in the 0.3 line:
11
-
12
- * ClassWithHasFlags.set_#{flag_name}_sql # Returns the sql string for setting a flag for use in customized SQL
13
- * ClassWithHasFlags.unset_#{flag_name}_sql # Returns the sql string for unsetting a flag for use in customized SQL
14
- * ClassWithHasFlags.flag_columns # Returns the column_names used by FlagShihTzu as bit fields
15
- * has_flags :strict => true # DuplicateFlagColumnException raised when a single DB column is declared as a flag column twice
16
- * Less verbosity for expected conditions when the DB connection for the class is unavailable.
17
- * Tests for additional features, but does not change any behavior of 0.2 versions by default.
18
- * Easily migrate from 0.2 versions. No code changes required.
19
-
20
- =FlagShihTzu
1
+ =FlagShihTzu {<img src="https://secure.travis-ci.org/pboling/flag_shih_tzu.png" />}[http://travis-ci.org/pboling/flag_shih_tzu] {<img src="http://api.coderwall.com/pboling/endorsecount.png" />}[http://coderwall.com/pboling]
21
2
 
22
3
  Bit fields for ActiveRecord
23
4
 
@@ -25,8 +6,6 @@ An extension for {ActiveRecord}[https://rubygems.org/gems/activerecord]
25
6
  to store a collection of boolean attributes in a single integer column
26
7
  as a bit field.
27
8
 
28
- http://github.com/xing/flag_shih_tzu
29
-
30
9
  This gem lets you use a single integer column in an ActiveRecord model
31
10
  to store a collection of boolean attributes (flags). Each flag can be used
32
11
  almost in the same way you would use any boolean attribute on an
@@ -34,7 +13,7 @@ ActiveRecord object.
34
13
 
35
14
  The benefits:
36
15
  * No migrations needed for new boolean attributes. This helps a lot
37
- if you have very large db-tables, on which you want to avoid ALTER TABLE
16
+ if you have very large db-tables, on which you want to avoid `ALTER TABLE`
38
17
  whenever possible.
39
18
  * Only the one integer column needs to be indexed.
40
19
 
@@ -44,11 +23,22 @@ without needing any migration. Just add a new flag to the +has_flags+ call.
44
23
  And just in case you are wondering what a "Shih Tzu" is:
45
24
  http://en.wikipedia.org/wiki/Shih_Tzu
46
25
 
26
+ == Change of Ownership and 0.3.X Release Notes
27
+
28
+ FlagShihTzu was originally a {XING AG}[http://www.xing.com/] project. {Peter Boling}[http://peterboling.com] was a long time contributor and watcher of the project.
29
+ In September 2012 XING transferred ownership of the project to Peter Boling. Peter Boling had been maintaining a
30
+ fork with extended capabilities. These additional features become a part of the 0.3 line. The 0.2 line of the gem will
31
+ remain true to XING's original. The 0.3 line aims to maintain complete parity and compatibility with XING's original as
32
+ well. I will continue to monitor other forks for original ideas and improvements. Pull requests are welcome, but please
33
+ rebase your work onto the current master to make integration easier.
34
+
35
+ More information on the changes for 0.3.X: {pboling/flag_shih_tzu/wiki/Changes-for-0.3.x}[https://github.com/pboling/flag_shih_tzu/wiki/Changes-for-0.3.x]
47
36
 
48
37
  ==Build status
49
38
 
50
- {<img src="https://secure.travis-ci.org/pboling/flag_shih_tzu.png" />}[http://travis-ci.org/pboling/flag_shih_tzu]
39
+ Travis CI: {<img src="https://secure.travis-ci.org/pboling/flag_shih_tzu.png" />}[http://travis-ci.org/pboling/flag_shih_tzu]
51
40
 
41
+ CodeClimate: https://codeclimate.com/github/pboling/flag_shih_tzu
52
42
 
53
43
  ==Prerequisites
54
44
 
@@ -377,11 +367,35 @@ and a helpful group of
377
367
  Thanks!
378
368
 
379
369
  Find out more about Peter Boling's work
380
- {PeterBoling.com}[http://peterboling.com/].
370
+ {RailsBling.com}[http://railsbling.com/].
381
371
 
382
372
  Find out more about XING
383
373
  {Devblog}[http://devblog.xing.com/].
384
374
 
375
+ == Contributing
376
+
377
+ 1. Fork it
378
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
379
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
380
+ 4. Push to the branch (`git push origin my-new-feature`)
381
+ 5. Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
382
+ 6. Create new Pull Request
383
+
384
+ == Versioning
385
+
386
+ This library aims to adhere to {Semantic Versioning 2.0.0}[http://semver.org/].
387
+ Violations of this scheme should be reported as bugs. Specifically,
388
+ if a minor or patch version is released that breaks backward
389
+ compatibility, a new version should be immediately released that
390
+ restores compatibility. Breaking changes to the public API will
391
+ only be introduced with new major versions.
392
+
393
+ As a result of this policy, you can (and should) specify a
394
+ dependency on this gem using the {Pessimistic Version Constraint}[http://docs.rubygems.org/read/chapter/16#page74] with two digits of precision.
395
+
396
+ For example:
397
+
398
+ spec.add_dependency 'flag_shih_tzu', '~> 4.0'
385
399
 
386
400
  ==License
387
401
 
@@ -5,6 +5,8 @@ require "flag_shih_tzu/version"
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "flag_shih_tzu"
7
7
  s.version = FlagShihTzu::VERSION
8
+ s.licenses = ['MIT']
9
+ s.email = 'peter.boling@gmail.com'
8
10
  s.platform = Gem::Platform::RUBY
9
11
  s.authors = ["Patryk Peszko", "Sebastian Roebke", "David Anderson", "Tim Payton"]
10
12
  s.homepage = "https://github.com/xing/flag_shih_tzu"
data/lib/flag_shih_tzu.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "rubygems"
2
2
  require "active_record"
3
3
  require "active_support/all"
4
+ require "flag_shih_tzu/validators"
4
5
 
5
6
  module FlagShihTzu
6
7
  # taken from ActiveRecord::ConnectionAdapters::Column
@@ -31,8 +32,15 @@ module FlagShihTzu
31
32
  :check_for_column => true
32
33
  }.update(opts)
33
34
  colmn = opts[:column].to_s
34
-
35
- return if opts[:check_for_column] && ! check_flag_column(colmn)
35
+ if !is_valid_column_name(opts[:column])
36
+ puts "FlagShihTzu says: Please use a String to designate column names! I see you here: #{caller.first}"
37
+ opts[:column] = opts[:column].to_s
38
+ end
39
+ colmn = opts[:column]
40
+ if opts[:check_for_column] && !check_flag_column(colmn)
41
+ puts "FlagShihTzu says: Flag column #{colmn} appears to be missing!\nTo turn off this warning set check_for_colum: false in has_flags definition here: #{caller.first}"
42
+ return
43
+ end
36
44
 
37
45
  # options are stored in a class level hash and apply per-column
38
46
  self.flag_options ||= {}
@@ -55,8 +63,9 @@ module FlagShihTzu
55
63
  raise ArgumentError, "has_flags: flag name #{flag_name} already defined, please choose different name" if method_defined?(flag_name)
56
64
 
57
65
  flag_mapping[colmn][flag_name] = 1 << (flag_key - 1)
66
+ #puts "Defined: #{flag_key} as #{flag_mapping[colmn][flag_name]}"
58
67
 
59
- class_eval <<-EVAL
68
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
60
69
  def #{flag_name}
61
70
  flag_enabled?(:#{flag_name}, '#{colmn}')
62
71
  end
@@ -96,7 +105,7 @@ module FlagShihTzu
96
105
  EVAL
97
106
 
98
107
  if colmn != DEFAULT_COLUMN_NAME
99
- class_eval <<-EVAL
108
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
100
109
 
101
110
  def all_#{colmn}
102
111
  all_flags('#{colmn}')
@@ -131,7 +140,7 @@ module FlagShihTzu
131
140
 
132
141
  # Define bancg methods when requested
133
142
  if flag_options[colmn][:bang_methods]
134
- class_eval <<-EVAL
143
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
135
144
  def #{flag_name}!
136
145
  enable_flag(:#{flag_name}, '#{colmn}')
137
146
  end
@@ -143,11 +152,20 @@ module FlagShihTzu
143
152
  end
144
153
 
145
154
  # Define the named scopes if the user wants them and AR supports it
146
- if flag_options[colmn][:named_scopes] && respond_to?(named_scope_method)
147
- class_eval <<-EVAL
148
- #{named_scope_method} :#{flag_name}, lambda { { :conditions => #{flag_name}_condition } }
149
- #{named_scope_method} :not_#{flag_name}, lambda { { :conditions => not_#{flag_name}_condition } }
150
- EVAL
155
+ if flag_options[colmn][:named_scopes]
156
+ if ActiveRecord::VERSION::MAJOR == 2 && respond_to?(:named_scope)
157
+ # Prevent deprecation notices on Rails 3 when using +named_scope+ instead of +scope+.
158
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
159
+ named_scope :#{flag_name}, lambda { { :conditions => #{flag_name}_condition } }
160
+ named_scope :not_#{flag_name}, lambda { { :conditions => not_#{flag_name}_condition } }
161
+ EVAL
162
+ elsif respond_to?(:scope)
163
+ # Prevent deprecation notices on Rails 4 when using +conditions+ instead of +where+.
164
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
165
+ scope :#{flag_name}, lambda { where(#{flag_name}_condition) }
166
+ scope :not_#{flag_name}, lambda { where(not_#{flag_name}_condition) }
167
+ EVAL
168
+ end
151
169
  end
152
170
  end
153
171
 
@@ -173,8 +191,43 @@ module FlagShihTzu
173
191
  raise NoSuchFlagException.new("determine_flag_colmn_for: Couldn't determine column for your flags!")
174
192
  end
175
193
 
194
+ def chained_flags_with(*args)
195
+ where(chained_flags_condition(*args))
196
+ end
197
+
198
+ def chained_flags_condition(colmn, *args)
199
+ "(#{self.table_name}.#{colmn} in (#{chained_flags_values(colmn, *args).join(',')}))"
200
+ end
201
+
202
+ def flag_keys(colmn = DEFAULT_COLUMN_NAME)
203
+ flag_mapping[colmn].keys
204
+ end
205
+
176
206
  private
177
207
 
208
+ def max_flag_value_for_column(colmn)
209
+ flag_mapping[colmn].values.max
210
+ end
211
+
212
+ def chained_flags_values(colmn, *args)
213
+ val = (1..(2 * max_flag_value_for_column(colmn))).to_a
214
+ args.each do |flag|
215
+ neg = false
216
+ if flag.match /^not_/
217
+ neg = true
218
+ flag = flag.to_s.sub(/^not_/, '').to_sym
219
+ end
220
+ check_flag(flag, colmn)
221
+ flag_values = sql_in_for_flag(flag, colmn)
222
+ if neg
223
+ val = val - flag_values
224
+ else
225
+ val = val & flag_values
226
+ end
227
+ end
228
+ val
229
+ end
230
+
178
231
  def parse_options(*args)
179
232
  options = args.shift
180
233
  if args.size >= 1
@@ -191,22 +244,30 @@ module FlagShihTzu
191
244
  def check_flag_column(colmn, custom_table_name = self.table_name)
192
245
  # If you aren't using ActiveRecord (eg. you are outside rails) then do not fail here
193
246
  # If you are using ActiveRecord then you only want to check for the table if the table exists so it won't fail pre-migration
194
- has_ar = !!defined?(ActiveRecord) && self.respond_to?(:descends_from_active_record?)
247
+ has_ar = (!!defined?(ActiveRecord) && self.respond_to?(:descends_from_active_record?))
195
248
  # Supposedly Rails 2.3 takes care of this, but this precaution is needed for backwards compatibility
196
- has_table = has_ar ? ActiveRecord::Base.connection.tables.include?(custom_table_name) : true
197
-
249
+ has_table = has_ar ? connection.tables.include?(custom_table_name) : true
198
250
  if has_table
199
251
  found_column = columns.find {|column| column.name == colmn}
200
252
  #If you have not yet run the migration that adds the 'flags' column then we don't want to fail, because we need to be able to run the migration
201
253
  #If the column is there but is of the wrong type, then we must fail, because flag_shih_tzu will not work
202
254
  if found_column.nil?
203
- logger.warn("Error: Column '#{colmn}' doesn't exist on table '#{custom_table_name}'. Did you forget to run migrations?") and return false
255
+ if respond_to?(:logger)
256
+ logger.warn("Error: Column '#{colmn}' doesn't exist on table '#{custom_table_name}'. Did you forget to run migrations?") and return false
257
+ else
258
+ puts("Error: Column '#{colmn}' doesn't exist on table '#{custom_table_name}'. Did you forget to run migrations?") and return false
259
+ end
204
260
  elsif found_column.type != :integer
205
261
  raise IncorrectFlagColumnException.new("Table '#{custom_table_name}' must have an integer column named '#{colmn}' in order to use FlagShihTzu.") and return false
206
262
  end
207
263
  else
208
264
  # ActiveRecord gem probably hasn't loaded yet?
209
- logger.warn("FlagShihTzu#has_flags: Table '#{custom_table_name}' doesn't exist. Have all migrations been run?") and return false
265
+ if respond_to?(:logger)
266
+ logger.warn("FlagShihTzu#has_flags: Table '#{custom_table_name}' doesn't exist. Have all migrations been run?") if has_ar
267
+ return false
268
+ #else
269
+ # puts("FlagShihTzu#has_flags: Table '#{custom_table_name}' doesn't exist. Have all migrations been run?") and return false
270
+ end
210
271
  end
211
272
 
212
273
  true
@@ -232,10 +293,10 @@ module FlagShihTzu
232
293
  # returns an array of integers suitable for a SQL IN statement.
233
294
  def sql_in_for_flag(flag, colmn)
234
295
  val = flag_mapping[colmn][flag]
235
- num = 2 ** flag_mapping[flag_options[colmn][:column]].length
296
+ num = 2 * max_flag_value_for_column(colmn)
236
297
  (1..num).select {|i| i & val == val}
237
298
  end
238
-
299
+
239
300
  def sql_set_for_flag(flag, colmn, enabled = true, custom_table_name = self.table_name)
240
301
  check_flag(flag, colmn)
241
302
  "#{colmn} = #{colmn} #{enabled ? "| " : "& ~" }#{flag_mapping[colmn][flag]}"
@@ -249,6 +310,10 @@ module FlagShihTzu
249
310
  flag_name.is_a?(Symbol)
250
311
  end
251
312
 
313
+ def is_valid_column_name(colmn)
314
+ colmn.is_a?(String)
315
+ end
316
+
252
317
  # Returns the correct method to create a named scope.
253
318
  # Use to prevent deprecation notices on Rails 3 when using +named_scope+ instead of +scope+.
254
319
  def named_scope_method
@@ -291,32 +356,32 @@ module FlagShihTzu
291
356
  self[colmn] || 0
292
357
  end
293
358
 
294
- def set_flags(value, colmn)
359
+ def set_flags(value, colmn = DEFAULT_COLUMN_NAME)
295
360
  self[colmn] = value
296
361
  end
297
362
 
298
- def all_flags(column)
299
- flag_mapping[column].keys
363
+ def all_flags(colmn = DEFAULT_COLUMN_NAME)
364
+ flag_mapping[colmn].keys
300
365
  end
301
366
 
302
- def selected_flags(column)
303
- all_flags(column).map { |flag_name| self.send(flag_name) ? flag_name : nil }.compact
367
+ def selected_flags(colmn = DEFAULT_COLUMN_NAME)
368
+ all_flags(colmn).map { |flag_name| self.send(flag_name) ? flag_name : nil }.compact
304
369
  end
305
370
 
306
- def select_all_flags(column)
307
- all_flags(column).each do |flag|
308
- enable_flag(flag, column)
371
+ def select_all_flags(colmn = DEFAULT_COLUMN_NAME)
372
+ all_flags(colmn).each do |flag|
373
+ enable_flag(flag, colmn)
309
374
  end
310
375
  end
311
376
 
312
- def unselect_all_flags(column)
313
- all_flags(column).each do |flag|
314
- disable_flag(flag, column)
377
+ def unselect_all_flags(colmn = DEFAULT_COLUMN_NAME)
378
+ all_flags(colmn).each do |flag|
379
+ disable_flag(flag, colmn)
315
380
  end
316
381
  end
317
382
 
318
- def has_flag?(column = DEFAULT_COLUMN_NAME)
319
- not selected_flags(column).empty?
383
+ def has_flag?(colmn = DEFAULT_COLUMN_NAME)
384
+ not selected_flags(colmn).empty?
320
385
  end
321
386
 
322
387
  private
@@ -0,0 +1,54 @@
1
+ module ActiveModel
2
+ module Validations
3
+
4
+ class PresenceOfFlagsValidator < EachValidator
5
+ def validate_each(record, attribute, value)
6
+ value = record.send(:read_attribute_for_validation, attribute)
7
+ check_flag(record, attribute)
8
+ record.errors.add(attribute, :blank, options) if value.blank? or value == 0
9
+ end
10
+
11
+ private
12
+
13
+ def check_flag(record, attribute)
14
+ unless record.class.flag_columns.include? attribute.to_s
15
+ raise ArgumentError.new("#{attribute} is not one of the flags columns (#{record.class.flag_columns.join(', ')})")
16
+ end
17
+ end
18
+ end
19
+
20
+ module HelperMethods
21
+ # Validates that the specified attributes are flags and are not blank.
22
+ # Happens by default on save. Example:
23
+ #
24
+ # class Spaceship < ActiveRecord::Base
25
+ # include FlagShihTzu
26
+ #
27
+ # has_flags({ 1 => :warpdrive, 2 => :hyperspace }, :column => 'engines')
28
+ # validates_presence_of_flags :engines
29
+ # end
30
+ #
31
+ # The engines attribute must be a flag in the object and it cannot be blank.
32
+ #
33
+ # Configuration options:
34
+ # * <tt>:message</tt> - A custom error message (default is: "can't be blank").
35
+ # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
36
+ # validation contexts by default (+nil+), other options are <tt>:create</tt>
37
+ # and <tt>:update</tt>.
38
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
39
+ # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
40
+ # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
41
+ # or string should return or evaluate to a true or false value.
42
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
43
+ # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
44
+ # or <tt>:unless => Proc.new { |spaceship| spaceship.warp_step <= 2 }</tt>). The method,
45
+ # proc or string should return or evaluate to a true or false value.
46
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
47
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
48
+ def validates_presence_of_flags(*attr_names)
49
+ validates_with PresenceOfFlagsValidator, _merge_attributes(attr_names)
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module FlagShihTzu
2
- VERSION = "0.3.2"
2
+ VERSION = "0.3.4"
3
3
  end
@@ -54,6 +54,16 @@ class SpaceshipWith3CustomFlagsColumn < ActiveRecord::Base
54
54
  has_flags({ 1 => :power, 2 => :anti_ax_routine }, :column => 'hal3000')
55
55
  end
56
56
 
57
+ class SpaceshipWithSymbolAndStringFlagColumns < ActiveRecord::Base
58
+ self.table_name = 'spaceships_with_symbol_and_string_flag_columns'
59
+ include FlagShihTzu
60
+
61
+ has_flags({ 1 => :warpdrive, 2 => :hyperspace }, :column => :peace, :check_for_column => true)
62
+ has_flags({ 1 => :photon, 2 => :laser, 3 => :ion_cannon, 4 => :particle_beam }, :column => :love, :check_for_column => true)
63
+ has_flags({ 1 => :power, 2 => :anti_ax_routine }, :column => 'happiness', :check_for_column => true)
64
+ validates_presence_of_flags :peace, :love
65
+ end
66
+
57
67
  class SpaceshipWithBitOperatorQueryMode < ActiveRecord::Base
58
68
  self.table_name = 'spaceships'
59
69
  include FlagShihTzu
@@ -68,9 +78,44 @@ class SpaceshipWithBangMethods < ActiveRecord::Base
68
78
  has_flags(1 => :warpdrive, 2 => :shields, :bang_methods => true)
69
79
  end
70
80
 
81
+ class SpaceshipWithMissingFlags < ActiveRecord::Base
82
+ self.table_name = 'spaceships'
83
+ include FlagShihTzu
84
+
85
+ has_flags 1 => :warpdrive,
86
+ 3 => :electrolytes
87
+ end
88
+
71
89
  class SpaceCarrier < Spaceship
72
90
  end
73
91
 
92
+ class SpaceshipWithValidationsAndCustomFlagsColumn < ActiveRecord::Base
93
+ self.table_name = 'spaceships_with_custom_flags_column'
94
+ include FlagShihTzu
95
+
96
+ has_flags(1 => :warpdrive, 2 => :hyperspace, :column => 'bits')
97
+ validates_presence_of_flags :bits
98
+ end
99
+
100
+ class SpaceshipWithValidationsAnd3CustomFlagsColumn < ActiveRecord::Base
101
+ self.table_name = 'spaceships_with_3_custom_flags_column'
102
+ include FlagShihTzu
103
+
104
+ has_flags({ 1 => :warpdrive, 2 => :hyperspace }, :column => 'engines')
105
+ has_flags({ 1 => :photon, 2 => :laser, 3 => :ion_cannon, 4 => :particle_beam }, :column => 'weapons')
106
+ has_flags({ 1 => :power, 2 => :anti_ax_routine }, :column => 'hal3000')
107
+
108
+ validates_presence_of_flags :engines, :weapons
109
+ end
110
+
111
+ class SpaceshipWithValidationsOnNonFlagsColumn < ActiveRecord::Base
112
+ self.table_name = 'spaceships_with_custom_flags_column'
113
+ include FlagShihTzu
114
+
115
+ has_flags(1 => :warpdrive, 2 => :hyperspace, :column => 'bits')
116
+ validates_presence_of_flags :id
117
+ end
118
+
74
119
  # table planets is missing intentionally to see if flagshihtzu handles missing tables gracefully
75
120
  class Planet < ActiveRecord::Base
76
121
  end
@@ -176,6 +221,11 @@ class FlagShihTzuClassMethodsTest < Test::Unit::TestCase
176
221
  assert_equal "(spaceships.flags in (4,5,6,7))", Spaceship.electrolytes_condition
177
222
  end
178
223
 
224
+ def test_should_define_a_sql_condition_method_for_flag_enabled_with_missing_flags
225
+ assert_equal "(spaceships.flags in (1,3,5,7))", SpaceshipWithMissingFlags.warpdrive_condition
226
+ assert_equal "(spaceships.flags in (4,5,6,7))", SpaceshipWithMissingFlags.electrolytes_condition
227
+ end
228
+
179
229
  def test_should_accept_a_table_alias_option_for_sql_condition_method
180
230
  assert_equal "(old_spaceships.flags in (1,3,5,7))", Spaceship.warpdrive_condition(:table_alias => 'old_spaceships')
181
231
  end
@@ -193,6 +243,11 @@ class FlagShihTzuClassMethodsTest < Test::Unit::TestCase
193
243
  assert_equal "(spaceships.flags not in (4,5,6,7))", Spaceship.not_electrolytes_condition
194
244
  end
195
245
 
246
+ def test_should_define_a_sql_condition_method_for_flag_not_enabled_with_missing_flags
247
+ assert_equal "(spaceships.flags not in (1,3,5,7))", SpaceshipWithMissingFlags.not_warpdrive_condition
248
+ assert_equal "(spaceships.flags not in (4,5,6,7))", SpaceshipWithMissingFlags.not_electrolytes_condition
249
+ end
250
+
196
251
  def test_should_define_a_sql_condition_method_for_flag_enabled_with_custom_table_name
197
252
  assert_equal "(custom_spaceships.flags in (1,3,5,7))", Spaceship.send(:sql_condition_for_flag, :warpdrive, 'flags', true, 'custom_spaceships')
198
253
  end
@@ -259,7 +314,7 @@ class FlagShihTzuClassMethodsTest < Test::Unit::TestCase
259
314
  Spaceship.update_all Spaceship.set_flag_sql(:warpdrive, true),
260
315
  ["id=?", spaceship.id]
261
316
  spaceship.reload
262
-
317
+
263
318
  assert_equal true, spaceship.warpdrive
264
319
  assert_equal true, spaceship.shields
265
320
  assert_equal true, spaceship.electrolytes
@@ -301,6 +356,33 @@ class FlagShihTzuClassMethodsTest < Test::Unit::TestCase
301
356
  assert_equal 0, Spaceship.not_warpdrive.not_shields.count
302
357
  end
303
358
 
359
+ def test_should_return_the_correct_condition_with_chained_flags
360
+ assert_equal "(spaceships.flags in (3,7))", Spaceship.chained_flags_condition("flags", :warpdrive, :shields)
361
+ assert_equal "(spaceships.flags in (7))", Spaceship.chained_flags_condition("flags", :warpdrive, :shields, :electrolytes)
362
+ assert_equal "(spaceships.flags in (2,6))", Spaceship.chained_flags_condition("flags", :not_warpdrive, :shields)
363
+ end
364
+
365
+ def test_should_return_the_correct_number_of_items_with_chained_flags_with
366
+ spaceship = Spaceship.new
367
+ spaceship.enable_flag(:warpdrive)
368
+ spaceship.enable_flag(:shields)
369
+ spaceship.save!
370
+ spaceship.reload
371
+ spaceship_2 = Spaceship.new
372
+ spaceship_2.enable_flag(:warpdrive)
373
+ spaceship_2.save!
374
+ spaceship_2.reload
375
+ spaceship_3 = Spaceship.new
376
+ spaceship_3.enable_flag(:shields)
377
+ spaceship_3.save!
378
+ spaceship_3.reload
379
+ assert_equal 2, Spaceship.chained_flags_with("flags", :warpdrive).count
380
+ assert_equal 1, Spaceship.chained_flags_with("flags", :warpdrive, :shields).count
381
+ assert_equal 1, Spaceship.chained_flags_with("flags", :warpdrive, :not_shields).count
382
+ assert_equal 0, Spaceship.chained_flags_with("flags", :not_warpdrive, :shields, :electrolytes).count
383
+ assert_equal 1, Spaceship.chained_flags_with("flags", :not_warpdrive, :shields, :not_electrolytes).count
384
+ end
385
+
304
386
  def test_should_not_define_named_scopes_if_not_wanted
305
387
  assert !SpaceshipWithoutNamedScopes.respond_to?(:warpdrive)
306
388
  assert !SpaceshipWithoutNamedScopesOldStyle.respond_to?(:warpdrive)
@@ -340,6 +422,18 @@ class FlagShihTzuClassMethodsTest < Test::Unit::TestCase
340
422
  end
341
423
  end
342
424
 
425
+ def test_should_not_error_out_when_column_is_not_present
426
+ assert_nothing_raised(ActiveRecord::StatementInvalid) do
427
+ Planet.class_eval do
428
+ # Now it has a table that exists, but the column does not.
429
+ self.table_name = 'spaceships'
430
+ include FlagShihTzu
431
+
432
+ has_flags({ 1 => :warpdrive, 2 => :hyperspace }, :column => :i_do_not_exist, :check_for_column => true)
433
+ end
434
+ end
435
+ end
436
+
343
437
  private
344
438
 
345
439
  def assert_where_value(expected, scope)
@@ -439,11 +533,15 @@ class FlagShihTzuInstanceMethodsTest < Test::Unit::TestCase
439
533
 
440
534
  # --------------------------------------------------
441
535
 
442
- def test_should_define_an_all_flags_reader_method
536
+ def test_should_define_an_all_flags_reader_method_with_arity_1
443
537
  assert_array_similarity [:electrolytes, :warpdrive, :shields], @spaceship.all_flags('flags')
444
538
  end
445
539
 
446
- def test_should_define_a_selected_flags_reader_method
540
+ def test_should_define_an_all_flags_reader_method_with_arity_0
541
+ assert_array_similarity [:electrolytes, :warpdrive, :shields], @spaceship.all_flags
542
+ end
543
+
544
+ def test_should_define_a_selected_flags_reader_method_with_arity_1
447
545
  assert_array_similarity [], @spaceship.selected_flags('flags')
448
546
 
449
547
  @spaceship.warpdrive = true
@@ -457,14 +555,35 @@ class FlagShihTzuInstanceMethodsTest < Test::Unit::TestCase
457
555
  assert_array_similarity [], @spaceship.selected_flags('flags')
458
556
  end
459
557
 
460
- def test_should_define_a_select_all_flags_method
558
+ def test_should_define_a_selected_flags_reader_method_with_arity_0
559
+ assert_array_similarity [], @spaceship.selected_flags
560
+
561
+ @spaceship.warpdrive = true
562
+ assert_array_similarity [:warpdrive], @spaceship.selected_flags
563
+
564
+ @spaceship.electrolytes = true
565
+ assert_array_similarity [:electrolytes, :warpdrive], @spaceship.selected_flags
566
+
567
+ @spaceship.warpdrive = false
568
+ @spaceship.electrolytes = false
569
+ assert_array_similarity [], @spaceship.selected_flags
570
+ end
571
+
572
+ def test_should_define_a_select_all_flags_method_with_arity_1
461
573
  @spaceship.select_all_flags('flags')
462
574
  assert @spaceship.warpdrive
463
575
  assert @spaceship.shields
464
576
  assert @spaceship.electrolytes
465
577
  end
466
578
 
467
- def test_should_define_an_unselect_all_flags_method
579
+ def test_should_define_a_select_all_flags_method_with_arity_0
580
+ @spaceship.select_all_flags
581
+ assert @spaceship.warpdrive
582
+ assert @spaceship.shields
583
+ assert @spaceship.electrolytes
584
+ end
585
+
586
+ def test_should_define_an_unselect_all_flags_method_with_arity_1
468
587
  @spaceship.warpdrive = true
469
588
  @spaceship.shields = true
470
589
  @spaceship.electrolytes = true
@@ -476,7 +595,19 @@ class FlagShihTzuInstanceMethodsTest < Test::Unit::TestCase
476
595
  assert !@spaceship.electrolytes
477
596
  end
478
597
 
479
- def test_should_define_an_has_flag_method
598
+ def test_should_define_an_unselect_all_flags_method_with_arity_0
599
+ @spaceship.warpdrive = true
600
+ @spaceship.shields = true
601
+ @spaceship.electrolytes = true
602
+
603
+ @spaceship.unselect_all_flags
604
+
605
+ assert !@spaceship.warpdrive
606
+ assert !@spaceship.shields
607
+ assert !@spaceship.electrolytes
608
+ end
609
+
610
+ def test_should_define_an_has_flag_method_with_arity_1
480
611
  assert !@spaceship.has_flag?('flags')
481
612
 
482
613
  @spaceship.warpdrive = true
@@ -492,6 +623,22 @@ class FlagShihTzuInstanceMethodsTest < Test::Unit::TestCase
492
623
  assert !@spaceship.has_flag?('flags')
493
624
  end
494
625
 
626
+ def test_should_define_an_has_flag_method_with_arity_0
627
+ assert !@spaceship.has_flag?
628
+
629
+ @spaceship.warpdrive = true
630
+ assert @spaceship.has_flag?
631
+
632
+ @spaceship.shields = true
633
+ assert @spaceship.has_flag?
634
+
635
+ @spaceship.electrolytes = true
636
+ assert @spaceship.has_flag?
637
+
638
+ @spaceship.unselect_all_flags
639
+ assert !@spaceship.has_flag?
640
+ end
641
+
495
642
  # --------------------------------------------------
496
643
 
497
644
  def test_should_define_a_customized_all_flags_reader_method
@@ -742,11 +889,11 @@ class FlagShihTzuInstanceMethodsTest < Test::Unit::TestCase
742
889
  assert !SpaceshipWithoutFlagsColumn.method_defined?(:warpdrive)
743
890
  end
744
891
 
745
- def test_column_guessing_for_default_column
892
+ def test_column_guessing_for_default_column_2
746
893
  assert_equal 'flags', @spaceship.class.determine_flag_colmn_for(:warpdrive)
747
894
  end
748
895
 
749
- def test_column_guessing_for_default_column
896
+ def test_column_guessing_for_default_column_1
750
897
  assert_raises FlagShihTzu::NoSuchFlagException do
751
898
  @spaceship.class.determine_flag_colmn_for(:xxx)
752
899
  end
@@ -757,6 +904,57 @@ class FlagShihTzuInstanceMethodsTest < Test::Unit::TestCase
757
904
  assert_equal 'bits', @big_spaceship.class.determine_flag_colmn_for(:warpdrive)
758
905
  end
759
906
 
907
+ # --------------------------------------------------
908
+
909
+ def test_validation_should_raise_if_not_a_flag_column
910
+ spaceship = SpaceshipWithValidationsOnNonFlagsColumn.new
911
+ assert_raises ArgumentError do
912
+ spaceship.valid?
913
+ end
914
+ end
915
+
916
+ def test_validation_should_succeed_with_a_blank_optional_flag
917
+ spaceship = Spaceship.new
918
+ assert_equal true, spaceship.valid?
919
+ end
920
+
921
+ def test_validation_should_fail_with_a_nil_required_flag
922
+ spaceship = SpaceshipWithValidationsAndCustomFlagsColumn.new
923
+ spaceship.bits = nil
924
+ assert_equal false, spaceship.valid?
925
+ assert_equal ["can't be blank"], spaceship.errors.messages[:bits]
926
+ end
927
+
928
+ def test_validation_should_fail_with_a_blank_required_flag
929
+ spaceship = SpaceshipWithValidationsAndCustomFlagsColumn.new
930
+ assert_equal false, spaceship.valid?
931
+ assert_equal ["can't be blank"], spaceship.errors.messages[:bits]
932
+ end
933
+
934
+ def test_validation_should_succeed_with_a_set_required_flag
935
+ spaceship = SpaceshipWithValidationsAndCustomFlagsColumn.new
936
+ spaceship.warpdrive = true
937
+ assert_equal true, spaceship.valid?
938
+ end
939
+
940
+ def test_validation_should_fail_with_a_blank_required_flag_among_2
941
+ spaceship = SpaceshipWithValidationsAnd3CustomFlagsColumn.new
942
+ assert_equal false, spaceship.valid?
943
+ assert_equal ["can't be blank"], spaceship.errors.messages[:engines]
944
+ assert_equal ["can't be blank"], spaceship.errors.messages[:weapons]
945
+
946
+ spaceship.warpdrive = true
947
+ assert_equal false, spaceship.valid?
948
+ assert_equal ["can't be blank"], spaceship.errors.messages[:weapons]
949
+ end
950
+
951
+ def test_validation_should_succeed_with_a_set_required_flag_among_2
952
+ spaceship = SpaceshipWithValidationsAnd3CustomFlagsColumn.new
953
+ spaceship.warpdrive = true
954
+ spaceship.photon = true
955
+ assert_equal true, spaceship.valid?
956
+ end
957
+
760
958
  end
761
959
 
762
960
  class FlagShihTzuDerivedClassTest < Test::Unit::TestCase
@@ -848,6 +1046,8 @@ class FlagShihTzuClassMethodsTest < Test::Unit::TestCase
848
1046
  assert_equal Spaceship.flag_columns, ['flags']
849
1047
  assert_equal SpaceshipWith2CustomFlagsColumn.flag_columns, ['bits', 'commanders']
850
1048
  assert_equal SpaceshipWith3CustomFlagsColumn.flag_columns, ['engines', 'weapons', 'hal3000']
1049
+ assert_equal SpaceshipWithValidationsAnd3CustomFlagsColumn.flag_columns, ["engines", "weapons", "hal3000"]
1050
+ assert_equal SpaceshipWithSymbolAndStringFlagColumns.flag_columns, ['peace', 'love', 'happiness']
851
1051
  end
852
1052
 
853
1053
  end
data/test/schema.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  ActiveRecord::Schema.define(:version => 0) do
2
- create_table :spaceships, :force => true do |t|
2
+ create_table :spaceships, :force => true do |t|
3
3
  t.string :type, :null => false, :default => 'Spaceship'
4
4
  t.integer :flags, :null => false, :default => 0
5
5
  t.string :incorrect_flags_column, :null => false, :default => ''
6
- end
6
+ end
7
7
 
8
- create_table :spaceships_with_custom_flags_column, :force => true do |t|
8
+ create_table :spaceships_with_custom_flags_column, :force => true do |t|
9
9
  t.integer :bits, :null => false, :default => 0
10
10
  end
11
11
 
12
- create_table :spaceships_with_2_custom_flags_column, :force => true do |t|
12
+ create_table :spaceships_with_2_custom_flags_column, :force => true do |t|
13
13
  t.integer :bits, :null => false, :default => 0
14
14
  t.integer :commanders, :null => false, :default => 0
15
15
  end
@@ -20,10 +20,22 @@ ActiveRecord::Schema.define(:version => 0) do
20
20
  t.integer :hal3000, :null => false, :default => 0
21
21
  end
22
22
 
23
- create_table :spaceships_without_flags_column, :force => true do |t|
23
+ create_table :spaceships_with_3_custom_flags_column, :force => true do |t|
24
+ t.integer :engines, :null => false, :default => 0
25
+ t.integer :weapons, :null => false, :default => 0
26
+ t.integer :hal3000, :null => false, :default => 0
27
+ end
28
+
29
+ create_table :spaceships_with_symbol_and_string_flag_columns, :force => true do |t|
30
+ t.integer :peace, :null => false, :default => 0
31
+ t.integer :love, :null => false, :default => 0
32
+ t.integer :happiness, :null => false, :default => 0
33
+ end
34
+
35
+ create_table :spaceships_without_flags_column, :force => true do |t|
24
36
  end
25
37
 
26
- create_table :spaceships_with_non_integer_column, :force => true do |t|
38
+ create_table :spaceships_with_non_integer_column, :force => true do |t|
27
39
  t.string :flags, :null => false, :default => 'A string'
28
40
  end
29
41
  end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flag_shih_tzu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
5
- prerelease:
4
+ version: 0.3.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Patryk Peszko
@@ -12,100 +11,85 @@ authors:
12
11
  autorequire:
13
12
  bindir: bin
14
13
  cert_chain: []
15
- date: 2012-11-06 00:00:00.000000000 Z
14
+ date: 2013-06-21 00:00:00.000000000 Z
16
15
  dependencies:
17
16
  - !ruby/object:Gem::Dependency
18
17
  name: activerecord
19
18
  requirement: !ruby/object:Gem::Requirement
20
- none: false
21
19
  requirements:
22
- - - ! '>='
20
+ - - '>='
23
21
  - !ruby/object:Gem::Version
24
22
  version: 2.3.0
25
23
  type: :runtime
26
24
  prerelease: false
27
25
  version_requirements: !ruby/object:Gem::Requirement
28
- none: false
29
26
  requirements:
30
- - - ! '>='
27
+ - - '>='
31
28
  - !ruby/object:Gem::Version
32
29
  version: 2.3.0
33
30
  - !ruby/object:Gem::Dependency
34
31
  name: bundler
35
32
  requirement: !ruby/object:Gem::Requirement
36
- none: false
37
33
  requirements:
38
- - - ! '>='
34
+ - - '>='
39
35
  - !ruby/object:Gem::Version
40
36
  version: '0'
41
37
  type: :development
42
38
  prerelease: false
43
39
  version_requirements: !ruby/object:Gem::Requirement
44
- none: false
45
40
  requirements:
46
- - - ! '>='
41
+ - - '>='
47
42
  - !ruby/object:Gem::Version
48
43
  version: '0'
49
44
  - !ruby/object:Gem::Dependency
50
45
  name: rdoc
51
46
  requirement: !ruby/object:Gem::Requirement
52
- none: false
53
47
  requirements:
54
- - - ! '>='
48
+ - - '>='
55
49
  - !ruby/object:Gem::Version
56
50
  version: 2.4.2
57
51
  type: :development
58
52
  prerelease: false
59
53
  version_requirements: !ruby/object:Gem::Requirement
60
- none: false
61
54
  requirements:
62
- - - ! '>='
55
+ - - '>='
63
56
  - !ruby/object:Gem::Version
64
57
  version: 2.4.2
65
58
  - !ruby/object:Gem::Dependency
66
59
  name: rake
67
60
  requirement: !ruby/object:Gem::Requirement
68
- none: false
69
61
  requirements:
70
- - - ! '>='
62
+ - - '>='
71
63
  - !ruby/object:Gem::Version
72
64
  version: '0'
73
65
  type: :development
74
66
  prerelease: false
75
67
  version_requirements: !ruby/object:Gem::Requirement
76
- none: false
77
68
  requirements:
78
- - - ! '>='
69
+ - - '>='
79
70
  - !ruby/object:Gem::Version
80
71
  version: '0'
81
72
  - !ruby/object:Gem::Dependency
82
73
  name: sqlite3
83
74
  requirement: !ruby/object:Gem::Requirement
84
- none: false
85
75
  requirements:
86
- - - ! '>='
76
+ - - '>='
87
77
  - !ruby/object:Gem::Version
88
78
  version: '0'
89
79
  type: :development
90
80
  prerelease: false
91
81
  version_requirements: !ruby/object:Gem::Requirement
92
- none: false
93
82
  requirements:
94
- - - ! '>='
83
+ - - '>='
95
84
  - !ruby/object:Gem::Version
96
85
  version: '0'
97
- description: ! 'Bit fields for ActiveRecord:
98
-
86
+ description: |
87
+ Bit fields for ActiveRecord:
99
88
  This gem lets you use a single integer column in an ActiveRecord model
100
-
101
89
  to store a collection of boolean attributes (flags). Each flag can be used
102
-
103
90
  almost in the same way you would use any boolean attribute on an
104
-
105
91
  ActiveRecord object.
106
-
107
- '
108
- email:
92
+ email: peter.boling@gmail.com
109
93
  executables: []
110
94
  extensions: []
111
95
  extra_rdoc_files: []
@@ -124,37 +108,39 @@ files:
124
108
  - gemfiles/Gemfile.activerecord-3.2.x
125
109
  - init.rb
126
110
  - lib/flag_shih_tzu.rb
111
+ - lib/flag_shih_tzu/validators.rb
127
112
  - lib/flag_shih_tzu/version.rb
128
113
  - test/database.yml
129
114
  - test/flag_shih_tzu_test.rb
130
115
  - test/schema.rb
131
116
  - test/test_helper.rb
132
117
  homepage: https://github.com/xing/flag_shih_tzu
133
- licenses: []
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
134
121
  post_install_message:
135
122
  rdoc_options: []
136
123
  require_paths:
137
124
  - lib
138
125
  required_ruby_version: !ruby/object:Gem::Requirement
139
- none: false
140
126
  requirements:
141
- - - ! '>='
127
+ - - '>='
142
128
  - !ruby/object:Gem::Version
143
129
  version: '0'
144
130
  required_rubygems_version: !ruby/object:Gem::Requirement
145
- none: false
146
131
  requirements:
147
- - - ! '>='
132
+ - - '>='
148
133
  - !ruby/object:Gem::Version
149
134
  version: '0'
150
135
  requirements: []
151
136
  rubyforge_project:
152
- rubygems_version: 1.8.24
137
+ rubygems_version: 2.0.3
153
138
  signing_key:
154
- specification_version: 3
139
+ specification_version: 4
155
140
  summary: Bit fields for ActiveRecord
156
141
  test_files:
157
142
  - test/database.yml
158
143
  - test/flag_shih_tzu_test.rb
159
144
  - test/schema.rb
160
145
  - test/test_helper.rb
146
+ has_rdoc: