eve_app 0.1.0

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +21 -0
  5. data/app/controllers/eve_app/application_controller.rb +5 -0
  6. data/app/controllers/eve_app/regions_controller.rb +3 -0
  7. data/app/controllers/eve_app/simple_resource_controller.rb +38 -0
  8. data/app/controllers/eve_app/solar_systems_controller.rb +2 -0
  9. data/app/controllers/eve_app/types_controller.rb +3 -0
  10. data/app/helpers/eve_app/entity_helper.rb +15 -0
  11. data/app/helpers/eve_app/output_helper.rb +25 -0
  12. data/app/models/eve_app/activity.rb +26 -0
  13. data/app/models/eve_app/activity_material.rb +11 -0
  14. data/app/models/eve_app/activity_product.rb +8 -0
  15. data/app/models/eve_app/activity_skill.rb +5 -0
  16. data/app/models/eve_app/application_record.rb +5 -0
  17. data/app/models/eve_app/category.rb +27 -0
  18. data/app/models/eve_app/concerns/activity_relation.rb +26 -0
  19. data/app/models/eve_app/group.rb +3 -0
  20. data/app/models/eve_app/market_group.rb +25 -0
  21. data/app/models/eve_app/region.rb +3 -0
  22. data/app/models/eve_app/solar_system.rb +14 -0
  23. data/app/models/eve_app/station.rb +9 -0
  24. data/app/models/eve_app/type.rb +57 -0
  25. data/app/serializers/eve_app/application_serializer.rb +2 -0
  26. data/app/serializers/eve_app/region_serializer.rb +5 -0
  27. data/app/serializers/eve_app/solar_system_serializer.rb +8 -0
  28. data/app/serializers/eve_app/type_serializer.rb +11 -0
  29. data/config/routes.rb +5 -0
  30. data/lib/eve_app/engine.rb +6 -0
  31. data/lib/eve_app/eve_central.rb +78 -0
  32. data/lib/eve_app/item_parser.rb +112 -0
  33. data/lib/eve_app/sde/data_importer.rb +43 -0
  34. data/lib/eve_app/sde/downloader.rb +53 -0
  35. data/lib/eve_app/sde/normalizer.rb +106 -0
  36. data/lib/eve_app/sde.rb +27 -0
  37. data/lib/eve_app/version.rb +3 -0
  38. data/lib/eve_app/xml_api/calls.rb +197 -0
  39. data/lib/eve_app/xml_api/classes.rb +260 -0
  40. data/lib/eve_app/xml_api/client.rb +86 -0
  41. data/lib/eve_app/xml_api.rb +10 -0
  42. data/lib/eve_app.rb +35 -0
  43. data/lib/table-list.yml +88 -0
  44. data/lib/table-map.yml +177 -0
  45. data/lib/tasks/eve_app.rake +30 -0
  46. metadata +186 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cc346e32e0fdcd281846f98346cd231288a606ee
4
+ data.tar.gz: 1f6e12200b262a43d5407b192a5ad732032186a1
5
+ SHA512:
6
+ metadata.gz: 2e03523c202975205711c309a7cb3d4cf2d81f97c584158ce0e2bcc034a4394d639ef522da0a6e65994eb8164d447c3cfa7f7a15325a9bb55519fd8a57b11ffc
7
+ data.tar.gz: 036c919a55bf638855191868595c0734f9f380bb835fa00fd5828018c0da12229b993d9f5944e0ad53e48fb525166abb08d5035269086f1137a005458d38c620
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Danny Hiemstra
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.md ADDED
@@ -0,0 +1,28 @@
1
+ # EveApp
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'eve-app'
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install eve-app
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
8
+ load 'rails/tasks/engine.rake'
9
+ load 'rails/tasks/statistics.rake'
10
+ load "lib/tasks/eve_app.rake"
11
+
12
+ require 'bundler/gem_tasks'
13
+ require 'rake/testtask'
14
+
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.libs << 'test'
17
+ t.pattern = 'test/**/*_test.rb'
18
+ t.verbose = false
19
+ end
20
+
21
+ task default: :test
@@ -0,0 +1,5 @@
1
+ module EveApp
2
+ class ApplicationController < ActionController::API
3
+ # include ActiveModelSerializers::Serialization
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ class EveApp::RegionsController < EveApp::SimpleResourceController
2
+ self.allow_index = true
3
+ end
@@ -0,0 +1,38 @@
1
+ require_dependency "eve_app/application_controller"
2
+
3
+ module EveApp
4
+ class SimpleResourceController < EveApp::ApplicationController
5
+ class_attribute :allow_index
6
+ class_attribute :includes
7
+ class_attribute :serializer_includes
8
+ # before_action :authenticate_token!
9
+
10
+ def index
11
+ if params[:filter] && params[:filter][:id]
12
+ render json: scope.where(id: params[:filter][:id].split(',')), include: serializer_include
13
+ elsif self.allow_index
14
+ render json: scope, include: self.serializer_include
15
+ else
16
+ render :bad_request
17
+ end
18
+ end
19
+
20
+ def show
21
+ render json: scope.find(params[:id]) #, include: self.serializer_include
22
+ end
23
+
24
+ protected
25
+
26
+ def model_name
27
+ self.class.to_s.gsub(/(.+?)Controller/, '\1').singularize.constantize
28
+ end
29
+
30
+ def scope
31
+ model_name.includes(self.includes).page(params[:page])
32
+ end
33
+
34
+ def serializer_include
35
+ self.serializer_includes || ['*']
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,2 @@
1
+ class EveApp::SolarSystemsController < EveApp::SimpleResourceController
2
+ end
@@ -0,0 +1,3 @@
1
+ class EveApp::TypesController < EveApp::SimpleResourceController
2
+ self.includes = %w(group category)
3
+ end
@@ -0,0 +1,15 @@
1
+ module EveApp::EntityHelper
2
+ def entity(record, size: :medium, description: nil, link_url: nil)
3
+ title = link_url ? link_to(record.name, link_url) : record.name
4
+ body = [
5
+ content_tag(:h6, title.html_safe),
6
+ content_tag(:div, description || record.try(:description), class: 'text-sm text-muted')
7
+ ].join("\n").html_safe
8
+ wrapper = [
9
+ image_tag(record.image),
10
+ content_tag(:div, body, class: "media-body")
11
+ ].join("\n").html_safe
12
+
13
+ raw content_tag(:div, wrapper, class: "media media-entity #{size}")
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module EveApp::OutputHelper
2
+ def isk(number, force_negative=false, round=true)
3
+ if number
4
+ number = BigDecimal.new(number) if number.is_a?(String)
5
+ number = number / 100.0
6
+ precision = round ? 0 : 2
7
+ number = -number if force_negative
8
+ negative = number < 0
9
+ isk = number_to_currency(number, unit: 'ISK', separator: '.', delimiter: ',', format: "%n %u", precision: precision)
10
+ content_tag(:span, isk, class: "isk #{negative ? 'text-danger' : ''}")
11
+ else
12
+ '-'
13
+ end
14
+ end
15
+
16
+ def number(value)
17
+ number_with_delimiter value
18
+ end
19
+
20
+ def percentage(value)
21
+ return '-' unless value
22
+ value = BigDecimal.new(value) if value.is_a?(String)
23
+ value.round(1).to_s + "%"
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ class EveApp::Activity < EveApp::ApplicationRecord
2
+ TYPE_MAP = {
3
+ 1 => :manufacture,
4
+ 3 => :research_te,
5
+ 4 => :research_me,
6
+ 5 => :copying,
7
+ 8 => :invention
8
+ }
9
+ MANUFACTURE = 1
10
+ RESEARCH_TE = 3
11
+ RESEARCH_ME = 4
12
+ COPYING = 5
13
+ INVENTION = 8
14
+
15
+ has_many :industry_activities
16
+
17
+ def icon
18
+ case id
19
+ when 1 then 'manufacturing.png'
20
+ when 3 then 'researchTime.png'
21
+ when 4 then 'researchMaterial.png'
22
+ when 5 then 'copying.png'
23
+ when 8 then 'invention.png'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ class EveApp::ActivityMaterial < EveApp::ApplicationRecord
2
+ include EveApp::ActivityRelation
3
+
4
+ belongs_to :material, class_name: 'EveApp::Type', foreign_key: :material_type_id
5
+
6
+ scope :order_by_quantity, -> { order(quantity: :desc) }
7
+
8
+ def quantity_for(runs)
9
+ runs > 0 ? runs * quantity : 0
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ class EveApp::ActivityProduct < EveApp::ApplicationRecord
2
+ include EveApp::ActivityRelation
3
+
4
+ belongs_to :activity_product, class_name: 'EveApp::Type', foreign_key: :product_type_id
5
+
6
+ scope :invention, -> { where(activity_id: EveApp::Activity::INVENTION) }
7
+ scope :manufacture, -> { where(activity_id: EveApp::Activity::MANUFACTURE) }
8
+ end
@@ -0,0 +1,5 @@
1
+ class EveApp::ActivitySkill < EveApp::ApplicationRecord
2
+ include EveApp::ActivityRelation
3
+
4
+ belongs_to :skill, class_name: 'EveApp::Type', foreign_key: :skill_id
5
+ end
@@ -0,0 +1,5 @@
1
+ module EveApp
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ class EveApp::Category < EveApp::ApplicationRecord
2
+ MATERIAL = 4
3
+ ACCESSOIRE = 5
4
+ SHIP = 6
5
+ MODULE = 7
6
+ CHARGE = 8
7
+ BLUEPRINT = 9
8
+ SKILL = 16
9
+ DATACORE = 17
10
+ DRONE = 18
11
+ DEPLOYABLE = 22
12
+ STARBASE = 23
13
+ IMPLANT = 20
14
+ APPAREL = 30
15
+ SUBSYSTEM = 32
16
+ ANCIENT_RELICS = 34
17
+ DECRYPTOR = 35
18
+ PLANET_INTERACTION = 41
19
+ COMMODITY = 43
20
+ STRUCTURE = 65
21
+ STRUCTURE_MODULE = 66
22
+ FIGHTER = 87
23
+
24
+ has_many :types
25
+
26
+ scope :published, -> { where(published: true) }
27
+ end
@@ -0,0 +1,26 @@
1
+ module EveApp::ActivityRelation
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ belongs_to :type
6
+ belongs_to :activity
7
+
8
+ # scope :invention, -> { where(activity_id: EveApp::Activity::INVENTION) }
9
+ scope :for, -> (tid, aid) { where(type_id: tid, activity_id: aid) }
10
+ scope :order_by_name, -> {
11
+ includes(type_name).order('types.name')
12
+ }
13
+
14
+ # ????
15
+ # def activity_activity
16
+ # EveApp::Activity.where(type_id: type_id, activity_id: activity_id)
17
+ # end
18
+ end
19
+
20
+ class_methods do
21
+ def type_name
22
+ reflections = self.reflect_on_all_associations(:belongs_to)
23
+ reflections.select { |r| r.options[:class_name] == 'Type' && r.name != :type }.first.name
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ class EveApp::Group < EveApp::ApplicationRecord
2
+ has_many :types, class_name: 'EveApp::Type'
3
+ end
@@ -0,0 +1,25 @@
1
+ class EveApp::MarketGroup < EveApp::ApplicationRecord
2
+ BLUEPRINTS = 2
3
+ SHIPS = 4
4
+ SHIP_EQUIPTMENT = 9
5
+ SHIP_MODIFICATIONS = 955
6
+ DRONES = 157
7
+ CAPITAL_MODULE = 1052
8
+ DECRYPTORS = 1873
9
+ REFINED_MATERIALS = 1335
10
+ PILOT_SERVICES = 1942
11
+ NANITE_PASTE = 1103
12
+
13
+ belongs_to :parent, class_name: 'EveApp::MarketGroup', foreign_key: 'parent_group_id'
14
+ belongs_to :root, class_name: 'EveApp::MarketGroup', foreign_key: 'root_group_id'
15
+
16
+ has_many :types
17
+
18
+ def root
19
+ obj = self
20
+ while !obj.parent.nil?
21
+ obj = obj.parent
22
+ end
23
+ obj
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ class EveApp::Region < EveApp::ApplicationRecord
2
+ has_many :solar_systems, class_name: 'EveApp::SolarSystem'
3
+ end
@@ -0,0 +1,14 @@
1
+ class EveApp::SolarSystem < EveApp::ApplicationRecord
2
+ JITA = 30000142
3
+ PERIMITER = 30000144
4
+ NEW_CALDARI = 30000145
5
+ AMARR = 30002187
6
+ NIYABAINEN = 30000143
7
+ P3EN = 30000250
8
+ ASHAB = 30003491
9
+ MAURASI = 30000140
10
+
11
+ belongs_to :region
12
+
13
+ has_many :stations
14
+ end
@@ -0,0 +1,9 @@
1
+ class EveApp::Station < EveApp::ApplicationRecord
2
+ belongs_to :solar_system
3
+ belongs_to :region
4
+
5
+ has_many :stations
6
+
7
+ scope :active, -> { where(deleted: false) }
8
+ scope :structure, -> { where('id > ?', 100000000) }
9
+ end
@@ -0,0 +1,57 @@
1
+ class EveApp::Type < EveApp::ApplicationRecord
2
+ belongs_to :category
3
+ belongs_to :group
4
+ belongs_to :market_group
5
+ belongs_to :market_group_root, class_name: 'EveApp::MarketGroup'
6
+
7
+ scope :published, -> { where(published: true) }
8
+
9
+ def blueprint?
10
+ category_id == EveApp::Category::BLUEPRINT
11
+ end
12
+
13
+ def accessoire?
14
+ category_id == EveApp::Category::ACCESSOIRE
15
+ end
16
+
17
+ def ship?
18
+ category_id == EveApp::Category::SHIP
19
+ end
20
+
21
+ def image(size=64)
22
+ "https://image.eveonline.com/Type/#{id}_#{size}.png"
23
+ end
24
+
25
+ def description
26
+ category.name
27
+ end
28
+
29
+ def sort_key
30
+ @_sort_key ||= [sort_index, name].join('-')
31
+ end
32
+
33
+ private
34
+
35
+ def sort_index
36
+ case category_id
37
+ when EveApp::Category::SHIP
38
+ return 1000
39
+ when EveApp::Category::MODULE
40
+ case market_group_root_id
41
+ when EveApp::MarketGroup::SHIP_MODIFICATIONS
42
+ return 1500
43
+ else
44
+ return 1100
45
+ end
46
+ when EveApp::Category::CHARGE
47
+ case market_group_id
48
+ when EveApp::MarketGroup::NANITE_PASTE
49
+ return 1300
50
+ else
51
+ return 1200
52
+ end
53
+ when EveApp::Category::DRONE then 1500
54
+ else 2000
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,2 @@
1
+ class EveApp::ApplicationSerializer < ActiveModel::Serializer
2
+ end
@@ -0,0 +1,5 @@
1
+ class EveApp::RegionSerializer < EveApp::ApplicationSerializer
2
+ attributes :id, :name
3
+
4
+ # link(:self) { region_path(object) }
5
+ end
@@ -0,0 +1,8 @@
1
+ class EveApp::SolarSystemSerializer < ActiveModel::Serializer
2
+ attributes :id, :name
3
+ attribute :security do
4
+ object.security.round(2)
5
+ end
6
+
7
+ # link(:self) { solar_system_path(object) }
8
+ end
@@ -0,0 +1,11 @@
1
+ class EveApp::TypeSerializer < ActiveModel::Serializer
2
+ attributes :id, :name
3
+ attribute :category do
4
+ object.category.try(:name)
5
+ end
6
+ attribute :group do
7
+ object.group.try(:name)
8
+ end
9
+
10
+ # link(:self) { type_path(object) }
11
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ EveApp::Engine.routes.draw do
2
+ resources :types, only: %i(index show)
3
+ resources :solar_systems, only: %i(index show), path: 'solar-systems'
4
+ resources :regions, only: %i(index show)
5
+ end
@@ -0,0 +1,6 @@
1
+ module EveApp
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace EveApp
4
+ config.generators.api_only = true
5
+ end
6
+ end
@@ -0,0 +1,78 @@
1
+ require 'rest-client'
2
+ require 'multi_json'
3
+
4
+ module EveApp
5
+ class EveCentral
6
+ ENDPOINT = 'http://api.eve-central.com/api/marketstat/json'
7
+ MAX_TRIES = 3
8
+
9
+ def self.fetch(type_ids, system_id=EveApp::SolarSystem::JITA)
10
+ type_ids = Array[type_ids].flatten
11
+ results = []
12
+ type_ids.in_groups_of(100, false) do |group|
13
+ results += fetch_prices(group, system_id)
14
+ end
15
+ results
16
+ end
17
+
18
+ def self.fetch_prices(type_ids, system_id=EveApp::SolarSystem::JITA)
19
+ type_ids = Array[type_ids].flatten
20
+ json = request(generate_url(ENDPOINT, type_ids, system_id))
21
+ parse_response(json)
22
+ end
23
+
24
+ def self.generate_url(endpoint, type_ids, system_id, extra={})
25
+ type_ids = Array[type_ids].flatten
26
+ params = type_ids.map { |id| "typeid=#{id}" }
27
+ params.push("usesystem=#{system_id}")
28
+ extra.each do |key,val|
29
+ params.push("#{key}=#{val}")
30
+ end
31
+ "#{endpoint}?#{params.join('&')}"
32
+ end
33
+
34
+ def self.request(url)
35
+ response = nil
36
+
37
+ 1.upto(MAX_TRIES) do |try|
38
+ begin
39
+ response = RestClient.get(url, user_agent: 'EveBuddy 0.1')
40
+ break if response
41
+ rescue StandardError => e
42
+ puts "==================================================="
43
+ puts "Error received #{e.class}: #{e.message}"
44
+ puts url.to_s
45
+ puts "==================================================="
46
+ break
47
+ end
48
+ end
49
+
50
+ MultiJson.load(response.body, symbolize_keys: true) rescue {}
51
+ end
52
+
53
+ def self.parse_response(json)
54
+ json.map { |row|
55
+ OpenStruct.new(
56
+ type_id: row[:all][:forQuery][:types].first,
57
+ solar_system_id: row[:all][:forQuery][:systems].first,
58
+ buy: PriceResult.new(:buy, row[:buy]),
59
+ sell: PriceResult.new(:sell, row[:sell])
60
+ )
61
+ }
62
+ end
63
+
64
+ class PriceResult
65
+ attr_reader :type, :volume, :average, :max, :min, :stddev, :median, :percentile
66
+
67
+ def initialize(type, data)
68
+ @type = type
69
+ @volume = data[:volume].to_i
70
+ @average = (data[:avg].to_f * 100).round
71
+ @max = (data[:max].to_f * 100).round
72
+ @min = (data[:min].to_f * 100).round
73
+ @stddev = (data[:stdDev].to_f * 100).round
74
+ @median = (data[:median].to_f * 100).round
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,112 @@
1
+ module EveApp
2
+ class ItemParser
3
+ attr_reader :text, :lines, :header, :items
4
+
5
+ def initialize(text)
6
+ @text = text
7
+ @lines = []
8
+
9
+ normalized_lines
10
+ determine_items
11
+ end
12
+
13
+ def header?
14
+ !!header
15
+ end
16
+
17
+ def ships
18
+ @_ships ||= items.select(&:ship?)
19
+ end
20
+
21
+ private
22
+
23
+ def normalized_lines
24
+ text.split(/\r?\n/).map(&:strip).map(&:presence).compact.each do |line|
25
+ if Header.matches?(line)
26
+ @header = Header.new(line)
27
+ elsif line.include?(',')
28
+ line.split(',').each do |sline|
29
+ @lines.push(Line.new(sline))
30
+ end
31
+ else
32
+ @lines.push(Line.new(line))
33
+ end
34
+ end
35
+ end
36
+
37
+ def determine_items
38
+ @items = lines.select(&:valid?).group_by(&:type).map do |type, lines|
39
+ OpenStruct.new(type: type, ship?: type.ship?, quantity: lines.map(&:quantity).sum)
40
+ end
41
+ end
42
+
43
+ class Header
44
+ REGEX = /\A\[(.+), (.+)\]\z/
45
+
46
+ attr_reader :text, :type, :name
47
+
48
+ def self.matches?(text)
49
+ text =~ REGEX
50
+ end
51
+
52
+ def initialize(text)
53
+ @text = text
54
+ parse_header
55
+ end
56
+
57
+ private
58
+
59
+ def parse_header
60
+ data = text.scan(REGEX).first
61
+ @type = EveApp::Type.find_by(name: data[0])
62
+ @name = data[1]
63
+ end
64
+ end
65
+
66
+ class Line
67
+ REGEX = /(?<=\s|\t|^)(x?\s?[0-9]+\s?x?)(?=\s|\t|$)/i
68
+
69
+ attr_reader :text, :parts, :type, :quantity, :quantity_words
70
+
71
+ def initialize(text)
72
+ @text = text
73
+ @parts = text.split(/\t/)
74
+ @quantity_words = find_quantity_words
75
+ @type = EveApp::Type.where(name: search_strings).first
76
+ @quantity = find_quantity if type
77
+ end
78
+
79
+ def valid?
80
+ !!type
81
+ end
82
+
83
+ private
84
+
85
+ def find_quantity_words
86
+ matches = text.scan(REGEX)
87
+ if matches.any?
88
+ matches.flatten.compact.map(&:strip)
89
+ else
90
+ []
91
+ end
92
+ end
93
+
94
+ def find_quantity
95
+ return 0 unless type
96
+
97
+ str = text
98
+ str = parts[1] if parts.length > 2 && parts[1] =~ /[0-9]+/
99
+ str = str.gsub(type.name, '')
100
+ (str.strip.gsub(/[^0-9]/, '').presence || 1).to_i
101
+ end
102
+
103
+ def search_strings
104
+ @_search_strings ||= [
105
+ parts.first,
106
+ text.split(',').first,
107
+ quantity_words.map { |w| text.gsub(/\s#{w}\s?/, '') }
108
+ ].flatten.compact.uniq
109
+ end
110
+ end
111
+ end
112
+ end