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
    
        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. | 
| 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?
         | 
| @@ -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,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,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,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,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 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
         |