searchgasm 0.9.6 → 0.9.7

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.
Files changed (66) hide show
  1. data/CHANGELOG +2 -0
  2. data/Manifest +34 -21
  3. data/{README.mdown → README.rdoc} +96 -63
  4. data/Rakefile +1 -1
  5. data/examples/README.rdoc +4 -0
  6. data/lib/searchgasm/active_record/associations.rb +40 -42
  7. data/lib/searchgasm/active_record/base.rb +75 -61
  8. data/lib/searchgasm/condition/base.rb +127 -0
  9. data/lib/searchgasm/condition/begins_with.rb +20 -0
  10. data/lib/searchgasm/condition/child_of.rb +11 -0
  11. data/lib/searchgasm/condition/contains.rb +20 -0
  12. data/lib/searchgasm/condition/descendant_of.rb +24 -0
  13. data/lib/searchgasm/condition/does_not_equal.rb +28 -0
  14. data/lib/searchgasm/condition/ends_with.rb +20 -0
  15. data/lib/searchgasm/condition/equals.rb +20 -0
  16. data/lib/searchgasm/condition/greater_than.rb +25 -0
  17. data/lib/searchgasm/condition/greater_than_or_equal_to.rb +20 -0
  18. data/lib/searchgasm/condition/inclusive_descendant_of.rb +13 -0
  19. data/lib/searchgasm/condition/keywords.rb +33 -0
  20. data/lib/searchgasm/condition/less_than.rb +25 -0
  21. data/lib/searchgasm/condition/less_than_or_equal_to.rb +20 -0
  22. data/lib/searchgasm/condition/sibling_of.rb +16 -0
  23. data/lib/searchgasm/condition/tree.rb +16 -0
  24. data/lib/searchgasm/conditions/base.rb +221 -0
  25. data/lib/searchgasm/conditions/protection.rb +30 -0
  26. data/lib/searchgasm/config.rb +137 -0
  27. data/lib/searchgasm/helpers/form_helper.rb +159 -0
  28. data/lib/searchgasm/helpers/search_helper.rb +178 -0
  29. data/lib/searchgasm/helpers/utilities_helper.rb +125 -0
  30. data/lib/searchgasm/search/base.rb +73 -179
  31. data/lib/searchgasm/search/conditions.rb +42 -166
  32. data/lib/searchgasm/search/ordering.rb +149 -0
  33. data/lib/searchgasm/search/pagination.rb +69 -0
  34. data/lib/searchgasm/search/protection.rb +61 -0
  35. data/lib/searchgasm/utilities.rb +30 -0
  36. data/lib/searchgasm/version.rb +44 -47
  37. data/lib/searchgasm.rb +57 -21
  38. data/searchgasm.gemspec +71 -46
  39. data/test/test_active_record_associations.rb +1 -1
  40. data/test/test_active_record_base.rb +4 -4
  41. data/test/test_condition.rb +143 -0
  42. data/test/{test_searchgasm_conditions.rb → test_conditions_base.rb} +43 -33
  43. data/test/test_search_base.rb +189 -0
  44. data/test/test_search_ordering.rb +91 -0
  45. data/test/test_search_pagination.rb +56 -0
  46. data/test/test_search_protection.rb +35 -0
  47. metadata +70 -45
  48. data/lib/searchgasm/search/condition.rb +0 -105
  49. data/lib/searchgasm/search/condition_types/begins_with_condition.rb +0 -26
  50. data/lib/searchgasm/search/condition_types/child_of_condition.rb +0 -17
  51. data/lib/searchgasm/search/condition_types/contains_condition.rb +0 -26
  52. data/lib/searchgasm/search/condition_types/descendant_of_condition.rb +0 -30
  53. data/lib/searchgasm/search/condition_types/does_not_equal_condition.rb +0 -34
  54. data/lib/searchgasm/search/condition_types/ends_with_condition.rb +0 -26
  55. data/lib/searchgasm/search/condition_types/equals_condition.rb +0 -26
  56. data/lib/searchgasm/search/condition_types/greater_than_condition.rb +0 -31
  57. data/lib/searchgasm/search/condition_types/greater_than_or_equal_to_condition.rb +0 -26
  58. data/lib/searchgasm/search/condition_types/inclusive_descendant_of_condition.rb +0 -19
  59. data/lib/searchgasm/search/condition_types/keywords_condition.rb +0 -39
  60. data/lib/searchgasm/search/condition_types/less_than_condition.rb +0 -31
  61. data/lib/searchgasm/search/condition_types/less_than_or_equal_to_condition.rb +0 -26
  62. data/lib/searchgasm/search/condition_types/sibling_of_condition.rb +0 -22
  63. data/lib/searchgasm/search/condition_types/tree_condition.rb +0 -20
  64. data/lib/searchgasm/search/utilities.rb +0 -34
  65. data/test/test_searchgasm_base.rb +0 -185
  66. data/test/test_searchgasm_condition_types.rb +0 -143
@@ -1,171 +1,47 @@
1
- module BinaryLogic
2
- module Searchgasm
3
- module Search
4
- class Conditions
5
- include Utilities
6
-
7
- attr_accessor :klass, :protect, :relationship_name, :scope
8
-
9
- class << self
10
- def register_condition(klass)
11
- raise(ArgumentError, "You can only register conditions that extend BinaryLogic::Searchgasm::Search::ConditionTypes::Condition") unless klass.ancestors.include?(Condition)
12
- conditions << klass unless conditions.include?(klass)
13
- end
14
-
15
- def conditions
16
- @@conditions ||= []
17
- end
18
-
19
- def needed?(klass, conditions)
20
- if conditions.is_a?(Hash)
21
- conditions.stringify_keys.keys.each do |condition|
22
- return true unless klass.column_names.include?(condition)
23
- end
24
- end
25
-
26
- false
27
- end
1
+ module Searchgasm
2
+ module Search
3
+ module Conditions
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ alias_method_chain :initialize, :conditions
7
+ alias_method_chain :conditions=, :conditions
8
+ alias_method_chain :include, :conditions
9
+ alias_method_chain :sanitize, :conditions
28
10
  end
29
-
30
- def initialize(klass, init_values = {})
31
- self.klass = klass
32
- add_klass_conditions!
33
- add_column_conditions!
34
- add_associations!
35
- self.value = init_values
36
- end
37
-
38
- # Setup methods for searching
39
- [:all, :average, :calculate, :count, :find, :first, :maximum, :minimum, :sum].each do |method|
40
- class_eval <<-end_eval
41
- def #{method}(*args)
42
- self.value = args.extract_options!
43
- args << {:conditions => sanitize}
44
- klass.#{method}(*args)
45
- end
46
- end_eval
47
- end
48
-
49
- def assert_valid_values(values)
50
- keys = condition_names.collect { |condition_name| condition_name.to_sym }
51
- keys += klass.reflect_on_all_associations.collect { |association| association.name }
52
- values.symbolize_keys.assert_valid_keys(keys)
53
- end
54
-
55
- def associations
56
- objects.select { |object| object.is_a?(self.class) }
57
- end
58
-
59
- def condition_names
60
- @condition_names ||= []
61
- end
62
-
63
- def includes
64
- i = []
65
- associations.each do |association|
66
- association_includes = association.includes
67
- i << (association_includes.blank? ? association.relationship_name.to_sym : {association.relationship_name.to_sym => association_includes})
68
- end
69
- i.blank? ? nil : (i.size == 1 ? i.first : i)
70
- end
71
-
72
- def objects
73
- @objects ||= []
74
- end
75
-
76
- def protect?
77
- protect == true
78
- end
79
-
80
- def sanitize
81
- conditions = merge_conditions(*objects.collect { |object| object.sanitize })
82
- return scope if conditions.blank?
83
- merge_conditions(conditions, scope)
84
- end
85
-
86
- def value=(values)
87
- case values
88
- when Hash
89
- assert_valid_values(values)
90
- values.each { |condition, value| send("#{condition}=", value) }
91
- else
92
- raise(ArgumentError, "You can not set a scope or pass SQL while the search is being protected") if protect?
93
- self.scope = values
94
- end
95
- end
96
-
97
- def value
98
- values_hash = {}
99
- objects.each do |object|
100
- next unless object.explicitly_set_value?
101
- values_hash[object.name.to_sym] = object.value
102
- end
103
- values_hash
11
+ end
12
+
13
+ def initialize_with_conditions(klass, init_options = {})
14
+ self.conditions = Searchgasm::Conditions::Base.new(klass)
15
+ initialize_without_conditions(klass, init_options)
16
+ end
17
+
18
+ # Sets conditions on the search. Accepts a hash or a Searchgasm::Conditions::Base object.
19
+ def conditions_with_conditions=(values)
20
+ case values
21
+ when Searchgasm::Conditions::Base
22
+ @conditions = values
23
+ else
24
+ @conditions.conditions = values
104
25
  end
105
-
106
- private
107
- def add_associations!
108
- klass.reflect_on_all_associations.each do |association|
109
- self.class.class_eval <<-end_eval
110
- def #{association.name}
111
- if @#{association.name}.nil?
112
- @#{association.name} = self.class.new(#{association.class_name})
113
- @#{association.name}.relationship_name = "#{association.name}"
114
- self.objects << @#{association.name}
115
- end
116
- @#{association.name}
117
- end
118
-
119
- def #{association.name}=(value); #{association.name}.value = value; end
120
- def reset_#{association.name}!; objects.delete(#{association.name}); @#{association.name} = nil; end
121
- end_eval
122
- end
123
- end
124
-
125
- def add_column_conditions!
126
- klass.columns.each do |column|
127
- self.class.conditions.each do |condition|
128
- name = condition.name_for_column(column)
129
- next if name.blank?
130
- add_condition!(condition, name, column)
131
- condition.aliases_for_column(column).each { |alias_name| add_condition_alias!(alias_name, name) }
132
- end
133
- end
134
- end
135
-
136
- def add_condition!(condition, name, column = nil)
137
- self.condition_names << name
138
- self.class.class_eval <<-end_eval
139
- def #{name}_object
140
- if @#{name}.nil?
141
- @#{name} = #{condition.name}.new(klass#{column.nil? ? "" : ", \"#{column.name}\""})
142
- self.objects << @#{name}
143
- end
144
- @#{name}
145
- end
146
-
147
- def #{name}; #{name}_object.value; end
148
- def #{name}=(value); #{name}_object.value = value; end
149
- def reset_#{name}!; objects.delete(#{name}_object); @#{name} = nil; end
150
- end_eval
151
- end
152
-
153
- def add_condition_alias!(alias_name, name)
154
- self.condition_names << alias_name
155
- self.class.class_eval do
156
- alias_method alias_name, name
157
- alias_method "#{alias_name}=", "#{name}="
158
- end
159
- end
160
-
161
- def add_klass_conditions!
162
- self.class.conditions.each do |condition|
163
- name = condition.name_for_klass(klass)
164
- next if name.blank?
165
- add_condition!(condition, name)
166
- condition.aliases_for_klass(klass).each { |alias_name| add_condition_alias!(alias_name, name) }
167
- end
168
- end
26
+ end
27
+
28
+ def include_with_conditions
29
+ includes = [include_without_conditions, conditions.includes].flatten.compact.uniq
30
+ includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
31
+ end
32
+
33
+ def sanitize_with_conditions(for_method = nil)
34
+ find_options = sanitize_without_conditions(for_method)
35
+ find_options[:conditions] = find_options[:conditions].sanitize if find_options[:conditions]
36
+ find_options
37
+ end
38
+
39
+ def scope
40
+ conditions.scope
41
+ end
42
+
43
+ def scope=(value)
44
+ conditions.scope = value
169
45
  end
170
46
  end
171
47
  end
@@ -0,0 +1,149 @@
1
+ module Searchgasm
2
+ module Search
3
+ # = Search Ordering
4
+ #
5
+ # The purpose of this module is to provide easy ordering for your searches. All that these options do is
6
+ # build :order for you. This plays a huge part in ordering your data on the interface. See Searchgasm::Helpers::SearchHelper for more information.
7
+ module Ordering
8
+ def self.included(klass)
9
+ klass.class_eval do
10
+ alias_method_chain :include, :ordering
11
+ alias_method_chain :order=, :ordering
12
+ end
13
+ end
14
+
15
+ def include_with_ordering
16
+ includes = [include_without_ordering, order_by_includes].flatten.compact.uniq
17
+ includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
18
+ end
19
+
20
+ def order_with_ordering=(value)
21
+ @order_by = nil
22
+ self.order_without_ordering = value
23
+ end
24
+
25
+ # Convenience method for determining if the ordering is ascending
26
+ def asc?
27
+ !desc?
28
+ end
29
+
30
+ # Convenience method for determining if the ordering is descending
31
+ def desc?
32
+ order_as == "DESC"
33
+ end
34
+
35
+ # Determines how the search is being ordered: as DESC or ASC
36
+ def order_as
37
+ return "ASC" if order.blank?
38
+ order =~ /ASC$/i ? "ASC" : "DESC"
39
+ end
40
+
41
+ # Sets how the results will be ordered: ASC or DESC
42
+ def order_as=(value)
43
+ value = value.to_s.upcase
44
+ raise(ArgumentError, "order_as only accepts a string as ASC or DESC") unless ["ASC", "DESC"].include?(value)
45
+
46
+ if order.blank?
47
+ self.order = order_by_to_order(order_by, value)
48
+ else
49
+ self.order.gsub!(/(ASC|DESC)/i, value)
50
+ end
51
+
52
+ value
53
+ end
54
+
55
+ # Determines by what columns the search is being ordered. This is nifty in that is reverse engineers the order SQL to determine this, only
56
+ # if you haven't explicitly set the order_by option yourself.
57
+ def order_by
58
+ return @order_by if @order_by
59
+
60
+ if !order.blank?
61
+ # Reversege engineer order, only go 1 level deep with relationships, anything beyond that is probably excessive and not good for performance
62
+ order_parts = order.split(",").collect do |part|
63
+ part.strip!
64
+ part.gsub!(/ (ASC|DESC)$/i, "").gsub!(/(.*)\./, "")
65
+ table_name = ($1 ? $1.gsub(/[^a-z0-9_]/i, "") : nil)
66
+ part.gsub!(/[^a-z0-9_]/i, "")
67
+ reflection = nil
68
+ if table_name && table_name != klass.table_name
69
+ reflection = klass.reflect_on_association(table_name.to_sym) || klass.reflect_on_association(table_name.singularize.to_sym)
70
+ next unless reflection
71
+ {reflection.name.to_s => part}
72
+ else
73
+ part
74
+ end
75
+ end.compact
76
+ order_parts.size <= 1 ? order_parts.first : order_parts
77
+ else
78
+ klass.primary_key
79
+ end
80
+ end
81
+
82
+ # Lets you set how to order the data
83
+ #
84
+ # === Examples
85
+ #
86
+ # In these examples "ASC" is determined by the value of order_as
87
+ #
88
+ # order_by = :id # => users.id ASC
89
+ # order_by = [:id, name] # => users.id ASC, user.name ASC
90
+ # order_by = [:id, {:user_group => :name}] # => users.id ASC, user_groups.name ASC
91
+ def order_by=(value)
92
+ @order_by = get_order_by_value(value)
93
+ @order = order_by_to_order(@order_by, order_as) # use @order so @order_by doesnt get reset
94
+ @order_by
95
+ end
96
+
97
+ # Returns the includes neccessary for the "order" statement so that we don't get an SQL error
98
+ def order_by_includes
99
+ @order_by_includes ||= []
100
+ @order_by_includes.compact!
101
+ @order_by_includes.uniq!
102
+ @order_by_includes
103
+ end
104
+
105
+ private
106
+ def order_by_to_order(order_by, order_as, alt_klass = nil, new_includes = [])
107
+ table_name = (alt_klass || klass).table_name
108
+ sql_parts = []
109
+
110
+ case order_by
111
+ when Array
112
+ order_by.each { |part| sql_parts << order_by_to_order(part, order_as) }
113
+ when Hash
114
+ raise(ArgumentError, "when passing a hash to order_by you must only have 1 key: {:user_group => :name} not {:user_group => :name, :user_group => :id}. The latter should be [{:user_group => :name}, {:user_group => :id}]") if order_by.keys.size != 1
115
+ k = order_by.keys.first
116
+ v = order_by.values.first
117
+ new_includes << k.to_sym
118
+ sql_parts << order_by_to_order(v, order_as, k.to_s.classify.constantize, new_includes)
119
+ when Symbol, String
120
+ new_include = build_order_by_includes(new_includes)
121
+ self.order_by_includes << new_include if new_include
122
+ sql_parts << "#{quote_table_name(table_name)}.#{quote_column_name(order_by)} #{order_as}"
123
+ end
124
+
125
+ sql_parts.join(", ")
126
+ end
127
+
128
+ def build_order_by_includes(includes)
129
+ return includes.first if includes.size <= 1
130
+ includes = includes.dup
131
+
132
+ key = includes.shift
133
+ {key => build_order_by_includes(includes)}
134
+ end
135
+
136
+ def get_order_by_value(value)
137
+ Marshal.load(value.unpack("m").first) rescue value
138
+ end
139
+
140
+ def quote_column_name(column_name)
141
+ klass.connection.quote_column_name(column_name)
142
+ end
143
+
144
+ def quote_table_name(table_name)
145
+ klass.connection.quote_table_name(table_name)
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,69 @@
1
+ module Searchgasm
2
+ module Search
3
+ module Pagination
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ alias_method_chain :limit=, :pagination
7
+ alias_method_chain :offset=, :pagination
8
+ alias_method :per_page, :limit
9
+ alias_method :per_page=, :limit=
10
+ end
11
+ end
12
+
13
+ def limit_with_pagination=(value)
14
+ self.limit_without_pagination = value
15
+ self.page = @page unless @page.nil? # retry page now that the limit has changed
16
+ limit
17
+ end
18
+
19
+ def offset_with_pagination=(value)
20
+ self.offset_without_pagination = value
21
+ @page = nil
22
+ offset
23
+ end
24
+
25
+ def page
26
+ return 1 if offset.blank? || limit.blank?
27
+ (offset.to_f / limit).floor + 1
28
+ end
29
+
30
+ def page=(value)
31
+ # Have to use @offset, since self.offset= resets @page
32
+ if value.nil?
33
+ @page = value
34
+ return @offset = value
35
+ end
36
+
37
+ v = value.to_i
38
+ @page = v
39
+
40
+ if limit.blank?
41
+ @offset = nil
42
+ else
43
+ v -= 1 unless v == 0
44
+ @offset = v * limit
45
+ end
46
+ value
47
+ end
48
+
49
+ def page_count
50
+ return 1 if per_page.blank? || per_page <= 0
51
+ # Letting AR caching kick in with the count query
52
+ (count / per_page.to_f).ceil
53
+ end
54
+ alias_method :page_total, :page_count
55
+
56
+ def next_page!
57
+ raise("You are on the last page") if page == page_count
58
+ self.page += 1
59
+ all
60
+ end
61
+
62
+ def prev_page!
63
+ raise("You are on the first page") if page == 1
64
+ self.page -= 1
65
+ all
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,61 @@
1
+ module Searchgasm
2
+ module Search
3
+ module Protection
4
+ # Options that are allowed when protecting against SQL injections (still checked though)
5
+ SAFE_OPTIONS = Base::SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset]
6
+
7
+ # Options that are not allowed, at all, when protecting against SQL injections
8
+ VULNERABLE_OPTIONS = Base::VALID_FIND_OPTIONS - SAFE_OPTIONS
9
+
10
+ def self.included(klass)
11
+ klass.class_eval do
12
+ attr_reader :protect
13
+ alias_method_chain :options=, :protection
14
+ end
15
+ end
16
+
17
+ def options_with_protection=(values)
18
+ return unless values.is_a?(Hash)
19
+ self.protect = values.delete(:protect) if values.has_key?(:protect) # make sure we do this first
20
+ frisk!(values) if protect?
21
+ self.options_without_protection = values
22
+ end
23
+
24
+ def protect=(value)
25
+ conditions.protect = value
26
+ @protect = value
27
+ end
28
+
29
+ def protect?
30
+ protect == true
31
+ end
32
+
33
+ private
34
+ def order_by_safe?(order_by, alt_klass = nil)
35
+ return true if order_by.blank?
36
+
37
+ k = alt_klass || klass
38
+ column_names = k.column_names
39
+
40
+ [order_by].flatten.each do |column|
41
+ case column
42
+ when Hash
43
+ return false unless k.reflect_on_association(column.keys.first.to_sym)
44
+ return false unless order_by_safe?(column.values.first, column.keys.first.to_s.classify.constantize)
45
+ when Array
46
+ return false unless order_by_safe?(column)
47
+ else
48
+ return false unless column_names.include?(column.to_s)
49
+ end
50
+ end
51
+
52
+ true
53
+ end
54
+
55
+ def frisk!(options)
56
+ options.symbolize_keys.assert_valid_keys(SAFE_OPTIONS)
57
+ raise(ArgumentError, ":order_by can only contain colum names in the string, hash, or array format") unless order_by_safe?(get_order_by_value(options[:order_by]))
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,30 @@
1
+ module Searchgasm
2
+ module Utilities # :nodoc:
3
+ private
4
+ def merge_conditions(*conditions)
5
+ options = conditions.extract_options!
6
+ conditions.delete_if { |condition| condition.blank? }
7
+ return if conditions.blank?
8
+ return conditions.first if conditions.size == 1
9
+
10
+ conditions_strs = []
11
+ conditions_subs = []
12
+
13
+ conditions.each do |condition|
14
+ next if condition.blank?
15
+ arr_condition = [condition].flatten
16
+ conditions_strs << arr_condition.first
17
+ conditions_subs += arr_condition[1..-1]
18
+ end
19
+
20
+ return if conditions_strs.blank?
21
+
22
+ join = options[:any] ? "OR" : "AND"
23
+ conditions_str = "(#{conditions_strs.join(") #{join} (")})"
24
+
25
+ return conditions_str if conditions_subs.blank?
26
+
27
+ [conditions_str, *conditions_subs]
28
+ end
29
+ end
30
+ end
@@ -21,61 +21,58 @@
21
21
  # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22
22
  # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
23
  # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
- module BinaryLogic
25
-
26
- module Searchgasm
27
-
28
- # A class for describing the current version of a library. The version
29
- # consists of three parts: the +major+ number, the +minor+ number, and the
30
- # +tiny+ (or +patch+) number.
31
- class Version
32
-
33
- include Comparable
24
+ module Searchgasm
25
+ # = Version
26
+ #
27
+ # A class for describing the current version of a library. The version
28
+ # consists of three parts: the +major+ number, the +minor+ number, and the
29
+ # +tiny+ (or +patch+) number.
30
+ class Version
34
31
 
35
- # A convenience method for instantiating a new Version instance with the
36
- # given +major+, +minor+, and +tiny+ components.
37
- def self.[](major, minor, tiny)
38
- new(major, minor, tiny)
39
- end
32
+ include Comparable
33
+
34
+ # A convenience method for instantiating a new Version instance with the
35
+ # given +major+, +minor+, and +tiny+ components.
36
+ def self.[](major, minor, tiny)
37
+ new(major, minor, tiny)
38
+ end
40
39
 
41
- attr_reader :major, :minor, :tiny
40
+ attr_reader :major, :minor, :tiny
42
41
 
43
- # Create a new Version object with the given components.
44
- def initialize(major, minor, tiny)
45
- @major, @minor, @tiny = major, minor, tiny
46
- end
42
+ # Create a new Version object with the given components.
43
+ def initialize(major, minor, tiny)
44
+ @major, @minor, @tiny = major, minor, tiny
45
+ end
47
46
 
48
- # Compare this version to the given +version+ object.
49
- def <=>(version)
50
- to_i <=> version.to_i
51
- end
47
+ # Compare this version to the given +version+ object.
48
+ def <=>(version)
49
+ to_i <=> version.to_i
50
+ end
52
51
 
53
- # Converts this version object to a string, where each of the three
54
- # version components are joined by the '.' character. E.g., 2.0.0.
55
- def to_s
56
- @to_s ||= [@major, @minor, @tiny].join(".")
57
- end
52
+ # Converts this version object to a string, where each of the three
53
+ # version components are joined by the '.' character. E.g., 2.0.0.
54
+ def to_s
55
+ @to_s ||= [@major, @minor, @tiny].join(".")
56
+ end
58
57
 
59
- # Converts this version to a canonical integer that may be compared
60
- # against other version objects.
61
- def to_i
62
- @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
63
- end
64
-
65
- def to_a
66
- [@major, @minor, @tiny]
67
- end
58
+ # Converts this version to a canonical integer that may be compared
59
+ # against other version objects.
60
+ def to_i
61
+ @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
62
+ end
63
+
64
+ def to_a
65
+ [@major, @minor, @tiny]
66
+ end
68
67
 
69
- MAJOR = 0
70
- MINOR = 9
71
- TINY = 6
68
+ MAJOR = 0
69
+ MINOR = 9
70
+ TINY = 7
72
71
 
73
- # The current version as a Version instance
74
- CURRENT = new(MAJOR, MINOR, TINY)
75
- # The current version as a String
76
- STRING = CURRENT.to_s
77
-
78
- end
72
+ # The current version as a Version instance
73
+ CURRENT = new(MAJOR, MINOR, TINY)
74
+ # The current version as a String
75
+ STRING = CURRENT.to_s
79
76
 
80
77
  end
81
78