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.
- data/README.rdoc +10 -0
- data/Rakefile +1 -1
- data/lib/arspy/class_extensions.rb +2 -2
- data/lib/arspy/delegators/active_record_extensions.rb +2 -2
- data/lib/arspy/delegators/array_extensions.rb +4 -4
- data/lib/arspy/delegators/association_collection_extensions.rb +4 -4
- data/lib/arspy/delegators/null_extensions.rb +2 -2
- data/lib/arspy/operators/abbreviations.rb +68 -0
- data/lib/arspy/operators/attribute_test/base.rb +11 -0
- data/lib/arspy/operators/attribute_test/float_test.rb +24 -0
- data/lib/arspy/operators/attribute_test/integer_test.rb +24 -0
- data/lib/arspy/operators/attribute_test/range_test.rb +24 -0
- data/lib/arspy/operators/attribute_test/regexp_test.rb +23 -0
- data/lib/arspy/operators/attribute_test/string_test.rb +23 -0
- data/lib/arspy/operators/attribute_test/unsupported_test.rb +17 -0
- data/lib/arspy/operators/attribute_test.rb +20 -0
- data/lib/arspy/operators/interpreter/abbreviated_association_interpreter.rb +22 -0
- data/lib/arspy/operators/interpreter/abbreviated_attribute_interpreter.rb +22 -0
- data/lib/arspy/operators/interpreter/association_interpreter.rb +18 -0
- data/lib/arspy/operators/interpreter/attribute_interpreter.rb +21 -0
- data/lib/arspy/operators/interpreter/base.rb +11 -0
- data/lib/arspy/operators/interpreter/method_interpreter.rb +14 -0
- data/lib/arspy/operators/interpreter/null_interpreter.rb +12 -0
- data/lib/arspy/operators/interpreter.rb +29 -0
- data/lib/arspy/operators/selector/attribute_selector.rb +33 -0
- data/lib/arspy/operators/selector/base.rb +12 -0
- data/lib/arspy/operators/selector/hash_selector.rb +30 -0
- data/lib/arspy/operators/selector/integer_selector.rb +17 -0
- data/lib/arspy/operators/selector/range_selector.rb +17 -0
- data/lib/arspy/operators/selector/string_selector.rb +14 -0
- data/lib/arspy/operators/selector/unsupported_selector.rb +17 -0
- data/lib/arspy/operators/selector.rb +28 -0
- data/lib/arspy/operators.rb +40 -111
- data/lib/meta_programming/object.rb +2 -2
- data/spec/active_record.log +4327 -0
- data/spec/database.rb +85 -32
- data/spec/list_association_spec.rb +62 -0
- data/spec/list_field_spec.rb +5 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/with_command_spec.rb +106 -0
- 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
|
data/lib/arspy/operators.rb
CHANGED
@@ -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
|
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}",
|
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
|
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|
|
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 && !
|
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
|
-
|
98
|
-
|
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.
|
182
|
-
|
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(
|
5
|
+
base.extend(ClassMethods)
|
6
6
|
end
|
7
7
|
|
8
|
-
module
|
8
|
+
module ClassMethods
|
9
9
|
def metaclass
|
10
10
|
class << self; self; end
|
11
11
|
end
|