importeur 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2a5b8cd8b68e60849d7f6fc4921b41d4ff39187a
4
+ data.tar.gz: 9a23992b2d88fdad38518abc43057d3d585ea11c
5
+ SHA512:
6
+ metadata.gz: e1e552cb90dc22addb41d5fdaf14cb4774ebdb13952849a46be3f5d20d3d89741078a22498133039f9411e9d3866882b87b1989afedef3291e9c38c7431191ac
7
+ data.tar.gz: 90056d4a61059cffe16bcd2cf2c6198a8485045568e5cfad40f009cdaff358aedc91865325e95343396c5b6b7c55682b86ca60f9736e277b38e8c7e198ed9138
@@ -0,0 +1,70 @@
1
+ # Ruby CircleCI 2.0 configuration file
2
+ #
3
+ # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
+ #
5
+ version: 2
6
+ jobs:
7
+ build:
8
+ docker:
9
+ # specify the version you desire here
10
+ - image: circleci/ruby:2.4.1-node-browsers
11
+ environment:
12
+ DATABASE_URL: 'postgres://importeur@localhost/importeur'
13
+ CAKE_DATA_BUCKET: 'ad2games-cake-data-test-fixtures'
14
+ AWS_REGION: 'eu-west-1'
15
+ AWS_ACCESS_KEY_ID: 'aws-access-key-id'
16
+ AWS_SECRET_ACCESS_KEY: 'aws-secret-access-key'
17
+
18
+ # Specify service dependencies here if necessary
19
+ # CircleCI maintains a library of pre-built images
20
+ # documented at https://circleci.com/docs/2.0/circleci-images/
21
+ - image: circleci/postgres:9.6
22
+ environment:
23
+ POSTGRES_USER: importeur
24
+ POSTGRES_DB: importeur
25
+ POSTGRES_PASSWORD: ''
26
+
27
+ working_directory: ~/repo
28
+
29
+ steps:
30
+ - checkout
31
+
32
+ # Download and cache dependencies
33
+ - restore_cache:
34
+ keys:
35
+ - v1-dependencies-gems
36
+ # fallback to using the latest cache if no exact match is found
37
+ - v1-dependencies-
38
+
39
+ - run:
40
+ name: install dependencies
41
+ command: |
42
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
43
+
44
+ - save_cache:
45
+ paths:
46
+ - ./vendor/bundle
47
+ key: v1-dependencies-gems
48
+
49
+ # Database setup
50
+ - run: bundle exec rake create_test_db
51
+
52
+ # run tests!
53
+ - run:
54
+ name: run tests
55
+ command: |
56
+ mkdir /tmp/test-results
57
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
58
+
59
+ bundle exec rspec --format progress \
60
+ --format RspecJunitFormatter \
61
+ --out /tmp/test-results/rspec.xml \
62
+ --format progress \
63
+ ${TEST_FILES}
64
+
65
+ # collect reports
66
+ - store_test_results:
67
+ path: /tmp/test-results
68
+ - store_artifacts:
69
+ path: /tmp/test-results
70
+ destination: test-results
data/.env ADDED
@@ -0,0 +1,8 @@
1
+ APPNEXUS_USERNAME: 'appnexus-username'
2
+ APPNEXUS_PASSWORD: 'appnexus-password'
3
+ CAKE_DATA_BUCKET: 'ad2games-cake-data-test-fixtures'
4
+ AWS_REGION: 'eu-west-1'
5
+ AWS_ACCESS_KEY_ID: 'aws-access-key-id'
6
+ AWS_SECRET_ACCESS_KEY: 'aws-secret-access-key'
7
+ ROCKETFUEL_API_URI: 'https://api-sandbox.rocketfuel.com/2016/'
8
+ ROCKETFUEL_API_AUTH_TOKEN: 'rocketfuel-api-auth-token'
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ .env.test
12
+
13
+ # rspec failure tracking
14
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.15.4
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0 (2017-10-26)
2
+
3
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in importeur.gemspec
8
+ gemspec
9
+
10
+ group :development, :test do
11
+ gem 'rspec_junit_formatter', '0.2.2'
12
+ gem 'rubocop-ci', github: 'ad2games/rubocop-ci'
13
+ end
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # Importeur
2
+
3
+ A universal data import tool (a.k.a. ETL) 🙌
4
+
5
+ For now it's a single import job, made more composable and put into its own
6
+ place to be used in other projects. The vision is to add more things, so it can
7
+ handle more data sources and targets, eventually.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'importeur'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install importeur
24
+
25
+ ## Usage
26
+
27
+ The center of it all is the `ETL` class. By inistantiating it with different
28
+ extractors, transformers and loaders, it can be whatever you want it to be:
29
+
30
+ ```ruby
31
+ Importeur::ETL.new(
32
+ extractor: my_extractor,
33
+ transformer: my_transformer,
34
+ loader: my_loader
35
+ )
36
+ ```
37
+
38
+ All three only need to have a `call` method and, hence, can be simple Procs.
39
+ The extractor should return something enumerable, which is then iterated over
40
+ and each item passed into the transformer. The loader receives the enumerable
41
+ result of that.
42
+
43
+ So far a more or less generic `Extractor` exists.
44
+
45
+ ```ruby
46
+ Importeur::Extractor.new(
47
+ source,
48
+ cursor,
49
+ cursor_key
50
+ )
51
+ ```
52
+
53
+ It takes as arguments a data source that needs to implement
54
+
55
+ * An `items` method, returning anything enumerable, as well as a
56
+ `dataset_unique_id`, returning a unique ID for the current version of the
57
+ dataset
58
+ * A cursor, that needs to implement `read(key)` and `write(key, value)` (e.g.
59
+ `Rails.cache`) it makes the extractor return `nil` if there is no new data.
60
+ * A key (string), to be passed into the cursor.
61
+
62
+ An `ActiveRecordPostgresLoader` exists, that has a few very specific
63
+ dependencies. As the name suggests, it imports data into a Postgres database
64
+ using `ActiveRecord`. Additionally, the model used, needs to use
65
+ `acts_as_paranoid`.
66
+
67
+ Example use-cases can be found in the `spec/integration` directory.
68
+
69
+ ## Development
70
+
71
+ In order to be able to run the tests, a Postgres database is needed. Having
72
+ a PostgreSQL user with CREATEDB permission, create `.env.test` file with your
73
+ DATABASE_URL configuration, for example:
74
+
75
+ `DATABASE_URL: 'postgres://user:password@localhost:5432/importeur_test'`
76
+
77
+ Then run `bundle exec rake create_test_db` to create the database and
78
+ `bundle exec rspec` to run tests.
79
+
80
+ We run `rake rubocop` to make sure, everything looks good.
81
+
82
+ ## Contributing
83
+
84
+ Bug reports and pull requests are welcome on GitHub at
85
+ https://github.com/ad2games/importeur.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop-ci'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ load 'lib/task/db.rake'
10
+
11
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "importeur"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/importeur.gemspec ADDED
@@ -0,0 +1,47 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'importeur/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'importeur'
10
+ spec.version = Importeur::VERSION
11
+ spec.authors = ['Helge Rausch']
12
+ spec.email = ['helge@rausch.io']
13
+
14
+ spec.summary = 'Universal data importer'
15
+ spec.description = 'Universal data importer'
16
+ spec.homepage = 'https://ad2games.com/'
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
22
+ else
23
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
24
+ 'public gem pushes.'
25
+ end
26
+
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ f.match(%r{^(test|spec|features)/})
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ spec.add_development_dependency 'bundler', '~> 1.15'
35
+ spec.add_development_dependency 'rake', '~> 10.0'
36
+ spec.add_development_dependency 'rspec', '~> 3.0'
37
+ spec.add_development_dependency 'activerecord', '<5'
38
+ spec.add_development_dependency 'pry'
39
+ spec.add_development_dependency 'vcr'
40
+ spec.add_development_dependency 'webmock'
41
+ spec.add_development_dependency 'bucket_cake'
42
+ spec.add_development_dependency 'pg'
43
+ spec.add_development_dependency 'database_cleaner'
44
+ spec.add_development_dependency 'appnexusapi'
45
+ spec.add_development_dependency 'rocketfuel_api'
46
+ spec.add_development_dependency 'dotenv'
47
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Importeur
4
+ class ActiveRecordPostgresLoader
5
+ BATCH_SIZE = 500
6
+
7
+ def initialize(model, primary_key)
8
+ @model = model
9
+ @primary_key = primary_key
10
+ end
11
+
12
+ def call(data)
13
+ imported_ids = []
14
+ seen = Set.new
15
+ data.each_slice(BATCH_SIZE) do |batch|
16
+ batch_ids = batch.map { |attrs| attrs[primary_key] }
17
+ imported_ids.concat(batch_ids)
18
+ records = records_for_batch(batch_ids)
19
+ store_batch(batch, records, seen)
20
+ end
21
+ delete_old_records(imported_ids)
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :model, :primary_key
27
+
28
+ def store_batch(batch, records, seen)
29
+ batch.each do |attrs|
30
+ next unless seen.add?(attrs[primary_key])
31
+ store_record(attrs, records)
32
+ end
33
+ end
34
+
35
+ def store_record(attrs, records)
36
+ record = records.fetch(attrs[primary_key], model.new)
37
+ record.assign_attributes(attrs)
38
+ record.deleted_at = nil
39
+ return unless record.changed?
40
+ record.imported_at = Time.now
41
+ record.save!
42
+ end
43
+
44
+ def records_for_batch(batch_ids)
45
+ model
46
+ .with_deleted
47
+ .where(primary_key => batch_ids)
48
+ .index_by(&primary_key)
49
+ end
50
+
51
+ def delete_old_records(imported_ids)
52
+ # Basically `self.class.model.where.not(primary_key => imported_ids)`, but
53
+ # more efficient in this case.
54
+ model
55
+ .joins(<<-SQL)
56
+ LEFT JOIN (SELECT unnest(ARRAY[#{imported_ids.join(',')}]::int[]) AS primary_key) AS imported
57
+ ON imported.primary_key = #{model.table_name}.#{primary_key}
58
+ SQL
59
+ .where('imported.primary_key' => nil)
60
+ .delete_all
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Importeur
4
+ module DataSources
5
+ class Appnexus
6
+ def initialize(appnexus_service)
7
+ @appnexus_service = appnexus_service
8
+ end
9
+
10
+ def dataset_unique_id
11
+ md5 = Digest::MD5.new
12
+ items.each { |item| md5.update(item.to_s) }
13
+ md5.hexdigest
14
+ end
15
+
16
+ def items
17
+ @items ||= appnexus_service.get_all
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :appnexus_service
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Importeur
4
+ module DataSources
5
+ class Combined
6
+ def initialize(*entity_classes)
7
+ @entity_classes = entity_classes
8
+ end
9
+
10
+ def dataset_unique_id
11
+ data_sources.map(&:dataset_unique_id).join
12
+ end
13
+
14
+ def items
15
+ Enumerator.new do |y|
16
+ data_sources.each do |data_source|
17
+ data_source.items.each do |entity|
18
+ y << entity
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :entity_classes
27
+
28
+ def data_sources
29
+ @data_sources ||= entity_classes.map(&:new)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Importeur
4
+ module DataSources
5
+ class Rocketfuel
6
+ def initialize(rocketfuel_service, params = {})
7
+ @rocketfuel_service = rocketfuel_service
8
+ @params = params
9
+ end
10
+
11
+ def dataset_unique_id
12
+ md5 = Digest::MD5.new
13
+ items.each { |item| md5.update(item.to_s) }
14
+ md5.hexdigest
15
+ end
16
+
17
+ def items
18
+ @items ||= rocketfuel_service.get_all(params)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :rocketfuel_service, :params
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Importeur
4
+ class ETL
5
+ def initialize(extractor:, transformer:, loader:)
6
+ @extractor = extractor
7
+ @transformer = transformer
8
+ @loader = loader
9
+ end
10
+
11
+ def call
12
+ extracted = extractor.call
13
+ return if extracted.nil?
14
+ transformed = extracted.lazy.flat_map do |entity|
15
+ transformer.call(entity)
16
+ end
17
+ loader.call(transformed.lazy)
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :extractor, :transformer, :loader
23
+ end
24
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Importeur
4
+ class Extractor
5
+ def initialize(data_source, cursor, cursor_key)
6
+ @data_source = data_source
7
+ @cursor = cursor
8
+ @cursor_key = cursor_key
9
+ end
10
+
11
+ def call
12
+ return unless has_new_data?
13
+ Enumerator.new do |y|
14
+ feed.each do |item|
15
+ y << item
16
+ end
17
+ store_new_dataset_id
18
+ clear_current_dataset_id
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :current_dataset_id, :cursor, :cursor_key, :data_source
25
+
26
+ def has_new_data?
27
+ last_known_id = cursor.read(cursor_key)
28
+ current_dataset_id != last_known_id
29
+ end
30
+
31
+ def store_new_dataset_id
32
+ cursor.write(cursor_key, current_dataset_id)
33
+ end
34
+
35
+ def clear_current_dataset_id
36
+ @current_dataset_id = nil
37
+ end
38
+
39
+ def current_dataset_id
40
+ @current_dataset_id ||= dataset_id
41
+ end
42
+
43
+ def dataset_id
44
+ data_source.dataset_unique_id
45
+ end
46
+
47
+ def feed
48
+ @feed ||= data_source.items
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Importeur
4
+ VERSION = '0.1.0'
5
+ end
data/lib/importeur.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'importeur/version'
4
+ require 'importeur/etl'
5
+ require 'importeur/extractor'
6
+ require 'importeur/active_record_postgres_loader'
7
+ require 'importeur/data_sources/combined'
8
+ require 'importeur/data_sources/appnexus'
9
+ require 'importeur/data_sources/rocketfuel'
10
+
11
+ module Importeur
12
+ def self.root
13
+ Pathname.new(File.dirname(__dir__))
14
+ end
15
+ end
data/lib/task/db.rake ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dotenv'
4
+ Dotenv.load('.env.test')
5
+
6
+ require 'active_record'
7
+
8
+ desc 'Create a database for testing'
9
+ task :create_test_db do
10
+ database_configuration =
11
+ ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(
12
+ ENV.fetch('DATABASE_URL')
13
+ ).to_hash
14
+
15
+ ActiveRecord::Tasks::DatabaseTasks.drop(database_configuration)
16
+ ActiveRecord::Tasks::DatabaseTasks.create(database_configuration)
17
+
18
+ ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL'))
19
+ ActiveRecord::Migration.verbose = false
20
+ ActiveRecord::Schema.define(version: 1) do
21
+ drop_table :affiliates if table_exists?(:affiliates)
22
+ create_table :affiliates do |t|
23
+ t.string :name
24
+ t.datetime :deleted_at
25
+ t.datetime :imported_at
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,247 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: importeur
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Helge Rausch
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-10-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "<"
67
+ - !ruby/object:Gem::Version
68
+ version: '5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: vcr
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: bucket_cake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pg
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: database_cleaner
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: appnexusapi
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rocketfuel_api
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: dotenv
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ description: Universal data importer
196
+ email:
197
+ - helge@rausch.io
198
+ executables: []
199
+ extensions: []
200
+ extra_rdoc_files: []
201
+ files:
202
+ - ".circleci/config.yml"
203
+ - ".env"
204
+ - ".gitignore"
205
+ - ".rspec"
206
+ - ".travis.yml"
207
+ - CHANGELOG.md
208
+ - Gemfile
209
+ - README.md
210
+ - Rakefile
211
+ - bin/console
212
+ - bin/setup
213
+ - importeur.gemspec
214
+ - lib/importeur.rb
215
+ - lib/importeur/active_record_postgres_loader.rb
216
+ - lib/importeur/data_sources/appnexus.rb
217
+ - lib/importeur/data_sources/combined.rb
218
+ - lib/importeur/data_sources/rocketfuel.rb
219
+ - lib/importeur/etl.rb
220
+ - lib/importeur/extractor.rb
221
+ - lib/importeur/version.rb
222
+ - lib/task/db.rake
223
+ homepage: https://ad2games.com/
224
+ licenses: []
225
+ metadata:
226
+ allowed_push_host: https://rubygems.org
227
+ post_install_message:
228
+ rdoc_options: []
229
+ require_paths:
230
+ - lib
231
+ required_ruby_version: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ version: '0'
236
+ required_rubygems_version: !ruby/object:Gem::Requirement
237
+ requirements:
238
+ - - ">="
239
+ - !ruby/object:Gem::Version
240
+ version: '0'
241
+ requirements: []
242
+ rubyforge_project:
243
+ rubygems_version: 2.6.13
244
+ signing_key:
245
+ specification_version: 4
246
+ summary: Universal data importer
247
+ test_files: []