zcc 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/History.txt +15 -0
  2. data/Manifest.txt +12 -19
  3. data/Rakefile +2 -1
  4. data/bin/zcc +228 -202
  5. data/examples/zcc.yaml +21 -12
  6. data/examples/zebra/{register/empty → key/remove_me} +1 -1
  7. data/examples/zebra/{key/empty → lock/remove_me} +1 -1
  8. data/examples/zebra/records/0.mrc +1 -0
  9. data/examples/zebra/{lock/empty → register/remove_me} +1 -1
  10. data/examples/zebra/tab/marc21.abs +67 -0
  11. data/examples/zebra/tab/record.abs +1 -0
  12. data/examples/zebra/zebra.cfg +2 -2
  13. data/examples/zservers.yaml +869 -0
  14. data/lib/zcc.rb +8 -0
  15. data/lib/zcc/ansicolorz.rb +26 -0
  16. data/lib/zcc/cli_display.rb +461 -0
  17. data/lib/zcc/query.rb +183 -0
  18. data/lib/zcc/record.rb +265 -0
  19. data/lib/zcc/resultset.rb +169 -0
  20. data/lib/zcc/version.rb +2 -2
  21. data/lib/zcc/zoomer.rb +16 -2
  22. data/lib/zcc/zserver.rb +38 -0
  23. data/website/index.html +1 -1
  24. data/website/koha.html +18 -10
  25. data/website/koha.txt +13 -8
  26. data/website/zcc.html +35 -33
  27. data/website/zcc.txt +30 -32
  28. data/website/zebra.html +57 -15
  29. data/website/zebra.txt +56 -15
  30. metadata +23 -21
  31. data/examples/zebra/README +0 -2
  32. data/examples/zebra/records/examples/0.xml +0 -1
  33. data/examples/zebra/records/examples/1.xml +0 -1
  34. data/examples/zebra/records/examples/2.xml +0 -1
  35. data/examples/zebra/records/examples/3.xml +0 -1
  36. data/examples/zebra/records/examples/4.xml +0 -1
  37. data/examples/zebra/records/examples/5.xml +0 -1
  38. data/examples/zebra/records/examples/6.xml +0 -1
  39. data/examples/zebra/records/examples/7.xml +0 -1
  40. data/examples/zebra/records/examples/8.xml +0 -1
  41. data/examples/zebra/records/examples/9.xml +0 -1
  42. data/examples/zebra/shadow/empty +0 -1
  43. data/examples/zebra/tab/kohalis +0 -1
  44. data/examples/zebra/tmp/empty +0 -1
  45. data/lib/zcc/marcadditions.rb +0 -221
  46. data/lib/zcc/pickers.rb +0 -176
@@ -0,0 +1,183 @@
1
+ module ZCC
2
+
3
+ class Query
4
+ attr_reader :term, :zservers, :type, :zsearch
5
+
6
+ def initialize(term, zservers)
7
+ self.term = term #=> string of original query post any filtering
8
+ #puts "term: " + self.term
9
+ @zservers = []
10
+ @zservers << zservers #=>array of Zserver objects
11
+ self.zservers.compact!
12
+ self.zservers.flatten!
13
+ #puts self.inspect
14
+ #puts self.zservers[0].to_s
15
+ end
16
+
17
+ def term=(term)
18
+ if term =~ /\d+-\d+/
19
+ @term = lccn_conversion(term)
20
+ @type = 'lccn'
21
+ #puts "converted lccn to #{@term}"
22
+ @zsearch = "@attr 1=9 #{@term}"
23
+ elsif term =~ /\d+[X||\d]/ and !(term.match( /[a-wyz]/i ))
24
+ #puts "Searching for ISBN: #{term}"
25
+ @term = term
26
+ @type = 'isbn'
27
+ @zsearch = "@attr 1=7 #{@term}"
28
+ else ( term[/[a-z]/i] ) #-- This check for a string could be better!
29
+ #puts "searching for title:#{term}"
30
+ if term.match(/ :au /)
31
+ term = term.split(" :au ")
32
+ puts "Author and title then"
33
+ @term = term[0]
34
+ @zsearch = "@and @attr 1=4 \"#{@term}\" @attr 1=1 \"#{term[1]} \"" #was: "@attr 1=4 \"'#{@term}'\""
35
+ else
36
+ @term = term
37
+ @zsearch = "@attr 1=4 \"#{@term}\"" #was: "@attr 1=4 \"'#{@term}'\""
38
+ end
39
+ @type = 'title'
40
+
41
+ #puts self.term
42
+ #puts self.type
43
+ #puts self.zsearch
44
+
45
+ end
46
+ end
47
+
48
+ #This is the main search logic of the whole shebang. Aliased as 'search'.
49
+ #The method on a query object and is passed the number of records from each host to present to the user. Default number of records is 10.
50
+ #Still need to work on a way to get options in so this might change.
51
+ #Currently only MARC21 is supported.
52
+ def zoom(show = 10)
53
+ result_set = ResultSet.new(self)
54
+ #puts zservers.inspect
55
+ search_threads=[]
56
+ puts zservers.size.to_s + " z-servers in group " + zservers[0].group.to_s
57
+ z = 0
58
+ self.zservers.each do |server|
59
+ z += 1
60
+ #zservers are made up of...
61
+ #puts server.to_s
62
+ search_threads << Thread.new(server, z) do |myserver, myz|
63
+ begin
64
+ conn = ZOOM::Connection.new
65
+ conn.connect(myserver.host, server.port) #do |conn
66
+ conn.set_option('charset', 'UTF-8')
67
+ conn.preferred_record_syntax = 'MARC21'
68
+ conn.database_name = myserver.database
69
+ puts "#{myz} Searching: #{myserver.to_s} | #{self.zsearch}"
70
+ rset = conn.search(self.zsearch)
71
+ say(myz.to_s.headline + " Finished searching: #{myserver.to_s} | rset.size: " + "#{rset.size}".red.bold)
72
+ rset_recs = rset[0, show]
73
+ #puts "rset_recs in query.search: "
74
+ #puts rset_recs
75
+ i = 0
76
+ rset_recs.each do |rec|
77
+ #puts myserver.to_s
78
+ #puts rec
79
+ #puts
80
+ marc_record = ZCC.convert_char(rec)
81
+ #puts "gets past character conversion"
82
+ #puts "-------------\n"
83
+ #puts marc_record
84
+ #puts "---------------\n"
85
+ zcc_record = Record.new(marc_record, myserver)
86
+ result_set.ingest(zcc_record)
87
+ puts "#{myz} record #{i} from #{myserver}..."
88
+ i += 1
89
+ end
90
+ rescue Exception => e
91
+ zerror_log("dead thread: " + myserver.to_s + " | " + e)
92
+ puts "\a#{myz}!!!!!!!! Thread died #{myserver} !!!!!"
93
+ end
94
+ #puts "end: #{Thread.list}"
95
+ #puts "Results processed from: #{myserver.to_s}"
96
+ end
97
+
98
+ end
99
+ search_threads.each{|thread| thread.join}
100
+ return result_set
101
+ end
102
+
103
+ alias search zoom
104
+ def zerror_log error
105
+ File.open("#{File.expand_path("~")}/.zcc/zerror_log", "a+") do |f|
106
+ f.write error + "\n"
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+
113
+ def convert_char rsetrec
114
+ rec = MARC::Record.new_from_marc(rsetrec.raw)
115
+ #puts "initial rec" + rec.to_s
116
+ ldr9 = rec.leader[9, 1]
117
+ return_rec = ''
118
+ #puts "gets to creating dummy record."
119
+ if ldr9 == ' '
120
+ #return_rec = MARC::Record.new_from_marc(rsetrec.raw('MARC-8', 'UTF-8')) #This does NOT work
121
+ return_rec = MARC::XMLReader.new(StringIO.new(rsetrec.xml('MARC-8', 'UTF-8'))).to_a
122
+ return_rec = return_rec[0]
123
+ return_rec.leader[9,1] = 'a'
124
+ #puts "return_rec" + return_rec.to_s
125
+ elsif ldr9 == 'a'
126
+ #puts "already unicode"
127
+ return_rec = rec
128
+ else
129
+ raise "Invalid value in leader 9 for MARC21"
130
+ end
131
+ return_rec
132
+ end
133
+
134
+ def lccn_conversion lccn
135
+ split_lccn = lccn.split('-')
136
+ year_len = split_lccn[0].length
137
+ serial_len = split_lccn[1].length
138
+ start_length = year_len + serial_len
139
+ if year_len == 2
140
+ final_lccn = lccn.gsub('-', "#{'0' * (8 - start_length)}")
141
+ elsif year_len == 4
142
+ final_lccn = lccn.gsub('-', "#{'0' * (10 - start_length)}")
143
+ end
144
+ final_lccn
145
+ end
146
+
147
+ #++ compare() is a method to print out a comparison of two MARC records tag by tag (not by subfields).
148
+ # A match between lines is denoted with a 'm'. If there are differences between the records,
149
+ # the object that recieves the compare call is denoted with a '+' and the object passed in
150
+ # parens is denoted with '-'.
151
+ def compare_marc(orig, rec2)
152
+ puts "+ = #{orig.zserver.to_s}".bold
153
+ puts "- = #{rec2.zserver.to_s}".bold
154
+ orig = orig.marc
155
+ rec2 = rec2.marc
156
+
157
+ for ft in ('000'..'999')
158
+ fields_original = orig.find_all {|f| f.tag == ft}
159
+ fields_record2 = rec2.find_all {|f| f.tag == ft}
160
+ fields_orig = []
161
+ fields_rec2 = []
162
+
163
+ fields_original.each {|f| fields_orig << f.to_s}
164
+ fields_record2.each {|f| fields_rec2 << f.to_s}
165
+
166
+ matches = fields_orig & fields_rec2
167
+ matches.each { |f| puts "m".bold + " #{f}" } if matches
168
+
169
+ fields_orig -= matches
170
+ fields_orig.each {|f| puts "+".bold + " #{f}"} if fields_orig
171
+
172
+ fields_rec2 -= matches
173
+ fields_rec2.each {|f| puts "-".bold + " #{f}"} if fields_rec2
174
+ end
175
+ end
176
+
177
+ def blank_field_prompt(field, subfield)
178
+ #puts "subfield: #{subfield}"
179
+ value = ask("\nYour MARC record does not contain #{field}#{subfield}.\nEnter the intended value for #{field}#{subfield} or hit ENTER to leave blank\n#{field}#{subfield} > ")
180
+ value
181
+ end
182
+
183
+ end
@@ -0,0 +1,265 @@
1
+ module ZCC
2
+
3
+ class Record
4
+ attr_accessor :selected, :marc, :authorities, :rank
5
+ attr_reader :zserver
6
+ def initialize(marc, zserver, selected = false)
7
+ @marc = marc
8
+ @zserver = zserver
9
+ @selected = selected
10
+ @rank = 0
11
+ end
12
+
13
+ #def authorities<< (authority)
14
+ #end
15
+
16
+ def to_s
17
+ full_string = "\n---------------RECORD----------------\nzserver: " + zserver.to_s + "\n" + marc.to_s + "---------------RECORD----------------\n"
18
+ full_string
19
+ end
20
+
21
+ def title
22
+ self.marc['245']['a']
23
+ end
24
+
25
+ def author
26
+ end #1XX?
27
+
28
+ def extent
29
+ self.marc['260']['a']
30
+ end
31
+
32
+ def year_260
33
+ if self.marc['260'] && self.marc['260']['c']
34
+ date = self.marc['260']['c'].dup
35
+ date.gsub!('[', '')
36
+ date.gsub!(']', '')
37
+ date.gsub!('c','')
38
+ date
39
+ else
40
+ date = '0'
41
+ date
42
+ end
43
+ end
44
+
45
+ def years_fixed
46
+ end
47
+
48
+ def save(format)
49
+ end #save MARC record to file in proper format (MARC, MARCXML)
50
+
51
+
52
+
53
+ # linter depends on Perl's MARC::Lint and hence Perl's MARC::Record
54
+ # Use cpan to install them.
55
+ def linter
56
+ rec = self.marc
57
+ xml_rec = (rec.to_xml).to_s
58
+ if LINTER == false
59
+ puts 'You do not have the Perl MARC::Lint module installed or have disabled this feature.'
60
+ return
61
+ end
62
+ contents = `perl "#{File.expand_path("~")}"/.zcc/linter.pl "#{xml_rec}"`
63
+ if contents.empty?
64
+ puts "there were no errors detected by the linter."
65
+ else
66
+ puts contents
67
+ end
68
+ if rec.leader[18,1] == 'a'
69
+ puts "Leader indicates: AACR"
70
+ elsif rec.leader[18,1] == 'i'
71
+ puts "Leader indicates: ISBD"
72
+ else
73
+ puts "Leader indicates NOT AACR nor ISBD."
74
+ end
75
+ end
76
+
77
+ #++ The local_script method is for automating changes to the record before saving it. See the main script to turn on this feature. See zoomer.yaml for instructions on creating a script for your purposes.
78
+ def local_script(script)
79
+ #print $clear_code
80
+ record = self.marc
81
+
82
+ #--creating my procs for creating fields and appending subfields
83
+ # these two should probably be moved to methods
84
+ create_datafield = proc{|record, field, i1, i2| puts "Creating datafield: #{field}"; record.append(MARC::DataField.new(field.to_s, i1.to_s, i2.to_s)); puts "****", record, "***" if $testing;}
85
+
86
+ append_subfield = proc{|record, field, subfield, val|
87
+ field = field.to_s
88
+ subfield = subfield.to_s
89
+ value = val.dup
90
+ value = value.to_s
91
+ if value.include?('proc ')
92
+ #I've had problems with this bit but it seems to work now
93
+ value = value.sub(/^proc /, '')
94
+ real_value = eval("#{$procs[value]}")
95
+ next if real_value == nil
96
+ record[field].append(MARC::Subfield.new(subfield, real_value))
97
+ elsif
98
+ record[field].append(MARC::Subfield.new(subfield, value))
99
+ end
100
+ }
101
+
102
+ script.each do |single_script|
103
+ puts "--------------------------------------" if $testing
104
+ puts single_script.join(', ') if $testing
105
+ op, field, subfield, filler= single_script
106
+ field = field.to_s
107
+ subfield = subfield.to_s
108
+ operation = op.dup
109
+ if operation.include?('proc ')
110
+ operation = operation.dup.sub(/^proc /, '')
111
+ eval("#{$procs[operation]}")
112
+ elsif operation == 'create-field'
113
+ create_datafield.call(record, field, subfield, filler)
114
+ elsif operation == 'append-subfield'
115
+ append_subfield.call(record, field, subfield, filler)
116
+ elsif operation == 'prompt'
117
+ field_data = ask("What do you want to go into the #{field}#{subfield} #{filler}? ")
118
+
119
+ next if field_data.empty?
120
+ m_fields = record.find_all{|x| x.tag == field}
121
+ if m_fields.empty?
122
+ puts "m_fields is empty!!"
123
+ create_datafield.call(record, field, ' ', ' ')
124
+ m_fields = record.find_all{|x| x.tag == field}
125
+ end
126
+ m_fields.each {|field| field.append(MARC::Subfield.new(subfield, field_data))}
127
+ elsif operation == 'remove'
128
+ to_remove = record.find_all {|f| f.tag =~ Regexp.new( field.gsub('X','.'))}
129
+ to_remove.each do |remove|
130
+ record.fields.delete(remove)
131
+ end
132
+ elsif operation == 'sort-tags' ##This doesn't work right now
133
+ puts "sorting by tag"
134
+ puts record , "################"
135
+ record = record.sort_by{| field | field.tag}
136
+ puts record
137
+ STDIN.gets
138
+ else
139
+ puts "there's nothing for that yet in local_script"
140
+ end
141
+ puts record if $testing
142
+ end
143
+
144
+ record
145
+ end
146
+
147
+
148
+
149
+ def edit
150
+ puts "Subfield Editing"
151
+ puts "To denote which subfield you want to edit put it in the form of 245a.\nIn the case of repeating fields or subfields you will be prompted\nto choose the one you wish to edit. "
152
+ continue = true
153
+ while continue
154
+ fs = ask("What field and subfield would you like to edit (in form 245a) or quit?\n> "){|q| q.readline = true}
155
+ if fs == 'q' || fs == 'quit'
156
+ continue = false
157
+ next
158
+ end
159
+ if !(fs =~/\d\d\d\w/ )
160
+ puts "That's not a valid value"
161
+ next
162
+ end
163
+ self.change_subfield(fs)
164
+ end
165
+ end
166
+
167
+ #edit_subfield is passed to a MARC::Record object and give a subfield
168
+ def change_subfield(fs)
169
+ field_subfield = subfield_parse(fs)
170
+ field = field_subfield[0]
171
+ subfield = field_subfield[1]
172
+
173
+ fields = self.marc.find_all {|f| f.tag == field}
174
+ subfields = []
175
+ for field in fields
176
+ subfields << field.find_all {|s| s.code == subfield}
177
+ end
178
+ subfields.flatten!
179
+ if subfields.length > 1
180
+ i = 0
181
+ for subfield in subfields
182
+ puts "#{i}\t#{subfield}"
183
+ i += 1
184
+ end
185
+ num_to_change = ask("Which subfield do you want to change?"){|q| q.readline = true}
186
+ subfield_to_change = subfields[num_to_change.to_i]
187
+ elsif subfields.length == 1
188
+ subfield_to_change = subfields[0]
189
+ elsif subfields.empty?
190
+ puts "No such subfield!"
191
+ return nil
192
+ end
193
+
194
+
195
+ puts self.marc; puts
196
+
197
+ print "Currently:\t"
198
+ puts subfield_to_change.value
199
+ edit = ask("Your edit:\t"){|q| q.readline = true}
200
+ subfield_to_change.value = edit
201
+ puts; puts self.marc; puts
202
+
203
+ end
204
+
205
+ #subfield_parse method for getting from '245a' to ['245','a']
206
+ def subfield_parse(combined)
207
+ field_subfield = []
208
+ field_subfield << combined[0, 3]
209
+ field_subfield << combined[3,1]
210
+ end
211
+
212
+
213
+
214
+ # To use marc_to_csv it must be passed a csv template in the order of the fields.
215
+ # See the zoomer.yaml file for instructions on creating a template.
216
+ # See the main script for turning this feature on.
217
+ def marc_to_csv(template)
218
+ values = []
219
+ template.each do |template|
220
+ field, subfield, leng = template
221
+ field = field.to_s
222
+ subfield = subfield.to_s
223
+ if field == 'prompt'
224
+ value = ask("prompt: please enter the #{field} #{subfield}") #--subfield here is a label for the prompt
225
+
226
+ value = "\"#{value}\","
227
+ values << value
228
+ elsif field.length == 3 and field.match('\d')
229
+ fields = self.marc.find_all { | f | f.tag =~ Regexp.new( field.gsub('X','.'))}
230
+ value = ''
231
+ if fields.empty?
232
+ #puts "oh, no!"
233
+ value = blank_field_prompt(field, subfield)
234
+ #eval(blank_field_prompt)
235
+ else
236
+ fields.each do |f|
237
+ if f[subfield]
238
+ value += f[subfield] + "|"
239
+ else
240
+ value = blank_field_prompt(field, subfield)
241
+ end
242
+ end
243
+ end
244
+ value.sub!(/\|$/, '')
245
+ value = value[0, leng] if leng
246
+ #puts "value: #{value}"
247
+ value = "\"#{value}\","
248
+ values << value
249
+
250
+ else
251
+ puts "this is not a valid value for marc_to_csv"
252
+ end
253
+ end
254
+ #puts values
255
+ values[-1].sub!(/\,$/, '')
256
+ values = values.to_s
257
+ values += "\n"
258
+ end
259
+
260
+
261
+
262
+
263
+ end
264
+
265
+ end
@@ -0,0 +1,169 @@
1
+ module ZCC
2
+ class ResultSet
3
+ include Enumerable
4
+
5
+ attr_accessor :query, :sort_by, :index_start, :index_pos, :records, :rank
6
+
7
+ #All values of initialize are optional, though you'll want to supply it with a query object if you intend on getting records into your set through a z39.50 search. index_start and index_end will be used for the TUI display.
8
+ def initialize(query_object=nil, sort_by='title', index_start=0, index_pos=4 )
9
+ @query = query_object #query object
10
+
11
+ @sort_by = sort_by
12
+ @index_start = index_start
13
+ @index_pos = index_pos
14
+ #puts self.sort_by
15
+ #puts self.index_start
16
+ @records = []
17
+ #puts self.records.class
18
+ end
19
+
20
+ #Pretty prints the result set object. Nests pretty printed record objects within.
21
+ def to_s
22
+ full_string = "-------RESULT SET--------------\n" + "Result set has #{self.records.size} records."
23
+ self.records.each do |record|
24
+ full_string << record.to_s
25
+ end
26
+ full_string += "-------RESULT SET--------------\n"
27
+ end
28
+
29
+ # Method to add records only to a result set.
30
+ def ingest record
31
+ self.records << record
32
+ end
33
+
34
+ # appends a result set to another.
35
+ # Replaces the query and sort_by instance variables with the new ones.
36
+
37
+
38
+ #Removes unselected records from the result set. Uses the selected instance variable to check for true or false.
39
+ def remove_unselected!
40
+ self.records.each_index do |i|
41
+ if self.records[i].nil?
42
+ else
43
+ self.records[i] = nil unless self.records[i].selected
44
+ end
45
+ end
46
+ self.records.flatten!
47
+ self.records.compact!
48
+ self.records.uniq!
49
+ end
50
+
51
+ #Number of records in the result set
52
+ def size
53
+ self.records.length
54
+ end
55
+
56
+ alias length size
57
+
58
+ #returns number of ZCC::Records with @selected set to true
59
+ def selected_size
60
+ selected_records = self.find_all{|record| record.selected unless record == nil}
61
+ selected_records.length
62
+ end
63
+
64
+ alias selected_length selected_size
65
+
66
+ # This allows for Enumerable mixin.
67
+ def each
68
+ for record in @records
69
+ yield record
70
+ end
71
+ end
72
+
73
+ def sort_by_title!
74
+ self.records.compact!
75
+ self.records.flatten!
76
+ self.records = self.records.sort_by{|r| r.marc['245']['a']}
77
+ end
78
+
79
+ def sort_by_date!
80
+ self.remove_nil!
81
+ self.records = self.records.sort_by{ |r| r.year_260}
82
+ end
83
+
84
+ def sort_by_subfield! sf
85
+ self.remove_nil!
86
+ field, subfield = sf[0,3], sf[3,10]
87
+ subfield = 'a' if subfield == ''
88
+ nil_subfields = []
89
+ self.records.each_index do |i|
90
+ unless self.records[i].marc[field] && self.records[i].marc[field][subfield]
91
+ nil_subfields << self.records[i]
92
+ self.records[i] = nil
93
+ end
94
+ end
95
+ self.records.compact!
96
+ begin
97
+ self.records = self.records.sort_by{ |r| r.marc[field][subfield]}
98
+ rescue Exception => e
99
+ puts e
100
+ say_help("There was an error.\nYour records have not been sorted.")
101
+ end
102
+ self.records << nil_subfields
103
+ self.remove_nil!
104
+ end
105
+
106
+ def sort_by_standard!
107
+ self.remove_nil!
108
+ self.records = self.records.sort_by{|r| r.marc.leader[18]}
109
+ self.records.reverse
110
+ end
111
+
112
+ # Very simple relevancy ranking for title searches
113
+ def rank_by_relevance!
114
+ rank = 0
115
+ #raise NotImplementedError
116
+ #unless self.query.type == 'title'
117
+ # raise "Relevancy ranking only works with titles (and not even them yet)"
118
+ #end
119
+ self.rank
120
+ term = self.query.term
121
+ re_term = Regexp.new("#{term}", true)
122
+ re_term2 = Regexp.new("\^#{term}", true)
123
+ self.records.each do |rec|
124
+ #puts re_term
125
+ #puts rec.marc['245']['a'] =~ re_term
126
+ rec.rank += 5 if rec.marc['245']['a'] =~ re_term
127
+ rec.rank += 10 if rec.marc['245']['a'] =~ re_term2
128
+ end
129
+ self.records = self.records.sort_by{|record| record.rank}
130
+ self.records.reverse!
131
+ end
132
+
133
+ def remove_nil!
134
+ self.records.compact!
135
+ self.records.flatten!
136
+ end
137
+
138
+ def hits_per_source
139
+ end #=> hash{zurl=>[rec, rec],zurl=>[rec, rec]
140
+
141
+ def []
142
+
143
+
144
+ end # Record at that position
145
+
146
+ def empty?
147
+ if self.records.size == 0
148
+ return true
149
+ elsif self.records.size > 0
150
+ return false
151
+ end
152
+ end #returns true if the @ZCCRecords array is empty or only nil values
153
+
154
+ #def find_all
155
+ #self.find_all {|record| record.selected}
156
+ #end
157
+
158
+ def <<(result_set)
159
+ #puts record
160
+ self.records << result_set.records
161
+ self.query = result_set.query
162
+ self.sort_by = result_set.sort_by
163
+ self.records.flatten!
164
+ #puts self.records.inspect
165
+ end
166
+
167
+
168
+ end
169
+ end