classifieds_cli_app 0.1.1 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e55d1ca44e97ba4d0669686fde104c89d80eaa0
4
- data.tar.gz: 1d08f774c879eb72c6415b0e316c546307826a33
3
+ metadata.gz: 01be9206924ea8e879c3236c148e9c2d6a339ae5
4
+ data.tar.gz: 3588d77e9d44be47a69d3fb05ecf11da97624cd7
5
5
  SHA512:
6
- metadata.gz: bae09c79008cb2e988da7dddd9ee53652f9e594c54630caec8e7e668a8d77e4606f893463bd31d8bb5d2a90827f3977276cd8d8850b8cc3fd23b049bbefbdcb5
7
- data.tar.gz: 92862e9b67c72ee5bf6c89b496d8a822dc936803a6e15ad891f643809632c07f1f77a945e5a1518d3dbf949675002a3d14dbc0c31b40ea1d2e1a56ab1ca7ebec
6
+ metadata.gz: d499692bde2dc07c237c0e4ac450755d848e7fcf935e0e9b8d962bae5c3c6114f87a6e9f41b08b11b08d97909e4316abd835acc4e956f556107e3d2ebe425c40
7
+ data.tar.gz: 158f1ba26b7b52697104b73e80203782849ef4658177687b618f42ce464aa39bfde9a55a975e6be66ca967ba83586e0b35c69d4a5d33683ed1bd6eac15cad71c
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- classifieds (0.1.0)
5
- nokogiri
4
+ classifieds_cli_app (0.1.1)
5
+ nokogiri (~> 1.6)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -24,8 +24,8 @@ PLATFORMS
24
24
 
25
25
  DEPENDENCIES
26
26
  bundler (~> 1.13)
27
- classifieds!
28
- pry
27
+ classifieds_cli_app!
28
+ pry (~> 0)
29
29
  rake (~> 10.0)
30
30
 
31
31
  BUNDLED WITH
@@ -1,18 +1,12 @@
1
1
  class Classifieds::Auto < Classifieds::Vehicle # describes a Vehicle type of: Automobile
2
2
 
3
- @@ATTR_COLUMN_WIDTHS = [12, 10]
4
- @@SUMMARY_COL_FORMATS = [[24,'l'], [7,'r'], [8,'r']] # [width, justification]
3
+ SUMMARY_COL_FORMATS = [[32,'l'], [7,'r'], [9,'r']] # [width, justification]
5
4
 
6
5
  def initialize(year, make, model, mileage, price, condition, detail_link)
7
6
  super(year, make, model, price, condition, detail_link)
8
7
  @mileage = mileage
9
8
  end
10
9
 
11
- # Return attribute field width for given column
12
- def attr_width(col)
13
- @@ATTR_COLUMN_WIDTHS[col-1]
14
- end
15
-
16
10
  # Creates listings from summary web page
17
11
  def self.scrape_results_page(results_url, results_url_file, results_doc)
18
12
  Classifieds::AutoScraper.scrape_results_page(results_url, results_url_file, results_doc, self)
@@ -25,11 +19,11 @@ class Classifieds::Auto < Classifieds::Vehicle # describes a Vehicle type of: A
25
19
 
26
20
  # Returns a summary listing data row
27
21
  def summary_detail
28
- Classifieds::Listing.fmt_cols([@title, @mileage, @price], @@SUMMARY_COL_FORMATS)
22
+ Classifieds::Listing.format_cols([@title, @mileage, @price], SUMMARY_COL_FORMATS)
29
23
  end
30
24
 
31
25
  # Returns the summary listing title row
32
26
  def self.summary_header
33
- Classifieds::Listing.fmt_cols(['Vehicle', 'Mileage', 'Price '], @@SUMMARY_COL_FORMATS)
27
+ Classifieds::Listing.format_cols(['Vehicle', 'Mileage', 'Price '], SUMMARY_COL_FORMATS)
34
28
  end
35
29
  end
@@ -31,7 +31,7 @@ class Classifieds::AutoScraper # converts automobile classified listings into o
31
31
  # Returns detail attributes and values in detail_values hash
32
32
  def self.scrape_results_detail_page(detail_doc, item_condition, detail_values)
33
33
  # Create some entries manually.
34
- detail_values['Description'.to_sym] = detail_doc.css('.aiDetailsDescription')[0].children[2].text.strip
34
+ detail_values['Description'.to_sym] = detail_doc.css('.aiDetailsDescription')[0].children[2].text.strip # Description must be first attribute.
35
35
  detail_values['Condition'.to_sym] = item_condition
36
36
  detail_values['Certified'.to_sym] = ''
37
37
  # Create the rest from scraping the html's detail attrribute/value table.
@@ -1,17 +1,11 @@
1
1
  class Classifieds::Boat < Classifieds::Vehicle # describes a Vehicle type of: Boat
2
2
 
3
- @@ATTR_COLUMN_WIDTHS = [15, 14]
4
- @@SUMMARY_COL_FORMATS = [[24,'l'], [8,'r']] # [width, justification]
3
+ SUMMARY_COL_FORMATS = [[32,'l'], [9,'r']] # [width, justification]
5
4
 
6
5
  def initialize(year, make, model, price, condition, detail_link)
7
6
  super(year, make, model, price, condition, detail_link)
8
7
  end
9
8
 
10
- # Return attribute field width for given column
11
- def attr_width(col)
12
- @@ATTR_COLUMN_WIDTHS[col-1]
13
- end
14
-
15
9
  # Creates listings from summary web page
16
10
  def self.scrape_results_page(results_url, results_url_file, results_doc)
17
11
  Classifieds::BoatScraper.scrape_results_page(results_url, results_url_file, results_doc, self)
@@ -24,11 +18,11 @@ class Classifieds::Boat < Classifieds::Vehicle # describes a Vehicle type of: B
24
18
 
25
19
  # Returns a summary listing data row
26
20
  def summary_detail
27
- Classifieds::Listing.fmt_cols([@title, @price], @@SUMMARY_COL_FORMATS)
21
+ Classifieds::Listing.format_cols([@title, @price], SUMMARY_COL_FORMATS)
28
22
  end
29
23
 
30
24
  # Returns the summary listing title row
31
25
  def self.summary_header
32
- Classifieds::Listing.fmt_cols(['Boat', 'Price '], @@SUMMARY_COL_FORMATS)
26
+ Classifieds::Listing.format_cols(['Boat', 'Price '], SUMMARY_COL_FORMATS)
33
27
  end
34
28
  end
@@ -60,7 +60,7 @@ private
60
60
  def self.do_alt_processing(doc, item_condition, detail_values)
61
61
  # Create some entries manually.
62
62
  main_content = doc.css('#main-content')
63
- detail_values['Description'.to_sym] = main_content.css('p').text.strip
63
+ detail_values['Description'.to_sym] = main_content.css('p').text.strip # Description must be first attribute.
64
64
  detail_values['Condition'.to_sym] = item_condition
65
65
 
66
66
  # Create the rest from scraping the html's detail attrribute/value table.
@@ -68,7 +68,7 @@ private
68
68
  (0...detail_cells.size).each { |index|
69
69
  dl_tag = detail_cells[index].children
70
70
  (0...dl_tag.size).step(2) { |child|
71
- attribute = dl_tag[child].text
71
+ attribute = dl_tag[child].text.chomp(':')
72
72
  value = dl_tag[child+1].text
73
73
  detail_values[attribute.to_sym] = value
74
74
  }
@@ -94,7 +94,7 @@ private
94
94
  if 0 < dl_tag.size # need to do alternate normal processing.
95
95
  process_detail_list_alt(dl_tag, detail_values)
96
96
  else
97
- attribute = attribute_tag.text
97
+ attribute = attribute_tag.text.chomp(':')
98
98
  value_tag = detail_cells[index].children[3]
99
99
 
100
100
  if value_tag
@@ -121,7 +121,7 @@ private
121
121
  (0...doc.size).step(4) { |index|
122
122
  attribute = doc[index+1]
123
123
  next if attribute.nil?
124
- attr_text = attribute.text
124
+ attr_text = attribute.text.chomp(':')
125
125
  value_tag = doc[index+3]
126
126
  detail_values[attr_text.to_sym] = value_tag.text if value_tag
127
127
  }
@@ -133,7 +133,7 @@ private
133
133
  children = doc[index].children
134
134
  child_index = 1
135
135
  while child_index < children.size
136
- attribute = children[child_index].text
136
+ attribute = children[child_index].text.chomp(':')
137
137
  value = children[child_index+2]
138
138
  if value.nil? || value.text.strip.size == 0
139
139
  value = children[child_index+1]
@@ -12,7 +12,7 @@ class Classifieds::Item # describes the thing in a listing that is for sale
12
12
 
13
13
  # Empty list of created objects
14
14
  def self.clear
15
- # all.clear
15
+ # all.clear # not used, for now.
16
16
  end
17
17
 
18
18
  COLUMN_SEPARATION = 5
@@ -21,49 +21,74 @@ class Classifieds::Item # describes the thing in a listing that is for sale
21
21
  def details_to_string(addon_details)
22
22
  Classifieds::Listing.scrape_listing_details(self.class, @detail_url, @condition, @detail_values) if @detail_values.empty?
23
23
 
24
+ # Setup attribute/value details array
24
25
  detail_values_array = @detail_values.to_a
25
- addon_details.delete(:Phone) if detail_phone # do not use seller phone if item details has a phone.
26
+ addon_details.delete(:Phone) if detail_phone? # do not addon phone if item details has a phone.
26
27
  detail_values_array.concat(addon_details.to_a)
27
- offset = detail_values_array.size / 2 # prepare for two column output.
28
- mod2 = detail_values_array.size % 2 # and account for an odd number of details.
29
- col1_ljust = max_col1_width(detail_values_array, offset+mod2) + COLUMN_SEPARATION
30
- result = ''
28
+ col2_offset = ((detail_values_array.size-1) / 2) + ((detail_values_array.size-1) % 2) # remove 1 from array size for description. It will get its own row.
31
29
 
32
- (0...offset+mod2).each { |index|
33
- # column 1
34
- attribute = detail_values_array[index][0].to_s
35
- value = detail_values_array[index][1]
36
- result << " #{Classifieds::Listing.format_detail(attribute, attr_width(1), value).ljust(col1_ljust)}"
30
+ col_attr_width = []
31
+ col_width = []
32
+
33
+ # Calculate column 1 widths
34
+ widths = max_col_widths(detail_values_array, 0, col2_offset) # [max_attr_width, max_val_width, max_column_width]
35
+ col_attr_width[0] = widths[0]
36
+ col_width[0] = [widths[2], Classifieds::Item.col_width_limit(1)].min # limit col width to max col width
37
+ col_width[0] += COLUMN_SEPARATION
38
+
39
+ # Calculate column 2 widths
40
+ widths = max_col_widths(detail_values_array, col2_offset+1, detail_values_array.size) # [max_attr_width, max_val_width, max_column_width]
41
+ col_attr_width[1] = widths[0]
42
+ col_width[1] = [widths[2], Classifieds::Item.col_width_limit(2)].min # limit col width to max col width
37
43
 
38
- # column 2
39
- if 'Description' == attribute.to_s # Have Description be on its own line.
40
- result << "\n"
41
- elsif (index + offset) < detail_values_array.size
42
- attribute = detail_values_array[index+offset][0].to_s
43
- value = detail_values_array[index+offset][1]
44
- result << "#{Classifieds::Listing.format_detail(attribute, attr_width(2), value)}\n"
45
- end
44
+ # Description value spans all columns.
45
+ attribute = detail_values_array[0][0].to_s
46
+ value = detail_values_array[0][1]
47
+ result = " #{Classifieds::Listing.format_detail(attribute, col_attr_width[0], value)}\n"
48
+
49
+ # Then display remaining details in two column format.
50
+ (1..col2_offset).each { |index|
51
+ result << " #{Classifieds::Item.format_pair(detail_values_array, index, col_attr_width[0], col_width[0])}"
52
+ next if (index+col2_offset) == detail_values_array.size # when odd number of details.
53
+ result << " #{Classifieds::Item.format_pair(detail_values_array, index+col2_offset, col_attr_width[1], col_width[1])}\n"
46
54
  }
47
55
  result
48
56
  end
49
57
 
50
- def detail_phone
51
- @detail_values[:Phone]
52
- end
53
-
54
58
  ## PRIVATE METHODS
55
59
  private
56
60
 
57
- # Find width of widest col1 data
58
- def max_col1_width(detail_values_array, end_val)
59
- max_width = 0
60
- (0...end_val).each { |index|
61
+ # Return attribute field width for given column
62
+ def self.col_width_limit(col)
63
+ [40, 40][col-1]
64
+ end
65
+
66
+ # Do the details contain a phone entry?
67
+ def detail_phone?
68
+ @detail_values[:Phone] ? true : false
69
+ end
70
+
71
+ # Formats an attribute/value pair for printing
72
+ def self.format_pair(array, index, attr_width, col_width)
73
+ attribute = array[index][0].to_s
74
+ value = array[index][1]
75
+ Classifieds::Listing.format_detail(attribute, attr_width, value).slice(0,col_width).ljust(col_width)
76
+ end
77
+
78
+ # Find widths of widest column data
79
+ # Returns [max_attr_width, max_val_width, max_column_width]
80
+ def max_col_widths(detail_values_array, start_index, end_index)
81
+ max_attr_width = 0
82
+ max_val_width = 0
83
+ (start_index...end_index).each { |index|
61
84
  attribute = detail_values_array[index][0].to_s
62
- next if 'Description' == attribute # Description spans all cols, so don't count its width.
85
+ width = Classifieds::Listing.format_detail_attr(attribute, 0).size
86
+ max_attr_width = width if width > max_attr_width
87
+ next if 'Description' == attribute # Description spans all cols, so don't consider its value width.
63
88
  value = detail_values_array[index][1]
64
- detail_width = Classifieds::Listing.format_detail(attribute, attr_width(1), value).size
65
- max_width = detail_width if detail_width > max_width
89
+ width = Classifieds::Listing.format_detail_val(value, 0).size
90
+ max_val_width = width if width > max_val_width
66
91
  }
67
- max_width
92
+ [max_attr_width, max_val_width, max_attr_width + max_val_width + ': '.size]
68
93
  end
69
94
  end
@@ -28,7 +28,7 @@ class Classifieds::Listing # describes a classified advertisement
28
28
 
29
29
  # Prints the specified summary listings for the specified item subclass
30
30
  def self.print_summary(item_class, start_index, end_index)
31
- puts " #{item_class.summary_header} #{Classifieds::Seller.summary_header} #{lfmt('List Date', 10)}"
31
+ puts " #{item_class.summary_header} #{Classifieds::Seller.summary_header} #{lfmt('List Date', 10)}"
32
32
  all[start_index..end_index].each_with_index { |listing, index| puts listing.summary_detail_row(start_index+index+1) }
33
33
  end
34
34
 
@@ -47,30 +47,30 @@ class Classifieds::Listing # describes a classified advertisement
47
47
 
48
48
  # Prints a summary detail row
49
49
  def summary_detail_row(item_number)
50
- "#{(item_number).to_s.rjust(2)}. #{@item.summary_detail} #{@seller.summary_detail} #{Classifieds::Listing.lfmt(@start_date, 10)}"
50
+ "#{(item_number).to_s.rjust(2)}. #{@item.summary_detail} #{@seller.summary_detail} #{Classifieds::Listing.lfmt(@start_date, 10)}"
51
51
  end
52
52
 
53
53
  # Formats an array of strings according to an array of formats
54
- def self.fmt_cols(values, formats)
54
+ def self.format_cols(values, formats)
55
55
  result = ''
56
- values.each_with_index { |value, index| result << "#{fmt_col(value, formats[index][0], formats[index][1])} " }
56
+ values.each_with_index { |value, index| result << "#{format_col(value, formats[index][0], formats[index][1])} " }
57
57
  result
58
58
  end
59
59
 
60
60
  MAX_LINE_LENGTH = 100
61
61
 
62
62
  # Format a detail attribute
63
- def self.fmt_detail_attr(string, width)
63
+ def self.format_detail_attr(string, width)
64
64
  "#{lfmt(string, width)}"
65
65
  end
66
66
 
67
67
  # Format a detail item
68
68
  def self.format_detail(attr, attr_width, val)
69
- "#{fmt_detail_attr(attr, attr_width)}: #{fmt_detail_val(val, attr_width)}"
69
+ "#{format_detail_attr(attr, attr_width)}: #{format_detail_val(val, attr_width)}"
70
70
  end
71
71
 
72
72
  # Format a detail value (with wrap if necessary)
73
- def self.fmt_detail_val(string, wrap_indent)
73
+ def self.format_detail_val(string, wrap_indent)
74
74
  new_string = string.strip
75
75
  if new_string.size > MAX_LINE_LENGTH
76
76
  new_string = ''
@@ -101,7 +101,7 @@ class Classifieds::Listing # describes a classified advertisement
101
101
  end
102
102
 
103
103
  # Format a column
104
- def self.fmt_col(str, width, justify)
104
+ def self.format_col(str, width, justify)
105
105
  case justify
106
106
  when 'l'
107
107
  "#{lfmt(str, width)}"
@@ -114,7 +114,7 @@ class Classifieds::Listing # describes a classified advertisement
114
114
 
115
115
  # Left justify string and pad or trim to size
116
116
  def self.lfmt(string, size)
117
- string.slice(0,size).ljust(size)
117
+ size == 0 ? string : string.slice(0,size).ljust(size)
118
118
  end
119
119
 
120
120
  # Right justify string and pad or trim to size
@@ -1,7 +1,7 @@
1
1
  class Classifieds::Seller # describes the entity that is selling the .Item in a .Listing
2
2
  # A seller is uniquely identified by name + location + phone
3
3
 
4
- @@SUMMARY_COL_FORMATS = [[28,'l'], [32,'l']] # [width, justification]
4
+ SUMMARY_COL_FORMATS = [[28,'l'], [32,'l']] # [width, justification]
5
5
 
6
6
  @@all_sellers = []
7
7
 
@@ -34,12 +34,12 @@ class Classifieds::Seller # describes the entity that is selling the .Item in a
34
34
 
35
35
  # Return a summary listing detail row
36
36
  def summary_detail
37
- Classifieds::Listing.fmt_cols([@name, @location], @@SUMMARY_COL_FORMATS)
37
+ Classifieds::Listing.format_cols([@name, @location], SUMMARY_COL_FORMATS)
38
38
  end
39
39
 
40
40
  # Return the summary listing summary title row
41
41
  def self.summary_header
42
- Classifieds::Listing.fmt_cols(['Seller', 'Location'], @@SUMMARY_COL_FORMATS)
42
+ Classifieds::Listing.format_cols(['Seller', 'Location'], SUMMARY_COL_FORMATS)
43
43
  end
44
44
 
45
45
  ## PRIVATE METHODS
@@ -1,3 +1,3 @@
1
1
  module Classifieds
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: classifieds_cli_app
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Pfingst