flag_shih_tzu 0.3.2 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
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: