flag_shih_tzu 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Binary file
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  test/debug.log
2
+ test/flag_shih_tzu_plugin.sqlite3.db
2
3
  coverage
3
4
  .idea
4
5
  .idea/*
@@ -0,0 +1,17 @@
1
+ Version 0.3.0 - NOV.05.2012 - first version maintained by Peter Boling
2
+
3
+ * ClassWithHasFlags.set_#{flag_name}_sql # Returns the sql string for setting a flag for use in customized SQL
4
+ * ClassWithHasFlags.unset_#{flag_name}_sql # Returns the sql string for unsetting a flag for use in customized SQL
5
+ * ClassWithHasFlags.flag_columns # Returns the column_names used by FlagShihTzu as bit fields
6
+ * has_flags :strict => true # DuplicateFlagColumnException raised when a single DB column is declared as a flag column twice
7
+ * Less verbosity for expected conditions when the DB connection for the class is unavailable.
8
+ * Tests for additional features, but does not change any behavior of 0.2.3 / 0.2.4 by default.
9
+ * Easily migrate from 0.2.3 / 0.2.4. Goal is no code changes required. Minor version bump to encourage caution.
10
+
11
+ Version 0.2.4 - NOV.05.2012 - released last few changes from XING master
12
+
13
+ * Fix deprecation warning for set_table_name
14
+ * Optional bang methods
15
+ * Complete Ruby 1.9(\.[^1]) and Rails 3.2.X compatibility
16
+
17
+ Version 0.2.3 - last version maintained by XING AG
@@ -1,3 +1,22 @@
1
+ == Change of Ownership and 0.3.0 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
+
1
20
  =FlagShihTzu
2
21
 
3
22
  Bit fields for ActiveRecord
@@ -28,7 +47,7 @@ http://en.wikipedia.org/wiki/Shih_Tzu
28
47
 
29
48
  ==Build status
30
49
 
31
- {<img src="https://secure.travis-ci.org/xing/flag_shih_tzu.png" />}[http://travis-ci.org/xing/flag_shih_tzu]
50
+ {<img src="https://secure.travis-ci.org/pboling/flag_shih_tzu.png" />}[http://travis-ci.org/pboling/flag_shih_tzu]
32
51
 
33
52
 
34
53
  ==Prerequisites
@@ -163,7 +182,14 @@ on Spaceship:
163
182
  Spaceship#electrolytes=
164
183
  Spaceship#electrolytes_changed?
165
184
 
166
- Opionally, you can set the <tt>:bang_methods</tt> option to true to enable the bang methods:
185
+ ===Generated class methods
186
+
187
+ Calling +has_flags+ as shown above creates the following class methods
188
+ on Spaceship:
189
+
190
+ Spaceship.flag_columns # [:features, :crew]
191
+
192
+ Optionally, you can set the <tt>:bang_methods</tt> option to true to enable the bang methods:
167
193
 
168
194
  Spaceship#electrolytes!
169
195
  Spaceship#not_electrolytes!
@@ -290,15 +316,19 @@ specify which config from <tt>test/database.yml</tt> to use, e.g.:
290
316
 
291
317
  ==Authors
292
318
 
319
+ {Peter Boling}[http://github.com/pboling],
293
320
  {Patryk Peszko}[http://github.com/ppeszko],
294
321
  {Sebastian Roebke}[http://github.com/boosty],
295
322
  {David Anderson}[http://github.com/alpinegizmo],
296
323
  {Tim Payton}[http://github.com/dizzy42]
297
324
  and a helpful group of
298
- {contributors}[https://github.com/xing/flag_shih_tzu/contributors].
325
+ {contributors}[https://github.com/pboling/flag_shih_tzu/contributors].
299
326
  Thanks!
300
327
 
301
- Please find out more about our work in our
328
+ Find out more about Peter Boling's work
329
+ {PeterBoling.com}[http://peterboling.com/].
330
+
331
+ Find out more about XING
302
332
  {Devblog}[http://devblog.xing.com/].
303
333
 
304
334
 
@@ -306,6 +336,7 @@ Please find out more about our work in our
306
336
 
307
337
  The MIT License
308
338
 
339
+ Copyright (c) 2012 {Peter Boling}[http://www.peterboling.com/]
309
340
  Copyright (c) 2011 {XING AG}[http://www.xing.com/]
310
341
 
311
342
  Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,5 +27,6 @@ ActiveRecord object.
27
27
  s.add_development_dependency "bundler"
28
28
  s.add_development_dependency "rdoc", ">= 2.4.2"
29
29
  s.add_development_dependency "rake"
30
+ #s.add_development_dependency "rcov"
30
31
  s.add_development_dependency "sqlite3"
31
32
  end
@@ -10,13 +10,15 @@ module FlagShihTzu
10
10
 
11
11
  def self.included(base)
12
12
  base.extend(ClassMethods)
13
- base.class_attribute :flag_options
14
- base.class_attribute :flag_mapping
13
+ base.class_attribute :flag_options unless defined?(base.flag_options)
14
+ base.class_attribute :flag_mapping unless defined?(base.flag_mapping)
15
+ base.class_attribute :flag_columns unless defined?(base.flag_columns)
15
16
  end
16
17
 
17
18
  class IncorrectFlagColumnException < Exception; end
18
19
  class NoSuchFlagQueryModeException < Exception; end
19
20
  class NoSuchFlagException < Exception; end
21
+ class DuplicateFlagColumnException < Exception; end
20
22
 
21
23
  module ClassMethods
22
24
  def has_flags(*args)
@@ -24,7 +26,8 @@ module FlagShihTzu
24
26
  opts = {
25
27
  :named_scopes => true,
26
28
  :column => DEFAULT_COLUMN_NAME,
27
- :flag_query_mode => :in_list
29
+ :flag_query_mode => :in_list,
30
+ :strict => false
28
31
  }.update(opts)
29
32
  colmn = opts[:column].to_s
30
33
 
@@ -36,8 +39,14 @@ module FlagShihTzu
36
39
 
37
40
  # the mappings are stored in this class level hash and apply per-column
38
41
  self.flag_mapping ||= {}
42
+ #If we already have an instance of the same column in the flag_mapping, then there is a double definition on a column
43
+ raise DuplicateFlagColumnException if opts[:strict] && !self.flag_mapping[colmn].nil?
39
44
  self.flag_mapping[colmn] ||= {}
40
45
 
46
+ # keep track of which flag columns are defined on this class
47
+ self.flag_columns ||= []
48
+ self.flag_columns << colmn
49
+
41
50
  flag_hash.each do |flag_key, flag_name|
42
51
  raise ArgumentError, "has_flags: flag keys should be positive integers, and #{flag_key} is not" unless is_valid_flag_key(flag_key)
43
52
  raise ArgumentError, "has_flags: flag names should be symbols, and #{flag_name} is not" unless is_valid_flag_name(flag_name)
@@ -75,6 +84,14 @@ module FlagShihTzu
75
84
  def self.not_#{flag_name}_condition
76
85
  sql_condition_for_flag(:#{flag_name}, '#{colmn}', false)
77
86
  end
87
+
88
+ def self.set_#{flag_name}_sql
89
+ sql_set_for_flag(:#{flag_name}, '#{colmn}', true)
90
+ end
91
+
92
+ def self.unset_#{flag_name}_sql
93
+ sql_set_for_flag(:#{flag_name}, '#{colmn}', false)
94
+ end
78
95
  EVAL
79
96
 
80
97
  # Define bancg methods when requested
@@ -121,39 +138,42 @@ module FlagShihTzu
121
138
  return options, add_options
122
139
  end
123
140
 
124
- def check_flag_column(colmn, table_name = self.table_name)
141
+ def check_flag_column(colmn, custom_table_name = self.table_name)
125
142
  # If you aren't using ActiveRecord (eg. you are outside rails) then do not fail here
126
143
  # 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
127
144
  has_ar = !!defined?(ActiveRecord) && self.respond_to?(:descends_from_active_record?)
128
145
  # Supposedly Rails 2.3 takes care of this, but this precaution is needed for backwards compatibility
129
- has_table = has_ar ? connection.tables.include?(table_name) : true
130
-
131
- logger.warn("Error: Table '#{table_name}' doesn't exist") and return false unless has_table
132
-
133
- if !has_ar || (has_ar && has_table)
134
- if found_column = columns.find {|column| column.name == colmn}
135
- raise IncorrectFlagColumnException, "Warning: Column '#{colmn}'must be of type integer in order to use FlagShihTzu" unless found_column.type == :integer
136
- else
137
- # Do not raise an exception since the migration to add the flags column might still be pending
138
- logger.warn("Warning: Table '#{table_name}' must have an integer column named '#{colmn}' in order to use FlagShihTzu") and return false
146
+ has_table = has_ar ? ActiveRecord::Base.connection.tables.include?(custom_table_name) : true
147
+
148
+ if has_table
149
+ found_column = columns.find {|column| column.name == colmn}
150
+ #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
151
+ #If the column is there but is of the wrong type, then we must fail, because flag_shih_tzu will not work
152
+ if found_column.nil?
153
+ logger.warn("Error: Column '#{colmn}' doesn't exist on table '#{custom_table_name}'. Did you forget to run migrations?") and return false
154
+ elsif found_column.type != :integer
155
+ raise IncorrectFlagColumnException.new("Table '#{custom_table_name}' must have an integer column named '#{colmn}' in order to use FlagShihTzu.") and return false
139
156
  end
157
+ else
158
+ # ActiveRecord gem probably hasn't loaded yet?
159
+ logger.warn("FlagShihTzu#has_flags: Table '#{custom_table_name}' doesn't exist. Have all migrations been run?") and return false
140
160
  end
141
161
 
142
162
  true
143
163
  end
144
164
 
145
- def sql_condition_for_flag(flag, colmn, enabled = true, table_name = self.table_name)
165
+ def sql_condition_for_flag(flag, colmn, enabled = true, custom_table_name = self.table_name)
146
166
  check_flag(flag, colmn)
147
167
 
148
168
  if flag_options[colmn][:flag_query_mode] == :bit_operator
149
169
  # use & bit operator directly in the SQL query.
150
170
  # This has the drawback of not using an index on the flags colum.
151
- "(#{table_name}.#{colmn} & #{flag_mapping[colmn][flag]} = #{enabled ? flag_mapping[colmn][flag] : 0})"
171
+ "(#{custom_table_name}.#{colmn} & #{flag_mapping[colmn][flag]} = #{enabled ? flag_mapping[colmn][flag] : 0})"
152
172
  elsif flag_options[colmn][:flag_query_mode] == :in_list
153
173
  # use IN() operator in the SQL query.
154
174
  # This has the drawback of becoming a big query when you have lots of flags.
155
175
  neg = enabled ? "" : "not "
156
- "(#{table_name}.#{colmn} #{neg}in (#{sql_in_for_flag(flag, colmn).join(',')}))"
176
+ "(#{custom_table_name}.#{colmn} #{neg}in (#{sql_in_for_flag(flag, colmn).join(',')}))"
157
177
  else
158
178
  raise NoSuchFlagQueryModeException
159
179
  end
@@ -165,6 +185,12 @@ module FlagShihTzu
165
185
  num = 2 ** flag_mapping[flag_options[colmn][:column]].length
166
186
  (1..num).select {|i| i & val == val}
167
187
  end
188
+
189
+ def sql_set_for_flag(flag, colmn, enabled = true, custom_table_name = self.table_name)
190
+ check_flag(flag, colmn)
191
+
192
+ "#{custom_table_name}.#{colmn} = #{custom_table_name}.#{colmn} #{enabled ? "| " : "& ~" }#{flag_mapping[colmn][flag]}"
193
+ end
168
194
 
169
195
  def is_valid_flag_key(flag_key)
170
196
  flag_key > 0 && flag_key == flag_key.to_i
@@ -1,3 +1,3 @@
1
1
  module FlagShihTzu
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -45,6 +45,15 @@ class SpaceshipWith2CustomFlagsColumn < ActiveRecord::Base
45
45
  has_flags({ 1 => :jeanlucpicard, 2 => :dajanatroj }, :column => 'commanders')
46
46
  end
47
47
 
48
+ class SpaceshipWith3CustomFlagsColumn < ActiveRecord::Base
49
+ self.table_name = 'spaceships_with_3_custom_flags_column'
50
+ include FlagShihTzu
51
+
52
+ has_flags({ 1 => :warpdrive, 2 => :hyperspace }, :column => 'engines')
53
+ has_flags({ 1 => :photon, 2 => :laser, 3 => :ion_cannon, 4 => :particle_beam }, :column => 'weapons')
54
+ has_flags({ 1 => :power, 2 => :anti_ax_routine }, :column => 'hal3000')
55
+ end
56
+
48
57
  class SpaceshipWithBitOperatorQueryMode < ActiveRecord::Base
49
58
  self.table_name = 'spaceships'
50
59
  include FlagShihTzu
@@ -117,6 +126,21 @@ class FlagShihTzuClassMethodsTest < Test::Unit::TestCase
117
126
  end
118
127
  end
119
128
 
129
+ def test_has_flags_should_raise_an_exception_when_flag_name_method_defined_by_flagshitzu_if_strict
130
+ assert_raises FlagShihTzu::DuplicateFlagColumnException do
131
+ eval(<<-EOF
132
+ class SpaceshipWithAlreadyUsedMethodByFlagshitzuStrict < ActiveRecord::Base
133
+ self.table_name = 'spaceships_with_2_custom_flags_column'
134
+ include FlagShihTzu
135
+
136
+ has_flags({ 1 => :jeanluckpicard }, :column => 'bits', :strict => true)
137
+ has_flags({ 1 => :jeanluckpicard }, :column => 'bits', :strict => true)
138
+ end
139
+ EOF
140
+ )
141
+ end
142
+ end
143
+
120
144
  def test_has_flags_should_not_raise_an_exception_when_flag_name_method_defined_by_flagshitzu
121
145
  assert_nothing_raised ArgumentError do
122
146
  eval(<<-EOF
@@ -553,4 +577,20 @@ class FlagShihTzuDerivedClassTest < Test::Unit::TestCase
553
577
  spaceship.not_warpdrive!
554
578
  assert !spaceship.warpdrive
555
579
  end
580
+
581
+ def test_should_return_a_sql_set_method_for_flag
582
+ assert_equal "spaceships.flags = spaceships.flags | 1", Spaceship.send( :sql_set_for_flag, :warpdrive, 'flags', true)
583
+ assert_equal "spaceships.flags = spaceships.flags & ~1", Spaceship.send( :sql_set_for_flag, :warpdrive, 'flags', false)
584
+ end
585
+
586
+ end
587
+
588
+ class FlagShihTzuClassMethodsTest < Test::Unit::TestCase
589
+
590
+ def test_should_track_columns_used_by_FlagShihTzu
591
+ assert_equal Spaceship.flag_columns, ['flags']
592
+ assert_equal SpaceshipWith2CustomFlagsColumn.flag_columns, ['bits', 'commanders']
593
+ assert_equal SpaceshipWith3CustomFlagsColumn.flag_columns, ['engines', 'weapons', 'hal3000']
594
+ end
595
+
556
596
  end
@@ -14,6 +14,12 @@ ActiveRecord::Schema.define(:version => 0) do
14
14
  t.integer :commanders, :null => false, :default => 0
15
15
  end
16
16
 
17
+ create_table :spaceships_with_3_custom_flags_column, :force => true do |t|
18
+ t.integer :engines, :null => false, :default => 0
19
+ t.integer :weapons, :null => false, :default => 0
20
+ t.integer :hal3000, :null => false, :default => 0
21
+ end
22
+
17
23
  create_table :spaceships_without_flags_column, :force => true do |t|
18
24
  end
19
25
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flag_shih_tzu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -110,8 +110,10 @@ executables: []
110
110
  extensions: []
111
111
  extra_rdoc_files: []
112
112
  files:
113
+ - .DS_Store
113
114
  - .gitignore
114
115
  - .travis.yml
116
+ - CHANGELOG
115
117
  - Gemfile
116
118
  - README.rdoc
117
119
  - Rakefile