arspy 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/README.rdoc +205 -0
  2. data/lib/arspy/operators.rb +34 -34
  3. metadata +2 -2
data/README.rdoc CHANGED
@@ -0,0 +1,205 @@
1
+ = ARSpy
2
+
3
+ A Rails console command line tool for exploring, browsing and inspecting
4
+ the structure, associations and data of ActiveRecord data models.
5
+
6
+ == Resources
7
+
8
+ Install
9
+
10
+ * sudo gem install arspy
11
+
12
+ Use
13
+
14
+ * require 'arspy'
15
+
16
+
17
+ == Description
18
+
19
+ ARSpy provides a number of functions for inspecting a Rails application's
20
+ ActiveRecord model. To use the gem all you need to to is the following:
21
+
22
+ sudo gem install arspy
23
+ ruby script/console
24
+ require 'arspy'
25
+
26
+ With ARSpy, you can view associations, fields and data related to tables and relations
27
+ in an easy-to-read format.
28
+
29
+ == Inspecting ActiveRecord Associations and Attributes (Fields)
30
+
31
+ === 'Listing fields' with the 'lf' command
32
+
33
+ Append the 'lf' command to an ActiveRecord object, class, or association method to
34
+ print the name, type and database variable type for each attribute.
35
+
36
+ User.lf
37
+ first_name :string (varchar(50))
38
+ last_name :string (varchar(50))
39
+ active :boolean (boolean)
40
+ age :age (int(11))
41
+
42
+
43
+ === 'Listing associations' with the 'la' command
44
+
45
+ Append the 'la' command to an ActiveRecord object, class or association method to
46
+ print the name, type, class and configuration information for each association.
47
+
48
+ User.la
49
+ blogs has_many (Blog)
50
+ comments has_many (Comment)
51
+ friendships has_many (Friendship)
52
+ friends has_many (User) {:through=>:friendships}
53
+ assets has_many (Asset) {:as=>:asset}
54
+
55
+ === Applying 'lf' and 'la' commands
56
+
57
+ The 'la' and 'lf' command can be used on any ActiveRecord object, class, association
58
+ or array of these.
59
+
60
+ u = User.find_by_last_name('Smith')
61
+ u.la
62
+ u.blogs.lf
63
+ u.blogs.title
64
+ u.blogs.la
65
+ u.blogs.comments.la
66
+ u.blogs.comments.user.lf
67
+ u.blogs.comments.user.pr(:first_name, :last_name) #see 'pr' command below
68
+
69
+
70
+ == Printing in Columns
71
+
72
+ === Printing arrays with the 'pr' command
73
+
74
+ Array data in the console is not so easy to read.
75
+
76
+ names
77
+ ["Peter Smith", "Sarah Johnson", "Randy Wade", "Alex Parsons", "Beth Silverton",
78
+ "Jenny Westmeyers", "Benjamin Grant", "Maria Stone"]
79
+
80
+ Use the 'pr' command to print in a column.
81
+
82
+ names.pr
83
+ Peter Smith
84
+ Sara Johnson
85
+ ...
86
+
87
+ === Print columns of object attributes with the 'pr' command
88
+
89
+ Arrays of ActiveRecord objects are even more difficult to read and analyze. Use
90
+ 'pr' command to print attributes (fields) in columns.
91
+
92
+ User.find(2).friends.pr :first_name, :last_name
93
+ Randy Wade
94
+ Benjamin Grant
95
+ Jenny Westmeyers
96
+ Maria Stone
97
+
98
+ === Printing expressions with 'pr' command
99
+
100
+ Pass an expression as one of the columns to the 'pr' command.
101
+
102
+ User.find(2).friends.pr '"#{last_name}, #{first_name}"', :age
103
+ Wade, Randy 25
104
+ Grant, Benjamin 28
105
+ Westmeyers, Jenny 24
106
+ Stone, Maria 33
107
+
108
+ Expressions using associations
109
+
110
+ User.find(2).friends.pr :first_name, :last_name, 'comments.count', 'blogs.count'
111
+ Randy Wade 23 3
112
+ Benjamin Grant 98 20
113
+ Jenny Westmeyers 213 2
114
+ Maria Stone 8 88
115
+
116
+
117
+ == Iterating Associations and Data
118
+
119
+ === Chaining associations to get data sets
120
+
121
+ Chain associations to get an array of the results. For example, the following will
122
+ collect all of the comment objects for all blogs written by User with ID=2.
123
+
124
+ User.find(2).blogs.comments
125
+ [#<Comment id: 21, user_id: 8, blog_id: 85, rating: 5, ...
126
+
127
+ Clean this up by further chainging to return an array of the 'comment' field
128
+ of each comment object and use 'pr' to print in a column.
129
+
130
+ User.find(2).blogs.comments.comment.pr
131
+ I agree with the blogger
132
+ I'm new to ActiveRecord. Is there anyway to...
133
+ How do you use polymorphic associations to...
134
+ ...
135
+
136
+ Note that chaining is accumulative. To limit the results set, use the 'wi' or 'wo'
137
+ commands.
138
+
139
+ == Limiting data sets
140
+
141
+ === Limiting with the 'with' command
142
+
143
+ The 'with' command is abbreviated to 'wi' in an expression. It's like saying only show
144
+ me the results with this condition OR this condition, etc.
145
+
146
+ Say we want to limit comments in the above example to only those written by Randy Wade
147
+ and Maria Stone. If their user ids are [5, 9], then we can write
148
+
149
+ User.find(2).blogs.comments.wi(:user_id=>[5, 9]).comments.pr
150
+
151
+ We could use ARSpy to look up the user ids.
152
+
153
+ User.find(2).blogs.comments.user.pr :first_name, :last_name, :id
154
+
155
+ Note in this example, the 'wi' command is acting on attributes of the object immediately
156
+ preceding it, namely, the Comment object.
157
+
158
+ === ORing and ANDing with the 'wi' command
159
+
160
+ The 'wi' command can take an unlimited number of parameters. Multiple parameters
161
+ works as an OR operation on the results. So the above example could have been written
162
+
163
+ User.find(2).blogs.comments.wi(:user_id=>5, :user_id=>9).comments.pr
164
+
165
+ with the same results.
166
+
167
+ To get an AND operation, chain the 'wi' command.
168
+
169
+ User.find(2).blogs.comments.wi(:blog_id=>6).wi(:user_id=>5).comments.pr
170
+
171
+ This example returns only comments for blog with ID 6 and user associated with
172
+ the comment with ID 5. On the other hand,
173
+
174
+ User.find(2).blogs.comments.wi(:blog_id=>6, :user_id=>5).comments.pr
175
+
176
+ returns all comments associated with blog id 6 OR user id 5.
177
+
178
+ === Parameters of the 'wi' command
179
+
180
+ The 'wi' command can take integers, strings and a hash of conditions.
181
+
182
+ Strings are expressions evaluated against the preceding object.
183
+
184
+ User.find(2).blogs.comments.wi('user.last_name.include?("Grant")').comment.pr
185
+
186
+ Integers are IDs for the object.
187
+
188
+ User.find(2).blogs.comments.wi(20,21,22).comment.pr
189
+
190
+ A hash of {attribute=>[values]}. The following displays comments belonging
191
+ to blogs with ids 6, 7 or 8.
192
+
193
+ User.find(2).blogs.comments.wi(:blog_id=>[6,7,8]).comments.pr
194
+
195
+
196
+ === Excluding with the 'wo' (without) command
197
+
198
+ The 'wo' command does the exact opposite of the 'wi' command, showing only those
199
+ results that do not meet the conditions passed in the parameters.
200
+
201
+
202
+ == Dependencies
203
+
204
+ * ActiveRecord
205
+ * ActiveSupport
@@ -2,31 +2,31 @@ module Arspy
2
2
  module Operators
3
3
  def self.list_associations(active_record_klass)
4
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)
5
+ rows = active_record_klass.reflect_on_all_associations.map do |assoc|
6
+ counts[assoc.macro] ||= 0
7
+ counts[assoc.macro] += 1
8
+ self.format_column_association(assoc)
9
9
  end
10
- rows.sort!{|a,b| a.first <=> b.first}
10
+ rows.sort!{|row1,row2| row1.first <=> row2.first}
11
11
  self.print_matrix(rows)
12
- "Total: #{counts.inject(0){|sum, c| sum+c.last}} (" + counts.map{|c| "#{c.last} #{c.first}" }.join(', ') + ")"
12
+ "Total: #{counts.inject(0){|sum, count| sum+count.last}} (" + counts.map{|count| "#{count.last} #{count.first}" }.join(', ') + ")"
13
13
  end
14
14
 
15
15
  def self.list_fields(active_record_klass)
16
- rows = active_record_klass.columns.map do |c|
17
- self.format_column_field(c)
16
+ rows = active_record_klass.columns.map do |column|
17
+ self.format_column_field(column)
18
18
  end
19
- rows.sort!{|a,b| a.first <=> b.first}
19
+ rows.sort!{|row1,row2| row1.first <=> row2.first}
20
20
  self.print_matrix(rows)
21
21
  "Total #{active_record_klass.columns.size} field#{active_record_klass.columns.size == 1 ? '' : 's'}"
22
22
  end
23
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]
24
+ def self.format_column_association(assoc)
25
+ select_options = assoc.options.select{|k,v| [:through, :as, :polymorphic].include?(k)}
26
+ [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]
27
27
  end
28
- def self.format_column_field(f)
29
- [f.name.to_s, ":#{f.type}", "(#{f.sql_type})"]
28
+ def self.format_column_field(field)
29
+ [field.name.to_s, ":#{field.type}", "(#{field.sql_type})"]
30
30
  end
31
31
 
32
32
  def self.print_array(array, *args)
@@ -46,7 +46,7 @@ module Arspy
46
46
  end
47
47
 
48
48
  def self.print_object(object, *args)
49
- print_matrix([args.map{|a| object[a]}]) if args
49
+ print_matrix([args.map{|arg| object[arg]}]) if args
50
50
  puts(object.inspect) unless args
51
51
  nil
52
52
  end
@@ -56,7 +56,7 @@ module Arspy
56
56
  when String then obj.instance_eval(arg) rescue false
57
57
  when Integer then obj.id == arg
58
58
  when Hash
59
- arg.any?{|k,v| self.test_attribute(obj, k, (v.is_a?(Array) ? v : [v]) ) }
59
+ arg.any?{|key,val| self.test_attribute(obj, key, (val.is_a?(Array) ? val : [val]) ) }
60
60
  else
61
61
  false
62
62
  end
@@ -64,11 +64,11 @@ module Arspy
64
64
  end
65
65
  def self.with(array, *args)
66
66
  return array if (args.empty? || array.nil? || array.empty?)
67
- array.select{|o| o && self.test_object(o, args)}
67
+ array.select{|obj| obj && self.test_object(obj, args)}
68
68
  end
69
69
  def self.without(array, *args)
70
70
  return array if (args.empty? || array.nil? || array.empty?)
71
- array.select{|o| o && !self.test_object(o, args)}
71
+ array.select{|obj| obj && !self.test_object(obj, args)}
72
72
  end
73
73
  def self.enable_abbreviations; @@abbreviations_enabled = true; end
74
74
  def self.disable_abbreviations; @@abbreviations_enabled = false; end
@@ -110,35 +110,35 @@ module Arspy
110
110
  attrib_descriptors = attributes.map{|method_name| {:method_name=>method_name, :type=>:attribute, :abbr=>abbreviate_method_name(method_name)}}
111
111
  all_descriptors = assoc_descriptors + attrib_descriptors
112
112
  object.class.instance_variable_set('@arspy_ambiguous_abbreviations', remove_ambiguities(all_descriptors))
113
- object.class.instance_variable_set('@arspy_abbreviations', Hash[*all_descriptors.map{|d| [d[:abbr], d] }.flatten])
113
+ object.class.instance_variable_set('@arspy_abbreviations', Hash[*all_descriptors.map{|desc| [desc[:abbr], desc] }.flatten])
114
114
  end
115
115
  def self.remove_ambiguities(descriptors)
116
116
  list={}
117
117
  ambiguities = {}
118
- descriptors.each do |d|
119
- if list.include?(d[:abbr])
120
- if ambiguities[d[:abbr]]
121
- ambiguities[d[:abbr]][:methods] << d[:method_name]
118
+ descriptors.each do |desc|
119
+ if list.include?(desc[:abbr])
120
+ if ambiguities[desc[:abbr]]
121
+ ambiguities[desc[:abbr]][:methods] << desc[:method_name]
122
122
  else
123
- ambiguities[d[:abbr]] = {:abbr=>d[:abbr], :methods=>[d[:method_name]]}
124
- ambiguities[d[:abbr]][:methods] << list[d[:abbr]][:method_name]
123
+ ambiguities[desc[:abbr]] = {:abbr=>desc[:abbr], :methods=>[desc[:method_name]]}
124
+ ambiguities[desc[:abbr]][:methods] << list[desc[:abbr]][:method_name]
125
125
  end
126
126
  else
127
- list[d[:abbr]] = d
127
+ list[desc[:abbr]] = desc
128
128
  end
129
129
  end
130
- descriptors.reject!{|d| ambiguities.map{|h| h.first}.include?(d[:abbr])}
130
+ descriptors.reject!{|desc| ambiguities.map{|h| h.first}.include?(desc[:abbr])}
131
131
  ambiguities
132
132
  end
133
133
  def self.abbreviate_method_name(method_name)
134
- splits = method_name.to_s.split('_')
134
+ words = method_name.to_s.split('_')
135
135
  abbr=[]
136
- if splits.first == ''
136
+ if words.first == ''
137
137
  abbr << '_'
138
138
  end
139
- splits.reject!{|s| s == ''}
140
- abbr += splits.map do |s|
141
- chars = s.split(//)
139
+ words.reject!{|word| word == ''}
140
+ abbr += words.map do |word|
141
+ chars = word.split(//)
142
142
  first = chars.shift
143
143
  [first, chars.map{|ch| ch =~ /[0-9]/ ? ch : nil}].compact.flatten.join('')
144
144
  end
@@ -156,8 +156,8 @@ module Arspy
156
156
  end
157
157
  def self.interpret_attribute_or_method(array, method_name, *args)
158
158
  return array.map(&method_name) if args.empty?
159
- raise 'Hash not allowed as attribute conditionals' if args.any?{|a| a.is_a?(Hash)}
160
- array.select{|o| o && self.test_attribute(o, method_name, args)}
159
+ raise 'Hash not allowed as attribute conditionals' if args.any?{|arg| arg.is_a?(Hash)}
160
+ array.select{|obj| obj && self.test_attribute(obj, method_name, args)}
161
161
  end
162
162
  public
163
163
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 3
9
- version: 0.0.3
8
+ - 4
9
+ version: 0.0.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jeff Patmon