arspy 0.0.5 → 0.0.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 (41) hide show
  1. data/README.rdoc +10 -0
  2. data/Rakefile +1 -1
  3. data/lib/arspy/class_extensions.rb +2 -2
  4. data/lib/arspy/delegators/active_record_extensions.rb +2 -2
  5. data/lib/arspy/delegators/array_extensions.rb +4 -4
  6. data/lib/arspy/delegators/association_collection_extensions.rb +4 -4
  7. data/lib/arspy/delegators/null_extensions.rb +2 -2
  8. data/lib/arspy/operators/abbreviations.rb +68 -0
  9. data/lib/arspy/operators/attribute_test/base.rb +11 -0
  10. data/lib/arspy/operators/attribute_test/float_test.rb +24 -0
  11. data/lib/arspy/operators/attribute_test/integer_test.rb +24 -0
  12. data/lib/arspy/operators/attribute_test/range_test.rb +24 -0
  13. data/lib/arspy/operators/attribute_test/regexp_test.rb +23 -0
  14. data/lib/arspy/operators/attribute_test/string_test.rb +23 -0
  15. data/lib/arspy/operators/attribute_test/unsupported_test.rb +17 -0
  16. data/lib/arspy/operators/attribute_test.rb +20 -0
  17. data/lib/arspy/operators/interpreter/abbreviated_association_interpreter.rb +22 -0
  18. data/lib/arspy/operators/interpreter/abbreviated_attribute_interpreter.rb +22 -0
  19. data/lib/arspy/operators/interpreter/association_interpreter.rb +18 -0
  20. data/lib/arspy/operators/interpreter/attribute_interpreter.rb +21 -0
  21. data/lib/arspy/operators/interpreter/base.rb +11 -0
  22. data/lib/arspy/operators/interpreter/method_interpreter.rb +14 -0
  23. data/lib/arspy/operators/interpreter/null_interpreter.rb +12 -0
  24. data/lib/arspy/operators/interpreter.rb +29 -0
  25. data/lib/arspy/operators/selector/attribute_selector.rb +33 -0
  26. data/lib/arspy/operators/selector/base.rb +12 -0
  27. data/lib/arspy/operators/selector/hash_selector.rb +30 -0
  28. data/lib/arspy/operators/selector/integer_selector.rb +17 -0
  29. data/lib/arspy/operators/selector/range_selector.rb +17 -0
  30. data/lib/arspy/operators/selector/string_selector.rb +14 -0
  31. data/lib/arspy/operators/selector/unsupported_selector.rb +17 -0
  32. data/lib/arspy/operators/selector.rb +28 -0
  33. data/lib/arspy/operators.rb +40 -111
  34. data/lib/meta_programming/object.rb +2 -2
  35. data/spec/active_record.log +4327 -0
  36. data/spec/database.rb +85 -32
  37. data/spec/list_association_spec.rb +62 -0
  38. data/spec/list_field_spec.rb +5 -0
  39. data/spec/spec_helper.rb +2 -0
  40. data/spec/with_command_spec.rb +106 -0
  41. metadata +39 -7
@@ -0,0 +1,28 @@
1
+ require 'arspy/operators/selector/base'
2
+ require 'arspy/operators/selector/attribute_selector'
3
+ require 'arspy/operators/selector/range_selector'
4
+ require 'arspy/operators/selector/integer_selector'
5
+ require 'arspy/operators/selector/string_selector'
6
+ require 'arspy/operators/selector/unsupported_selector'
7
+ require 'arspy/operators/selector/hash_selector'
8
+
9
+ module Arspy
10
+ module Operators
11
+
12
+ module Selector
13
+ @@selector_classes = [
14
+ IntegerSelector,
15
+ RangeSelector,
16
+ StringSelector,
17
+ HashSelector,
18
+ UnsupportedSelector
19
+ ]
20
+
21
+ def self.for(arg)
22
+ @@selector_classes.detect{|klass| klass.applies?(arg)}.new(arg)
23
+ end
24
+
25
+
26
+ end
27
+ end
28
+ end
@@ -1,7 +1,11 @@
1
+ require 'arspy/operators/attribute_test'
2
+ require 'arspy/operators/selector'
3
+ require 'arspy/operators/interpreter'
4
+
1
5
  module Arspy
2
6
  module Operators
3
7
 
4
- def self.list_associations(klass_or_object)
8
+ def self.list_associations(klass_or_object, *args)
5
9
  active_record_klass = klass_or_object.is_a?(Class) ? klass_or_object : klass_or_object.class
6
10
  return unless active_record_klass.ancestors.include?(ActiveRecord::Base)
7
11
  counts = {}
@@ -11,23 +15,26 @@ module Arspy
11
15
  self.format_column_association(assoc)
12
16
  end
13
17
  rows.sort!{|row1,row2| row1.first <=> row2.first}
18
+ return rows if args.include?(:data)
14
19
  self.print_matrix(rows)
15
20
  "Total: #{counts.inject(0){|sum, count| sum+count.last}} (" + counts.map{|count| "#{count.last} #{count.first}" }.join(', ') + ")"
16
21
  end
17
22
 
18
- def self.list_fields(klass_or_object)
23
+ def self.list_fields(klass_or_object, *args)
19
24
  active_record_klass = klass_or_object.is_a?(Class) ? klass_or_object : klass_or_object.class
20
25
  return unless active_record_klass.ancestors.include?(ActiveRecord::Base)
21
26
  rows = active_record_klass.columns.map do |column|
22
27
  self.format_column_field(column)
23
28
  end
24
29
  rows.sort!{|row1,row2| row1.first <=> row2.first}
30
+ return rows if args.include?(:data)
25
31
  self.print_matrix(rows)
26
32
  "Total #{active_record_klass.columns.size} field#{active_record_klass.columns.size == 1 ? '' : 's'}"
27
33
  end
28
34
 
29
35
  def self.awesome_print(klass_object_or_array, options={})
30
- AwesomePrint.new(options).puts klass_object_or_array
36
+ #AwesomePrint.new(options).puts(klass_object_or_array)
37
+ ap(klass_object_or_array)
31
38
  nil
32
39
  end
33
40
 
@@ -36,13 +43,25 @@ module Arspy
36
43
  [assoc.name.to_s, assoc.macro.to_s, "(#{assoc.options[:class_name] || assoc.name.to_s.singularize.camelize})", select_options.empty? ? '' : Hash[*select_options.flatten].inspect]
37
44
  end
38
45
  def self.format_column_field(field)
39
- [field.name.to_s, ":#{field.type}", "(#{field.sql_type})"]
46
+ [field.name.to_s, ":#{field.type}", format_db_type(field), format_db_type_modifiers(field)]
47
+ end
48
+ def self.format_db_type(field)
49
+ prec_scale = field.precision && field.scale ? "#{field.precision}:#{field.scale}" : ""
50
+ default = field.default ? " (#{field.default})" : ""
51
+ "#{field.sql_type}#{prec_scale}#{default}"
52
+ end
53
+ def self.format_db_type_modifiers(field)
54
+ modifiers = []
55
+ modifiers << 'PRIMARY' if field.primary
56
+ modifiers << 'NOT NULL' unless field.null
57
+ #modifiers << "LIMIT(#{field.limit})" if field.limit
58
+ modifiers.join(',')
40
59
  end
41
60
 
42
61
  def self.print_array(array, *args)
43
62
  if args.empty?
44
63
  case array.first
45
- when ActiveRecord::Base then AwesomePrint.new.puts(array)
64
+ when ActiveRecord::Base then ap(array)
46
65
  else
47
66
  array.each{|element| puts element}
48
67
  end
@@ -64,7 +83,8 @@ module Arspy
64
83
 
65
84
  def self.print_object(object, *args)
66
85
  print_matrix([args.map{|arg| object[arg]}]) unless args.empty?
67
- AwesomePrint.new.puts(object) if args.empty?
86
+ #AwesomePrint.new.puts(object) if args.empty?
87
+ ap(object) if args.empty?
68
88
  nil
69
89
  end
70
90
  def self.test_object(obj, args)
@@ -79,121 +99,30 @@ module Arspy
79
99
  end
80
100
  end
81
101
  end
102
+ # t.people.with(20..30)
103
+ # t.people.with(2,3,4)
104
+ # t.people.with(:name=>[/Ed/, /Jen/, 'Joe'])
105
+ # t.people.with(:age=>10..20)
106
+ # t.people.with('statistics.statistic_type.abbrevation'=>%w(GP SV% GAA))
82
107
  def self.with(array, *args)
83
108
  return array if (args.empty? || array.nil? || array.empty?)
84
- array.select{|obj| obj && self.test_object(obj, args)}
109
+ array.select{|obj|
110
+ obj && args.any?{|arg|
111
+ Selector.for(arg).select?(obj) }}
85
112
  end
86
113
  def self.without(array, *args)
87
114
  return array if (args.empty? || array.nil? || array.empty?)
88
- array.select{|obj| obj && !self.test_object(obj, args)}
115
+ array.select{|obj| obj && !args.any?{|arg| Selector.for(arg).select?(obj) }}
89
116
  end
90
- def self.enable_abbreviations; @@abbreviations_enabled = true; end
91
- def self.disable_abbreviations; @@abbreviations_enabled = false; end
92
- @@abbreviations_enabled = true
93
- def self.interpret(array, method_name, *args)
94
- return nil unless (array && method_name)
95
- return nil unless (array.is_a?(Array) && !array.empty? && array.first.is_a?(ActiveRecord::Base))
96
117
 
97
- if array.first.class.reflect_on_all_associations.detect{|a| a.name == method_name}
98
- return interpret_association(array, method_name, *args)
99
- elsif (array.first.attribute_names.include?(method_name.to_s) || array.first.respond_to?(method_name))
100
- return interpret_attribute_or_method(array, method_name, *args)
101
- end
102
- return @@abbreviations_enabled ? interpret_abbreviation(array, method_name, *args) : nil
103
- end
104
- private
105
- def self.interpret_abbreviation(array, symbol, *args)
106
- if (descriptor = resolve_abbreviation_for_attributes_and_associations(array.first, symbol))
107
- if descriptor[:type] == :association
108
- return interpret_association(array, descriptor[:method_name], *args)
109
- else
110
- return interpret_attribute_or_method(array, descriptor[:method_name], *args)
111
- end
112
- end
113
- nil
114
- end
115
- def self.resolve_abbreviation_for_attributes_and_associations(object, method_name)
116
- klass = object.class
117
- setup_abbreviations(object) unless object.instance_variable_defined?('@arspy_abbreviations')
118
- if (ambiguity = klass.instance_variable_get('@arspy_ambiguous_abbreviations')[method_name])
119
- raise "Ambiguous abbreviation '#{ambiguity[:abbr]}' could be #{quote_and_join(ambiguity[:methods])}"
120
- end
121
- klass.instance_variable_get('@arspy_abbreviations')[method_name]
122
- end
123
- def self.setup_abbreviations(object)
124
- associations = object.class.reflect_on_all_associations.map(&:name).map(&:to_sym)
125
- attributes = object.attribute_names.map(&:to_sym)
126
- assoc_descriptors = associations.map{|method_name| {:method_name=>method_name, :type=>:association, :abbr=>abbreviate_method_name(method_name)}}
127
- attrib_descriptors = attributes.map{|method_name| {:method_name=>method_name, :type=>:attribute, :abbr=>abbreviate_method_name(method_name)}}
128
- all_descriptors = assoc_descriptors + attrib_descriptors
129
- object.class.instance_variable_set('@arspy_ambiguous_abbreviations', remove_ambiguities(all_descriptors))
130
- object.class.instance_variable_set('@arspy_abbreviations', Hash[*all_descriptors.map{|desc| [desc[:abbr], desc] }.flatten])
131
- end
132
- def self.remove_ambiguities(descriptors)
133
- list={}
134
- ambiguities = {}
135
- descriptors.each do |desc|
136
- if list.include?(desc[:abbr])
137
- if ambiguities[desc[:abbr]]
138
- ambiguities[desc[:abbr]][:methods] << desc[:method_name]
139
- else
140
- ambiguities[desc[:abbr]] = {:abbr=>desc[:abbr], :methods=>[desc[:method_name]]}
141
- ambiguities[desc[:abbr]][:methods] << list[desc[:abbr]][:method_name]
142
- end
143
- else
144
- list[desc[:abbr]] = desc
145
- end
146
- end
147
- descriptors.reject!{|desc| ambiguities.map{|hash| hash.first}.include?(desc[:abbr])}
148
- ambiguities
149
- end
150
- def self.abbreviate_method_name(method_name)
151
- words = method_name.to_s.split('_')
152
- abbr=[]
153
- if words.first == ''
154
- abbr << '_'
155
- end
156
- words.reject!{|word| word == ''}
157
- abbr += words.map do |word|
158
- chars = word.split(//)
159
- first = chars.shift
160
- [first, chars.map{|ch| ch =~ /[0-9]/ ? ch : nil}].compact.flatten.join('')
161
- end
162
-
163
- abbr << '_' if (method_name.to_s =~ /_$/)
164
- abbr.join('').to_sym
165
- end
166
- def self.quote_and_join(array)
167
- return "'#{array.first}'" if array.size == 1
168
- last = array.pop
169
- "'#{array.join("', '")}' or '#{last}'"
170
- end
171
- def self.interpret_association(array, method_name, *args)
172
- array.map(&method_name).flatten
173
- end
174
- def self.interpret_attribute_or_method(array, method_name, *args)
175
- return array.map(&method_name) if args.empty?
176
- raise 'Hash not allowed as attribute conditionals' if args.any?{|arg| arg.is_a?(Hash)}
177
- array.select{|obj| obj && self.test_attribute(obj, method_name, args)}
178
- end
179
- public
118
+ def self.enable_abbreviations; Interpreter.enable_abbreviations(true); end
119
+ def self.disable_abbreviations; Interpreter.enable_abbreviations(false); end
180
120
 
181
- def self.test_attribute(obj, attr_sym, args)
182
- return false if (obj.nil? || attr_sym.nil? || args.empty?)
183
- value = obj.__send__(attr_sym)
184
- args.any? do |arg|
185
- case arg
186
- when String then (arg == value || (obj.instance_eval("#{attr_sym} #{arg}") rescue false))
187
- when Regexp then arg.match(value)
188
- when Range then arg.include?(value)
189
- when Integer then (value.is_a?(ActiveRecord::Base) ? arg == value.id : arg == value) #TODO: what about value is association collection
190
- when Float then arg == value
191
- else
192
- false
193
- end
194
- end
121
+ def self.interpret(array, method_name, *args)
122
+ Interpreter.for(array, method_name).interpret(*args)
195
123
  end
196
124
 
125
+
197
126
  @@column_padding = 2
198
127
  def self.print_matrix(matrix_array)
199
128
  return nil if matrix_array.empty?
@@ -2,10 +2,10 @@ module MetaProgramming
2
2
  module Object
3
3
  def self.included(base)
4
4
  raise 'This module may only be included in class Object' unless base.name == 'Object'
5
- base.extend(ClassExtensions)
5
+ base.extend(ClassMethods)
6
6
  end
7
7
 
8
- module ClassExtensions
8
+ module ClassMethods
9
9
  def metaclass
10
10
  class << self; self; end
11
11
  end