arspy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ pkg
3
+ nbproject
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-2009 Jeff Patmon
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,101 @@
1
+ #require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+ require 'gem_versions'
5
+ #require 'rake/contrib/sshpublisher'
6
+
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = 'arspy'
9
+ s.version = GemVersions.get_version
10
+ s.platform = Gem::Platform::RUBY
11
+ s.description = 'Active Record Spy'
12
+ s.summary = 'Rails console command line tool for browsing and inspecting the structure, associations and data of an ActiveRecord data model.'
13
+
14
+ exclude_folders = '' # 'spec/rails/{doc,lib,log,nbproject,tmp,vendor,test}'
15
+ exclude_files = [] # FileList['**/*.log'] + FileList[exclude_folders+'/**/*'] + FileList[exclude_folders]
16
+ s.files = FileList['{lib,spec}/**/*'] + %w(init.rb LICENSE Rakefile README.rdoc .gitignore) - exclude_files
17
+ s.require_path = 'lib'
18
+ s.has_rdoc = true
19
+ s.test_files = Dir['spec/*_spec.rb']
20
+
21
+ s.author = 'Jeff Patmon'
22
+ s.email = 'jpatmon@gmail.com'
23
+ s.homepage = 'http://github.com/jeffp/arspy/tree/master'
24
+ end
25
+
26
+ require 'spec/version'
27
+ require 'spec/rake/spectask'
28
+
29
+ desc "Run specs"
30
+ namespace :spec do
31
+ task :default=>:object
32
+ Spec::Rake::SpecTask.new(:object) do |t|
33
+ t.spec_files = FileList['spec/*_spec.rb']
34
+ t.libs << 'lib' << 'spec'
35
+ t.rcov = false
36
+ t.spec_opts = ['--options', 'spec/spec.opts']
37
+ #t.rcov_dir = 'coverage'
38
+ #t.rcov_opts = ['--exclude', "kernel,load-diff-lcs\.rb,instance_exec\.rb,lib/spec.rb,lib/spec/runner.rb,^spec/*,bin/spec,examples,/gems,/Library/Ruby,\.autotest,#{ENV['GEM_HOME']}"]
39
+ end
40
+ =begin
41
+ Spec::Rake::SpecTask.new(:sub) do |t|
42
+ t.spec_files = FileList['spec/inheritance_spec.rb']
43
+ t.libs << 'lib' << 'spec'
44
+ t.rcov = false
45
+ t.spec_opts = ['--options', 'spec/spec.opts']
46
+ end
47
+ Spec::Rake::SpecTask.new(:poro) do |t|
48
+ t.spec_files = FileList['spec/poro_spec.rb']
49
+ t.libs << 'lib' << 'spec'
50
+ t.rcov = false
51
+ t.spec_opts = ['--options', 'spec/spec.opts']
52
+ end
53
+
54
+ desc "Run ActiveRecord integration specs"
55
+ Spec::Rake::SpecTask.new(:active_record) do |t|
56
+ t.spec_files = FileList['spec/active_record/*_spec.rb']
57
+ t.libs << 'lib' << 'spec/active_record'
58
+ t.spec_opts = ['--options', 'spec/spec.opts']
59
+ t.rcov = false
60
+ end
61
+ Spec::Rake::SpecTask.new(:forms) do |t|
62
+ t.spec_files = FileList['spec/rails/spec/integrations/*_spec.rb']
63
+ t.libs << 'lib' << 'spec/rails/spec'
64
+ t.spec_opts = ['--options', 'spec/spec.opts']
65
+ t.rcov = false
66
+ end
67
+ # Spec::Rake::SpecTask.new(:associations) do |t|
68
+ # t.spec_files = FileList['spec/active_record/associations_spec.rb']
69
+ # t.libs << 'lib' << 'spec/active_record'
70
+ # t.rcov = false
71
+ # end
72
+ desc "Run all specs"
73
+ task :all=>[:object, :active_record, :forms]
74
+ =end
75
+ end
76
+
77
+
78
+ desc "Generate documentation for the #{spec.name} gem."
79
+ Rake::RDocTask.new(:rdoc) do |rdoc|
80
+ rdoc.rdoc_dir = 'rdoc'
81
+ rdoc.title = spec.name
82
+ #rdoc.template = '../rdoc_template.rb'
83
+ rdoc.options << '--line-numbers' << '--inline-source'
84
+ rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'lib/**/*.rb')
85
+ end
86
+
87
+ desc 'Generate a gemspec file.'
88
+ task :gemspec do
89
+ File.open("#{spec.name}.gemspec", 'w') do |f|
90
+ f.write spec.to_ruby
91
+ end
92
+ GemVersions.increment_version
93
+ end
94
+
95
+ Rake::GemPackageTask.new(spec) do |p|
96
+ p.gem_spec = spec
97
+ p.need_tar = RUBY_PLATFORM =~ /mswin/ ? false : true
98
+ p.need_zip = true
99
+ end
100
+
101
+ Dir['tasks/**/*.rake'].each {|rake| load rake}
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'arspy'
@@ -0,0 +1,10 @@
1
+ module Arspy
2
+ module ClassExtensions
3
+ module ActiveRecord
4
+ module Base
5
+ def la; Arspy::Operators.la(self); end
6
+ def lf; Arspy::Operators.lf(self); end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Arspy
2
+ module Delegators
3
+ module ActiveRecordExtensions
4
+ def pr(*args); Arspy::Operators.print_object(self, *args); end
5
+ def la; Arspy::Operators.la(self.class); end
6
+ def lf; Arspy::Operators.lf(self.class); end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ module Arspy
2
+ module Delegators
3
+ module ArrayExtensions
4
+ def self.included(base)
5
+ base.define_chained_method(:method_missing, :arspy) do |symbol, *args|
6
+ result = Arspy::Operators.interpret(self, symbol, *args)
7
+ result = method_missing_without_arspy(symbol, *args) unless result
8
+ result
9
+ end
10
+ base.define_chained_method(:id, :arspy) do
11
+ return id_without_arspy if (self.empty? || !self.first.is_a?(ActiveRecord::Base))
12
+ self.map(&:id)
13
+ end
14
+ end
15
+
16
+ def la
17
+ Arspy::Operators.la(self.first.class) unless (self.emtpy? || !(self.first.is_a?(ActiveRecord::Base)))
18
+ end
19
+ def lf
20
+ Arspy::Operators.lf(self.first.class) unless (self.empty? || !(self.first.is_a?(ActiveRecord::Base)))
21
+ end
22
+ def pr(*args)
23
+ Arspy::Operators.print_array(self, *args)
24
+ end
25
+ def wi(*args)
26
+ Arspy::Operators.with(self, *args)
27
+ end
28
+ def wo(*args)
29
+ Arspy::Operators.without(self, *args)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Arspy
2
+ module Delegators
3
+ module AssociationCollectionExtensions
4
+ def self.included(base)
5
+ base.define_chained_method(:method_missing, :arspy) do |symbol, *args|
6
+ load_target unless loaded?
7
+ result = Arspy::Operators.interpret(@target, symbol, *args)
8
+ result = method_missing_without_arspy(symbol, *args) unless result
9
+ result
10
+ end
11
+ end
12
+ def pr(*args)
13
+ load_target unless loaded?
14
+ Arspy::Operators.print_array(@target, *args)
15
+ end
16
+ def la
17
+ load_target unless loaded?
18
+ Arspy::Operators.la(@target.first.class) unless (@target.emtpy? || !(@target.first.is_a?(ActiveRecord::Base)))
19
+ end
20
+ def lf
21
+ load_target unless loaded?
22
+ Arspy::Operators.lf(@target.first.class) unless (@target.empty? || !(@target.first.is_a?(ActiveRecord::Base)))
23
+ end
24
+ def wi(*args)
25
+ load_target unless loaded?
26
+ Arspy::Operators.with(@target, *args)
27
+ end
28
+ def wo(*args)
29
+ load_target unless loaded?
30
+ Arspy::Operators.without(@target, *args)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ require 'arspy/delegators/null_extensions'
2
+ require 'arspy/delegators/array_extensions'
3
+ require 'arspy/delegators/association_collection_extensions'
4
+ require 'arspy/delegators/active_record_extensions'
5
+
6
+ module Arspy
7
+ module Delegators
8
+ module Factory
9
+ def self.module_for(klass)
10
+ case klass.name
11
+ when 'Array' then Arspy::Delegators::ArrayExtensions
12
+ when 'ActiveRecord::Base' then Arspy::Delegators::ActiveRecordExtensions
13
+ when 'ActiveRecord::Associations::HasManyThroughAssociation', 'ActiveRecord::Associations::AssociationCollection'
14
+ then Arspy::Delegators::AssociationCollectionExtensions
15
+ else
16
+ Arspy::Delegators::NullExtensions
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Arspy
2
+ module Delegators
3
+ module NullExtensions
4
+ def la; end
5
+ def lf; end
6
+ def pr(*args); end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ require 'arspy/delegators/factory'
2
+
3
+ module Arspy
4
+ module Delegators
5
+ end
6
+ end
@@ -0,0 +1,129 @@
1
+ module Arspy
2
+ module Operators
3
+ def self.la(active_record_klass)
4
+ counts = {}
5
+ rows = active_record_klass.reflect_on_all_associations.map do |a|
6
+ counts[a.macro] ||= 0
7
+ counts[a.macro] += 1
8
+ self.format_column_association(a)
9
+ end
10
+ rows.sort!{|a,b| a.first <=> b.first}
11
+ self.print_matrix(rows)
12
+ "Total: #{counts.inject(0){|sum, c| sum+c.last}} (" + counts.map{|c| "#{c.last} #{c.first}" }.join(', ') + ")"
13
+ end
14
+
15
+ def self.lf(active_record_klass)
16
+ rows = active_record_klass.columns.map do |c|
17
+ self.format_column_field(c)
18
+ end
19
+ rows.sort!{|a,b| a.first <=> b.first}
20
+ self.print_matrix(rows)
21
+ "Total #{active_record_klass.columns.size} field#{active_record_klass.columns.size == 1 ? '' : 's'}"
22
+ end
23
+
24
+ def self.format_column_association(a)
25
+ select_options = a.options.select{|k,v| [:through, :as, :polymorphic].include?(k)}
26
+ [a.name.to_s, a.macro.to_s, "(#{a.options[:class_name] || a.name.to_s.singularize.camelize})", select_options.empty? ? '' : Hash[*select_options.flatten].inspect]
27
+ end
28
+ def self.format_column_field(f)
29
+ [f.name.to_s, ":#{f.type}", "(#{f.sql_type})"]
30
+ end
31
+
32
+ def self.print_array(array, *args)
33
+ array.each{|element| puts element.is_a?(String) ? element : element.inspect } if args.empty?
34
+ self.print_matrix(
35
+ array.map do |obj|
36
+ args.map do |arg|
37
+ case arg
38
+ when Symbol then obj.__send__(arg)
39
+ when String then obj.respond_to?(arg) ? obj.__send__(arg) : (obj.instance_eval(arg) rescue nil)
40
+ else nil
41
+ end
42
+ end
43
+ end
44
+ ) unless args.empty?
45
+ nil
46
+ end
47
+
48
+ def self.print_object(object, *args)
49
+ print_matrix([args.map{|a| object[a]}]) if args
50
+ puts(object.inspect) unless args
51
+ nil
52
+ end
53
+ def self.test_object(obj, args)
54
+ args.any? do |arg|
55
+ case arg
56
+ when String then obj.instance_eval(arg) rescue false
57
+ when Integer then obj.id == arg
58
+ when Hash
59
+ arg.any?{|k,v| self.test_attribute(obj, k, (v.is_a?(Array) ? v : [v]) ) }
60
+ else
61
+ false
62
+ end
63
+ end
64
+ end
65
+ def self.with(array, *args)
66
+ return array if (args.empty? || array.nil? || array.empty?)
67
+ array.select{|o| o && self.test_object(o, args)}
68
+ end
69
+ def self.without(array, *args)
70
+ return array if (args.empty? || array.nil? || array.empty?)
71
+ array.select{|o| o && !self.test_object(o, args)}
72
+ end
73
+ def self.interpret(array, symbol, *args)
74
+ return nil unless (array && symbol)
75
+ return nil unless (array.is_a?(Array) && !array.empty? && array.first.is_a?(ActiveRecord::Base))
76
+
77
+ if array.first.class.reflect_on_all_associations.detect{|a| a.name == symbol}
78
+ array.map(&symbol).flatten
79
+ elsif (array.first.attribute_names.include?(symbol.to_s) || array.first.respond_to?(symbol))
80
+ return array.map(&symbol) if args.empty?
81
+ raise 'Hash not allowed as attribute conditionals' if args.any?{|a| a.is_a?(Hash)}
82
+ array.select{|o| o && self.test_attribute(o, symbol, args)}
83
+ else
84
+ nil
85
+ end
86
+ end
87
+
88
+ def self.test_attribute(obj, attr_sym, args)
89
+ return false if (obj.nil? || attr_sym.nil? || args.empty?)
90
+ value = obj.__send__(attr_sym)
91
+ args.any? do |arg|
92
+ case arg
93
+ when String then (arg == value || (obj.instance_eval("#{attr_sym} #{arg}") rescue false))
94
+ when Regexp then arg.match(value)
95
+ when Range then arg.include?(value)
96
+ when Integer then (value.is_a?(ActiveRecord::Base) ? arg == value.id : arg == value) #TODO: what about value is association collection
97
+ when Float then arg == value
98
+ else
99
+ false
100
+ end
101
+ end
102
+ end
103
+
104
+ def self.prepare_arguments(symbol, *args)
105
+ return nil if args.empty?
106
+ Hash[*args.map{|a| a.is_a?(Hash) ? a.map{|k,v| [k, v]} : [symbol, a]}.flatten]
107
+ end
108
+
109
+ @@column_padding = 2
110
+ def self.print_matrix(matrix_array)
111
+ return nil if matrix_array.empty?
112
+ raise 'Cannot print a non-matrix array' unless matrix_array.all?{|ar| ar.is_a? Array }
113
+
114
+ columns_per_row = matrix_array.map{|ar| ar.size }.max
115
+ init_array = Array.new(columns_per_row, 0)
116
+ max_widths = matrix_array.inject(init_array)do |mw, row|
117
+ row.each_with_index do |string, index|
118
+ mw[index] = [string.to_s.length, mw[index]].max
119
+ end
120
+ mw
121
+ end
122
+ matrix_array.each do |row|
123
+ index = -1
124
+ puts (row.map{|column| column.to_s + ' '*(max_widths[index += 1] - column.to_s.length) }.join(' '*@@column_padding))
125
+ end
126
+ nil
127
+ end
128
+ end
129
+ end
data/lib/arspy.rb ADDED
@@ -0,0 +1,26 @@
1
+ raise 'Arspy only runs in an ActiveRecord environment' unless defined?(ActiveRecord::Base)
2
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__))
3
+
4
+ require 'active_support/core_ext/string/inflections'
5
+ require 'meta_programming'
6
+ require 'arspy/operators'
7
+ require 'arspy/delegators'
8
+ require 'arspy/class_extensions'
9
+
10
+ module Arspy
11
+ def self.included(base)
12
+ base.send :include, Delegators::Factory.module_for(base)
13
+ extension = "Arspy::ClassExtensions::#{base.name}".constantize rescue nil
14
+ base.extend(extension) if extension
15
+ end
16
+ end
17
+
18
+ a = ActiveRecord::Associations
19
+ returning([Array, ActiveRecord::Base, a::AssociationCollection ]) { |classes|
20
+ unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
21
+ classes << a::HasManyThroughAssociation
22
+ end
23
+ }.each do |klass|
24
+ klass.send :include, Arspy
25
+ end
26
+
@@ -0,0 +1,33 @@
1
+ module MetaProgramming
2
+ module Object
3
+ def self.included(base)
4
+ raise 'This module may only be included in class Object' unless base.name == 'Object'
5
+ base.extend(ClassExtensions)
6
+ end
7
+
8
+ module ClassExtensions
9
+ def metaclass
10
+ class << self; self; end
11
+ end
12
+ def safe_alias_method_chain(method_name, ext)
13
+ class_eval do
14
+ method_name_with_ext = "#{method_name}_with_#{ext}".to_sym
15
+ if (method_defined?(method_name_with_ext) && !(metaclass.instance_variable_defined?("@#{method_name_with_ext}")))
16
+ if method_defined?(method_name.to_sym)
17
+ alias_method_chain(method_name.to_sym, ext.to_sym)
18
+ else
19
+ alias_method method_name.to_sym, method_name_with_ext
20
+ define_method("#{method_name}_without_#{ext}".to_sym) {|*args| }
21
+ end
22
+ metaclass.instance_variable_set("@#{method_name_with_ext}", true)
23
+ end
24
+ end
25
+ end
26
+
27
+ def define_chained_method(method_name, ext, &block)
28
+ define_method("#{method_name}_with_#{ext}".to_sym, block)
29
+ safe_alias_method_chain(method_name.to_sym, ext)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ require 'meta_programming/object'
2
+
3
+ module MetaProgramming
4
+ end
5
+
6
+ Object.send :include, MetaProgramming::Object
7
+
data/spec/database.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+
3
+ gem 'activerecord', ENV['AR_VERSION'] ? "=#{ENV['AR_VERSION']}" : '>=2.1.0'
4
+ require 'active_record'
5
+
6
+ ActiveRecord::Base.establish_connection({'adapter' => 'sqlite3', 'database' => ':memory:'})
7
+ ActiveRecord::Base.logger = Logger.new("#{File.dirname(__FILE__)}/active_record.log")
8
+ cx = ActiveRecord::Base.connection
9
+
10
+ cx.create_table(:things, :force=>true) do |t|
11
+
12
+ end
13
+ cx.create_table(:one_manies, :force=>true) do |t|
14
+
15
+ end
16
+ cx.create_table(:many_manies, :force=>true) do |t|
17
+
18
+ end
19
+ cx.create_table(:many_manies_things, :force=>true) do |t|
20
+
21
+ end
22
+ cx.create_table(:manies_one, :force=>true) do |t|
23
+
24
+ end
25
+
26
+ #basic_associations
27
+ connection.create_table(:companies, :force=>true) do |t|
28
+ t.string :name
29
+ t.string :status
30
+ end
31
+ connection.create_table(:contract_workers, :force=>true) do |t|
32
+ t.references :company
33
+ t.references :contractor
34
+ t.string :status
35
+ end
36
+ connection.create_table(:licenses, :force=>true) do |t|
37
+ t.references :company
38
+ t.string :status
39
+ end
40
+ connection.create_table(:contractors, :force=>true) do |t|
41
+ t.string :name
42
+ t.string :status
43
+ end
44
+ connection.create_table(:employees, :force=>true) do |t|
45
+ t.references :company
46
+ t.string :name
47
+ t.string :status
48
+ end
49
+
50
+ #polymorphic_associations
51
+ connection.create_table(:comments, :force=>true) do |t|
52
+ t.references :document, :polymorphic=>true
53
+ t.text :comment
54
+ t.string :status
55
+ end
56
+ connection.create_table(:articles, :force=>true) do |t|
57
+ t.string :name
58
+ t.string :status
59
+ end
60
+ connection.create_table(:images, :force=>true) do |t|
61
+ t.string :name
62
+ t.string :status
63
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: arspy
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Jeff Patmon
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-03 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Active Record Spy
22
+ email: jpatmon@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/meta_programming/object.rb
31
+ - lib/arspy/class_extensions.rb
32
+ - lib/arspy/delegators/factory.rb
33
+ - lib/arspy/delegators/association_collection_extensions.rb
34
+ - lib/arspy/delegators/array_extensions.rb
35
+ - lib/arspy/delegators/null_extensions.rb
36
+ - lib/arspy/delegators/active_record_extensions.rb
37
+ - lib/arspy/delegators.rb
38
+ - lib/arspy/operators.rb
39
+ - lib/meta_programming.rb
40
+ - lib/arspy.rb
41
+ - spec/database.rb
42
+ - init.rb
43
+ - LICENSE
44
+ - Rakefile
45
+ - README.rdoc
46
+ - .gitignore
47
+ has_rdoc: true
48
+ homepage: http://github.com/jeffp/arspy/tree/master
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.6
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Rails console command line tool for browsing and inspecting the structure, associations and data of an ActiveRecord data model.
77
+ test_files: []
78
+