arspy 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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
data/README.rdoc CHANGED
@@ -113,6 +113,15 @@ Expressions using associations
113
113
  Jenny Westmeyers 213 2
114
114
  Maria Stone 8 88
115
115
 
116
+ === Printing objects and classes
117
+
118
+ ARSpy integrates 'awesome print' to print lists of objects and classes. Print objects or
119
+ classes with the 'pr' command, or explicitly print with the 'ap' command.
120
+
121
+ User.ap # => prints the object attributes and columns with awesome print
122
+ User.find(2).blogs.title.pr #=> prints the titles of all blogs
123
+ User.find(2).blogs.pr # => prints the object fields and data usign awesome print
124
+
116
125
 
117
126
  == Iterating Associations and Data
118
127
 
@@ -203,3 +212,4 @@ results that do not meet the conditions passed in the parameters.
203
212
 
204
213
  * ActiveRecord
205
214
  * ActiveSupport
215
+ * Awesome Print
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ spec = Gem::Specification.new do |s|
12
12
  s.description = 'Active Record Spy'
13
13
  s.summary = 'Rails console command line tool for browsing and inspecting the structure, associations and data of an ActiveRecord data model.'
14
14
 
15
- s.add_dependency('awesome_print', '>= 0.1.1')
15
+ s.add_dependency('awesome_print', '>= 0.1.4')
16
16
 
17
17
  exclude_folders = '' # 'spec/rails/{doc,lib,log,nbproject,tmp,vendor,test}'
18
18
  exclude_files = [] # FileList['**/*.log'] + FileList[exclude_folders+'/**/*'] + FileList[exclude_folders]
@@ -2,8 +2,8 @@ module Arspy
2
2
  module ClassExtensions
3
3
  module ActiveRecord
4
4
  module Base
5
- def la; Arspy::Operators.list_associations(self); end
6
- def lf; Arspy::Operators.list_fields(self); end
5
+ def la(*args); Arspy::Operators.list_associations(self, *args); end
6
+ def lf(*args); Arspy::Operators.list_fields(self, *args); end
7
7
  def pr; Arspy::Operators.awesome_print(self); end
8
8
  def ap(opts={}); Arspy::Operators.awesome_print(self, opts); end
9
9
  end
@@ -1,8 +1,8 @@
1
1
  module Arspy
2
2
  module Delegators
3
3
  module ActiveRecordExtensions
4
- def la; Arspy::Operators.list_associations(self.class); end
5
- def lf; Arspy::Operators.list_fields(self.class); end
4
+ def la(*args); Arspy::Operators.list_associations(self.class, *args); end
5
+ def lf(*args); Arspy::Operators.list_fields(self.class, *args); end
6
6
  def pr(*args); Arspy::Operators.print_object(self, *args); end
7
7
  def ap(opts={}); Arspy::Operators.awesome_print(self, opts); end
8
8
  end
@@ -13,11 +13,11 @@ module Arspy
13
13
  end
14
14
  end
15
15
 
16
- def la
17
- Arspy::Operators.list_associations(self.first) unless self.emtpy?
16
+ def la(*args)
17
+ Arspy::Operators.list_associations(self.first, *args) unless self.emtpy?
18
18
  end
19
- def lf
20
- Arspy::Operators.list_fields(self.first) unless self.empty?
19
+ def lf(*args)
20
+ Arspy::Operators.list_fields(self.first, *args) unless self.empty?
21
21
  end
22
22
  def ap(opts={})
23
23
  Arspy::Operators.awesome_print(self, opts={}) unless self.empty?
@@ -9,13 +9,13 @@ module Arspy
9
9
  result
10
10
  end
11
11
  end
12
- def la
12
+ def la(*args)
13
13
  load_target unless loaded?
14
- Arspy::Operators.list_associations(@target.first) unless @target.emtpy?
14
+ Arspy::Operators.list_associations(@target.first, *args) unless @target.emtpy?
15
15
  end
16
- def lf
16
+ def lf(*args)
17
17
  load_target unless loaded?
18
- Arspy::Operators.list_fields(@target.first) unless @target.empty?
18
+ Arspy::Operators.list_fields(@target.first, *args) unless @target.empty?
19
19
  end
20
20
  def pr(*args)
21
21
  load_target unless loaded?
@@ -1,8 +1,8 @@
1
1
  module Arspy
2
2
  module Delegators
3
3
  module NullExtensions
4
- def la; end
5
- def lf; end
4
+ def la(*args); end
5
+ def lf(*args); end
6
6
  def pr(*args); end
7
7
  def ap(opts={}); end
8
8
  end
@@ -0,0 +1,68 @@
1
+ module Abbreviations
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ def resolve_abbreviation_for_attributes_and_associations!(object, method_name)
7
+ self.class.resolve_abbreviation_for_attributes_and_associations!(object, method_name)
8
+ end
9
+ module ClassMethods
10
+ def resolve_abbreviation_for_attributes_and_associations!(object, method_name)
11
+ klass = object.class
12
+ setup_abbreviations(object) unless object.instance_variable_defined?('@arspy_abbreviations')
13
+ if (ambiguity = klass.instance_variable_get('@arspy_ambiguous_abbreviations')[method_name])
14
+ raise "Ambiguous abbreviation '#{ambiguity[:abbr]}' could be #{quote_and_join(ambiguity[:methods])}"
15
+ end
16
+ klass.instance_variable_get('@arspy_abbreviations')[method_name]
17
+ end
18
+
19
+ def setup_abbreviations(object)
20
+ associations = object.class.reflect_on_all_associations.map(&:name).map(&:to_sym)
21
+ attributes = object.attribute_names.map(&:to_sym)
22
+ assoc_descriptors = associations.map{|method_name| {:method_name=>method_name, :type=>:association, :abbr=>abbreviate_method_name(method_name)}}
23
+ attrib_descriptors = attributes.map{|method_name| {:method_name=>method_name, :type=>:attribute, :abbr=>abbreviate_method_name(method_name)}}
24
+ all_descriptors = assoc_descriptors + attrib_descriptors
25
+ object.class.instance_variable_set('@arspy_ambiguous_abbreviations', remove_ambiguities(all_descriptors))
26
+ object.class.instance_variable_set('@arspy_abbreviations', Hash[*all_descriptors.map{|desc| [desc[:abbr], desc] }.flatten])
27
+ end
28
+ def remove_ambiguities(descriptors)
29
+ list={}
30
+ ambiguities = {}
31
+ descriptors.each do |desc|
32
+ if list.include?(desc[:abbr])
33
+ if ambiguities[desc[:abbr]]
34
+ ambiguities[desc[:abbr]][:methods] << desc[:method_name]
35
+ else
36
+ ambiguities[desc[:abbr]] = {:abbr=>desc[:abbr], :methods=>[desc[:method_name]]}
37
+ ambiguities[desc[:abbr]][:methods] << list[desc[:abbr]][:method_name]
38
+ end
39
+ else
40
+ list[desc[:abbr]] = desc
41
+ end
42
+ end
43
+ descriptors.reject!{|desc| ambiguities.map{|hash| hash.first}.include?(desc[:abbr])}
44
+ ambiguities
45
+ end
46
+ def abbreviate_method_name(method_name)
47
+ words = method_name.to_s.split('_')
48
+ abbr=[]
49
+ if words.first == ''
50
+ abbr << '_'
51
+ end
52
+ words.reject!{|word| word == ''}
53
+ abbr += words.map do |word|
54
+ chars = word.split(//)
55
+ first = chars.shift
56
+ [first, chars.map{|ch| ch =~ /[0-9]/ ? ch : nil}].compact.flatten.join('')
57
+ end
58
+
59
+ abbr << '_' if (method_name.to_s =~ /_$/)
60
+ abbr.join('').to_sym
61
+ end
62
+ def quote_and_join(array)
63
+ return "'#{array.first}'" if array.size == 1
64
+ last = array.pop
65
+ "'#{array.join("', '")}' or '#{last}'"
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,11 @@
1
+ module Arspy
2
+ module Operators
3
+ module AttributeTest
4
+ class Base
5
+ def initialize(param)
6
+ @param = param
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ module Arspy
2
+ module Operators
3
+ module AttributeTest
4
+
5
+
6
+ class FloatTest < Base
7
+
8
+ def self.applies?(param)
9
+ param.is_a? Float
10
+ end
11
+
12
+ def match?(value_or_object)
13
+ case value_or_object
14
+ when ActiveRecord::Base
15
+ false
16
+ else
17
+ value_or_object.to_f == @param rescue nil
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Arspy
2
+ module Operators
3
+ module AttributeTest
4
+
5
+
6
+ class IntegerTest < Base
7
+
8
+ def self.applies?(param)
9
+ param.is_a? Integer
10
+ end
11
+
12
+ def match?(value_or_object)
13
+ case value_or_object
14
+ when ActiveRecord::Base
15
+ value_or_object.id == @param
16
+ else
17
+ value_or_object.to_i == @param rescue nil
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Arspy
2
+ module Operators
3
+ module AttributeTest
4
+
5
+
6
+ class RangeTest < Base
7
+
8
+ def self.applies?(param)
9
+ param.is_a? Range
10
+ end
11
+
12
+ def match?(value_or_object)
13
+ case value_or_object
14
+ when ActiveRecord::Base
15
+ @param.include?(value_or_object.id)
16
+ else
17
+ @param.include?(value_or_object.to_i)
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ module Arspy
2
+ module Operators
3
+ module AttributeTest
4
+
5
+ class RegexpTest < Base
6
+ def self.applies?(param)
7
+ param.is_a? Regexp
8
+ end
9
+
10
+ def match?(value_or_object)
11
+ case value_or_object
12
+ when ActiveRecord::Base
13
+ raise "Test for '#{@param}' on object '#{value_or_object.class.name}' not supported."
14
+ else
15
+ value_or_object =~ @param
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Arspy
2
+ module Operators
3
+ module AttributeTest
4
+
5
+ class StringTest < Base
6
+
7
+ def self.applies?(param)
8
+ param.is_a? String
9
+ end
10
+
11
+ def match?(value_or_object)
12
+ case value_or_object
13
+ when ActiveRecord::Base
14
+ value_or_object.instance_eval{ value_or_object }
15
+ else
16
+ value_or_object.to_s == @param rescue nil
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module Arspy
2
+ module Operators
3
+ module AttributeTest
4
+ class UnsupportedTest < Base
5
+
6
+ def self.applies?(param)
7
+ true
8
+ end
9
+
10
+ def match?(object)
11
+ raise "#{@param.class.name} '#{@param.inspect}' not supported."
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ require 'arspy/operators/attribute_test/base'
2
+ require 'arspy/operators/attribute_test/integer_test'
3
+ require 'arspy/operators/attribute_test/range_test'
4
+ require 'arspy/operators/attribute_test/regexp_test'
5
+ require 'arspy/operators/attribute_test/string_test'
6
+ require 'arspy/operators/attribute_test/unsupported_test'
7
+
8
+ module Arspy
9
+ module Operators
10
+ module AttributeTest
11
+ @@attribute_test_classes = [
12
+ IntegerTest, RangeTest, StringTest, RegexpTest, UnsupportedTest
13
+ ]
14
+
15
+ def self.for(param)
16
+ @@attribute_test_classes.detect{|klass| klass.applies?(param)}.new(param)
17
+ end
18
+
19
+ end end
20
+ end
@@ -0,0 +1,22 @@
1
+ require 'arspy/operators/abbreviations'
2
+ module Arspy
3
+ module Operators
4
+ module Interpreter
5
+ class AbbreviatedAssociationInterpreter < AssociationInterpreter
6
+ include Abbreviations
7
+
8
+ def self.applies?(array, method_name)
9
+ descriptor = resolve_abbreviation_for_attributes_and_associations!(array.first, method_name)
10
+ descriptor && descriptor[:type] == :association
11
+ end
12
+
13
+ def initialize(array, method_name)
14
+ descriptor = resolve_abbreviation_for_attributes_and_associations!(array.first, method_name)
15
+ super(array, descriptor[:method_name])
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,22 @@
1
+ require 'arspy/operators/abbreviations'
2
+
3
+ module Arspy
4
+ module Operators
5
+ module Interpreter
6
+ class AbbreviatedAttributeInterpreter < AttributeInterpreter
7
+ include Abbreviations
8
+
9
+ def self.applies?(array, method_name)
10
+ descriptor = resolve_abbreviation_for_attributes_and_associations!(array.first, method_name)
11
+ descriptor && descriptor[:type] == :attribute
12
+ end
13
+
14
+ def initialize(array, method_name)
15
+ descriptor = resolve_abbreviation_for_attributes_and_associations!(array.first, method_name)
16
+ super(array, descriptor[:method_name])
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ module Arspy
2
+ module Operators
3
+ module Interpreter
4
+
5
+ class AssociationInterpreter < Base
6
+
7
+ def self.applies?(array, method_name)
8
+ array.first.class.reflect_on_all_associations.detect{|a| a.name == method_name}
9
+ end
10
+
11
+ def interpret(*args)
12
+ @array.map(&@method_name).flatten
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Arspy
2
+ module Operators
3
+ module Interpreter
4
+ class AttributeInterpreter < Base
5
+
6
+ def self.applies?(array, method_name)
7
+ array.first.attribute_names.include?(method_name.to_s)
8
+ end
9
+
10
+ def interpret(*args)
11
+ return @array.map(&@method_name) if args.empty?
12
+ raise 'Hash not supported as attribute conditionals' if args.any?{|arg| arg.is_a?(Hash)}
13
+ selector = Selector.for({@method_name => args})
14
+ @array.select{|obj| obj && selector.select?(obj)}
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module Arspy
2
+ module Operators
3
+ module Interpreter
4
+ class Base
5
+ def initialize(array, method_name)
6
+ @array, @method_name = array, method_name
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module Arspy
2
+ module Operators
3
+ module Interpreter
4
+
5
+ class MethodInterpreter < AttributeInterpreter
6
+ def self.applies?(array, method_name)
7
+ array.first.respond_to?(method_name) && array.first.method(method_name).arity == 0
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,12 @@
1
+ module Arspy
2
+ module Operators
3
+ module Interpreter
4
+ class NullInterpreter < Base
5
+ def self.applies?(array, method_name)
6
+ true
7
+ end
8
+ def interpret(*args); nil; end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+
2
+ require 'arspy/operators/interpreter/base'
3
+ require 'arspy/operators/interpreter/association_interpreter'
4
+ require 'arspy/operators/interpreter/attribute_interpreter'
5
+ require 'arspy/operators/interpreter/method_interpreter'
6
+ require 'arspy/operators/interpreter/abbreviated_association_interpreter'
7
+ require 'arspy/operators/interpreter/abbreviated_attribute_interpreter'
8
+ require 'arspy/operators/interpreter/null_interpreter'
9
+
10
+ module Arspy
11
+ module Operators
12
+ module Interpreter
13
+ @@interpreter_classes = [
14
+ AssociationInterpreter, AttributeInterpreter,
15
+ MethodInterpreter, AbbreviatedAssociationInterpreter,
16
+ AbbreviatedAttributeInterpreter,
17
+ NullInterpreter
18
+ ]
19
+
20
+ def self.for(array, method_name)
21
+ return NullInterpreter.new(array, method_name) unless (array && method_name && array.is_a?(Array) && !array.empty? && array.first.is_a?(ActiveRecord::Base))
22
+ @@interpreter_classes.detect{|klass| klass.applies?(array, method_name)}.new(array, method_name)
23
+ end
24
+
25
+ @@abbreviations_enabled = true
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ module Arspy
2
+ module Operators
3
+ module Selector
4
+
5
+ class AttributeSelector < Base
6
+ def self.applies?(key_value)
7
+ key_value.first.is_a? Symbol
8
+ end
9
+ def initialize(key_value)
10
+ super
11
+ @attribute = key_value.first.to_sym
12
+ @test_values = key_value.last.is_a?(Array) ? key_value.last : [key_value.last]
13
+ end
14
+
15
+ def select?(obj)
16
+ raise "Attribute '#{@attribute}' not found for #{obj.class.name}" unless obj.attribute_names.include?(@attribute.to_s)
17
+ response = obj.__send__ @attribute
18
+ return nil unless response
19
+ raise "Attributes resulting in arrays not supported" if response.is_a?(Array)
20
+ response = [response] unless response.is_a?(Array)
21
+ response.any?{|value_or_object| attribute_tests.any?{|test| test.match?(value_or_object)}}
22
+ end
23
+
24
+ def attribute_tests
25
+ @attribute_tests ||= @test_values.map do |param|
26
+ AttributeTest.for(param)
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ module Arspy
2
+ module Operators
3
+ module Selector
4
+ class Base
5
+ def initialize(arg)
6
+ @argument = arg
7
+ end
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ module Arspy
2
+ module Operators
3
+ module Selector
4
+
5
+ class HashSelector < Base
6
+ @@hash_selector_classes = [
7
+ Selector::AttributeSelector,
8
+ Selector::UnsupportedSelector
9
+ ]
10
+ def self.applies?(arg)
11
+ arg.is_a? Hash
12
+ end
13
+
14
+ def select?(obj)
15
+ selectors.any?{|selector| selector.select?(obj) }
16
+ end
17
+
18
+ def selectors
19
+ @selectors ||= @argument.map do |key_value|
20
+ hash_selector_class_for(key_value).new(key_value)
21
+ end
22
+ end
23
+ def hash_selector_class_for(key_value)
24
+ @@hash_selector_classes.detect{|klass| klass.applies?(key_value) }
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ module Arspy
2
+ module Operators
3
+ module Selector
4
+ class IntegerSelector < Base
5
+
6
+ def self.applies?(arg)
7
+ arg.is_a? Integer
8
+ end
9
+
10
+ def select?(obj)
11
+ obj.id == @argument
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Arspy
2
+ module Operators
3
+ module Selector
4
+ class RangeSelector < Base
5
+
6
+ def self.applies?(arg)
7
+ arg.is_a? Range
8
+ end
9
+
10
+ def select?(obj)
11
+ @argument.include?(obj.id)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ module Arspy
2
+ module Operators
3
+ module Selector
4
+ class StringSelector < Base
5
+ def self.applies?(arg)
6
+ arg.is_a? String
7
+ end
8
+ def select?(obj)
9
+ obj.instance_eval { @argument }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module Arspy
2
+ module Operators
3
+ module Selector
4
+ class UnsupportedSelector < Base
5
+ def self.applies?(arg)
6
+ true
7
+ end
8
+ def initialize(arg)
9
+ raise "#{arg.inspect} not supported."
10
+ end
11
+ def select?(obj)
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end