effective_developer 0.0.6 → 0.0.7

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: 321b83d7d7cf7a803e2516db52bb1610b32dd8a0
4
- data.tar.gz: 9f64632100cb5199d5b76bb4a6a57f8764ce64a3
3
+ metadata.gz: 6afcc37e01e11a752f1fa16ea168e739886571a5
4
+ data.tar.gz: 1f79a862bbaeaec604384015649860c8fc3ca53d
5
5
  SHA512:
6
- metadata.gz: 1d8b4e802b1e9b8c8bbc7bc78dc9aa12cf8518a9d1a35a859a86289a1417852c1f14f95cec6ce609a57b544e2838b34e2b31f4c3329052ac396aa642903bb3a3
7
- data.tar.gz: 7e7d7fba5828905f6b0663817424ba74a3532e857f84b8752e11cd37c1fb96bd62e5d0d218ab63bccbd883fde23dfbbba8995107b1f38206ecde74be2c9670c0
6
+ metadata.gz: 90ce51e217dab774391c2f2e697951a594383738b61b1c1473291804daadbb9dc88409932925faeb000e8abea7248a5a224e7e0bb8f543cfeea564c9e6be2deb
7
+ data.tar.gz: ce87d2c69e4844d62226ada1b014698269121d3db47265d0170e202ccba8ca168e2d77b4c18272357b01858bd3bdb773e89a180a9df0ba41f3ccc245b7ae60bd
data/README.md CHANGED
@@ -18,7 +18,7 @@ Run the bundle command to install it:
18
18
  bundle install
19
19
  ```
20
20
 
21
- To use the included command line scripts in any directory, clone this repo:
21
+ To use the included command line shell scripts in any directory, clone this repo:
22
22
 
23
23
  ```console
24
24
  git clone git@github.com:code-and-effect/effective_developer.git
@@ -70,7 +70,37 @@ A command line script that calls [BFG Repo-Cleaner](https://rtyley.github.io/bfg
70
70
 
71
71
  # Rake scripts
72
72
 
73
- ## Reset Pk Sequence
73
+ ## csv:export
74
+
75
+ Exports all database tables to individual .csv files.
76
+
77
+ ```ruby
78
+ rake export:csv
79
+ ```
80
+
81
+ ## csv:import::foos
82
+
83
+ Where table is the name of a model. Dynamically created rake task when a `/lib/csv_importers/foos.rb` file is present.
84
+
85
+ ```ruby
86
+ rake csv:import:foos
87
+ ```
88
+
89
+ ## csv:import::scaffold
90
+
91
+ Scaffolds an `Effective::CSVImporter` file for each .csv file in `/lib/csv_importers/data/*.csv`
92
+
93
+ ```ruby
94
+ rake csv:import:scaffold
95
+ ```
96
+
97
+ or
98
+
99
+ ```ruby
100
+ rake csv:import:scaffold[users]
101
+ ```
102
+
103
+ ## reset_pk_sequence
74
104
 
75
105
  If you ever run into the error `duplicate key violates unique constraint (id) error`, run this script:
76
106
 
@@ -2,22 +2,33 @@ require 'csv'
2
2
 
3
3
  module Effective
4
4
  class CSVImporter
5
- attr_reader :current_row, :last_row
5
+ attr_reader :current_row, :last_row, :csv_file
6
6
 
7
7
  A=0;B=1;C=2;D=3;E=4;F=5;G=6;H=7;I=8;J=9;K=10;L=11;M=12;N=13;
8
8
  O=14;P=15;Q=16;R=17;S=18;T=19;U=20;V=21;W=22;X=23;Y=24;Z=25;
9
9
  AA=26;AB=27;AC=28;AD=29;AE=30;AF=31;AG=32;AH=33;AI=34;AJ=35;
10
10
  AK=36;AL=37;AM=38;AN=39;AO=40;AP=41;AQ=42;AR=43;AS=44;AT=45;
11
11
 
12
- def initialize(csv_file, has_header_row = true)
13
- @csv_file = csv_file.to_s
14
- @has_header_row = has_header_row
12
+ def initialize(csv_file = default_csv_file(), header: true)
13
+ @has_header_row = header
14
+
15
+ @csv_file = csv_file
16
+ raise "#{@csv_file} does not exist" unless File.exists?(@csv_file)
15
17
  end
16
18
 
17
- def csv_file
18
- @csv_file
19
+ def columns
20
+ raise "Please define a method 'def columns' returning a Hash of {id: A, name: B}"
19
21
  end
20
22
 
23
+ def process_row
24
+ raise "Please define a method 'def process_row' to process your row"
25
+ end
26
+
27
+ # Override me if you need some before/after hooks
28
+ def before_import ; end
29
+ def after_import ; end
30
+
31
+ # This runs through each row and calls process_row() on it
21
32
  def import!
22
33
  log "Importing #{csv_file.split('/').last.sub('.csv', '')}"
23
34
 
@@ -30,37 +41,39 @@ module Effective
30
41
  log "Import complete (#{@errors_count} errors in #{@has_header_row ? @current_row_number-1 : @current_row_number} rows)"
31
42
  end
32
43
 
33
- def with_each_row(&block)
34
- @current_row_number = (@has_header_row ? 2 : 1)
44
+ # Returns an Array of Arrays, with each row run through normalize
45
+ def rows
46
+ @rows ||= [].tap do |rows|
47
+ CSV.foreach(csv_file, headers: @has_header_row) do |row|
48
+ rows << columns.map { |column, index| normalize(column, row[index].try(:strip).presence) }
49
+ end
50
+ end
51
+ end
35
52
 
36
- CSV.foreach(csv_file, headers: @has_header_row) do |row|
37
- @current_row = row
53
+ # UserStudentInfosImporter.new().where(id: 3, title: 'thing')
54
+ # Returns an Array of Hashes, representing any row that matches the selector
55
+ def where(attributes)
56
+ raise 'expected a Hash of attributes' unless attributes.kind_of?(Hash)
57
+ attributes.each { |column, _| raise "unknown column :#{column}" unless columns.key?(column) }
38
58
 
39
- begin
40
- yield
41
- print colorize('.', :green)
42
- rescue => e
43
- error(e.message)
44
- puts row
45
- puts e.backtrace.first(3)
59
+ rows.map do |row|
60
+ if attributes.all? { |column, value| row[columns[column]] == value }
61
+ columns.inject({}) { |retval, (column, index)| retval[column] = row[index]; retval }
46
62
  end
47
-
48
- @current_row_number += 1
49
- @last_row = row
50
- end
63
+ end.compact
51
64
  end
52
65
 
53
- def columns
54
- raise "Please define a method 'def columns' returning a Hash of {id: A, name: B}"
66
+ def where!(attributes)
67
+ where(attributes).presence || raise("csv row with #{attributes} not found")
55
68
  end
56
69
 
57
- def process_row
58
- raise "Please define a method 'def process_row' to process your row"
70
+ def find(attributes)
71
+ where(attributes).first
59
72
  end
60
73
 
61
- # Override me if you need some before/after hooks
62
- def before_import ; end
63
- def after_import ; end
74
+ def find!(attributes)
75
+ find(attributes).presence || raise("csv row with #{attributes} not found")
76
+ end
64
77
 
65
78
  # Normalize the value based on column name
66
79
  def normalize(column, value)
@@ -73,11 +86,46 @@ module Effective
73
86
  parse_datetime(column, value)
74
87
  elsif column.ends_with?('_on') # Date
75
88
  parse_datetime(column, value).beginning_of_day
89
+ elsif column.ends_with?('_to_i')
90
+ value.to_i
91
+ elsif column.ends_with?('_to_f')
92
+ value.to_f
93
+ elsif column.ends_with?('_to_s')
94
+ value.to_s
95
+ elsif column.ends_with?('_to_a')
96
+ if ['[]', '{}'].include?(value)
97
+ []
98
+ elsif value.starts_with?('{') && value.ends_with?('}')
99
+ YAML::load(value).keys.select { |str| str.to_s.present? }
100
+ else
101
+ YAML::load(value).to_a.select { |str| str.to_s.present? }
102
+ end
103
+ elsif column == 'id' || column.ends_with?('_id')
104
+ value.present? ? value.to_i : nil
76
105
  else
77
- value.presence || ''.freeze
106
+ value.presence
78
107
  end
79
108
  end
80
109
 
110
+ # Takes an object and loops through all columns assigning the current row values
111
+ def assign_columns(obj, only: [], except: [])
112
+ assigns = (
113
+ if only.present?
114
+ only = Array(only)
115
+ columns.keep_if { |key, _| only.include?(key) }
116
+ elsif except.present?
117
+ except = Array(except)
118
+ columns.delete_if { |key, _| except.include?(key) }
119
+ end
120
+ )
121
+
122
+ (assigns || columns).each do |column, _|
123
+ obj.send("#{column}=", col(column)) if obj.respond_to?(column)
124
+ end
125
+
126
+ obj
127
+ end
128
+
81
129
  def log(message)
82
130
  puts "\n#{message}";
83
131
  end
@@ -93,6 +141,26 @@ module Effective
93
141
 
94
142
  protected
95
143
 
144
+ def with_each_row(&block)
145
+ @current_row_number = (@has_header_row ? 2 : 1)
146
+
147
+ CSV.foreach(csv_file, headers: @has_header_row) do |row|
148
+ @current_row = row
149
+
150
+ begin
151
+ yield
152
+ print colorize('.', :green)
153
+ rescue => e
154
+ error(e.message)
155
+ puts row
156
+ puts e.backtrace.first(3)
157
+ end
158
+
159
+ @current_row_number += 1
160
+ @last_row = row
161
+ end
162
+ end
163
+
96
164
  def col(column)
97
165
  raise "unknown column :#{column} passed to col()" unless columns.key?(column) || column.kind_of?(Integer)
98
166
 
@@ -147,5 +215,11 @@ module Effective
147
215
  end
148
216
  end
149
217
 
218
+ private
219
+
220
+ def default_csv_file
221
+ ("lib/csv_importers/data/#{self.class.name.gsub('CsvImporters::', '').underscore.gsub('_importer', '')}.csv")
222
+ end
223
+
150
224
  end
151
225
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveDeveloper
2
- VERSION = '0.0.6'.freeze
2
+ VERSION = '0.0.7'.freeze
3
3
  end
@@ -0,0 +1,13 @@
1
+ module CsvImporters
2
+ class <%= klass %>Importer < Effective::CSVImporter
3
+ def columns
4
+ {<% columns.each_with_index do |column, index| %>
5
+ <%= column %>: <%= (letters[index] || index) %><%= ',' unless (index+1) == columns.length %><% end %>
6
+ }
7
+ end
8
+
9
+ def process_row
10
+ raise 'todo'
11
+ end
12
+ end
13
+ end
@@ -1,6 +1,6 @@
1
1
  # effective_csv_importer 1.0
2
2
 
3
- # Creates one rake task per importer model, as well as a `rake import:all` task.
3
+ # Creates one rake task per importer model, as well as a `rake csv:import:all` task.
4
4
 
5
5
  # Usage:
6
6
 
@@ -8,41 +8,92 @@
8
8
  # Put your csv data in /lib/csv_importers/data/posts.csv
9
9
  # Both filenames should be pluralized
10
10
 
11
- # rake import:posts (one task created per model)
12
- # rake import:all
11
+ # rake csv:import:posts (one task created per model)
12
+ # rake csv:import:all
13
+ # rake csv:import:scaffold
14
+ # rake csv:import:scaffold[users]
15
+ # rake csv:export
13
16
 
14
- namespace :import do
15
- Dir['lib/csv_importers/*.rb'].each do |file|
16
- importer = file.sub('lib/csv_importers/', '').sub('_importer.rb', '')
17
- csv_file = "lib/csv_importers/data/#{importer}.csv"
18
- next unless File.exists?(csv_file)
17
+ namespace :csv do
18
+ namespace :import do
19
+ # Create a rake task to import each csv file
20
+ Dir['lib/csv_importers/*.rb'].each do |file|
21
+ importer = file.sub('lib/csv_importers/', '').sub('_importer.rb', '')
22
+ csv_file = "lib/csv_importers/data/#{importer}.csv"
23
+ next unless File.exists?(csv_file)
19
24
 
20
- # Create a rake task to import this file
21
- desc "Import #{importer} from #{csv_file}"
25
+ # rake csv:import:foo
26
+ desc "Import #{importer} from #{csv_file}"
22
27
 
23
- task importer => :environment do
24
- require "#{Rails.application.root}/#{file}"
28
+ task importer => :environment do
29
+ require "#{Rails.application.root}/#{file}"
25
30
 
26
- klass = "CsvImporters::#{importer.classify.pluralize}Importer".safe_constantize
27
- raise "unable to constantize CsvImporters::#{importer.classify.pluralize}Importer for #{file}" unless klass
31
+ klass = "CsvImporters::#{importer.classify.pluralize}Importer".safe_constantize
32
+ raise "unable to constantize CsvImporters::#{importer.classify.pluralize}Importer for #{file}" unless klass
28
33
 
29
- klass.new(csv_file).import!
34
+ klass.new().import!
35
+ end
36
+ end
37
+
38
+ # rake csv:import:all
39
+ desc 'Import all from /lib/csv_importers/*.rb'
40
+
41
+ task :all => :environment do
42
+ Dir['lib/csv_importers/*.rb'].each do |file|
43
+ importer = file.sub('lib/csv_importers/', '').sub('_importer.rb', '')
44
+ csv_file = "lib/csv_importers/data/#{importer}.csv"
45
+ next unless File.exists?(csv_file)
46
+
47
+ Rake::Task["csv:import:#{importer}"].invoke
48
+ end
49
+ end
50
+
51
+ # rake csv:scaffold
52
+ # rake csv:scaffold[users]
53
+ desc 'Scaffold an Effective::CSVImporter for each /lib/csv_importers/data/*.csv file'
54
+
55
+ task :scaffold, [:file_name] => :environment do |t, args|
56
+ args.with_defaults(file_name: 'all')
57
+
58
+ require 'csv'
59
+
60
+ generator = ERB.new(File.read(File.dirname(__FILE__) + '/../generators/effective_developer/csv_importer.rb.erb'))
61
+ letters = ('A'..'AT').to_a
62
+
63
+ Dir['lib/csv_importers/data/*.csv'].each do |file|
64
+ csv_file = file.split('/').last.gsub('.csv', '')
65
+
66
+ next if (Array(args.file_name) != ['all'] && Array(args.file_name).include?(csv_file) == false)
67
+
68
+ klass = csv_file.classify.pluralize
69
+ columns = CSV.open(file, 'r') { |csv| csv.first }
70
+
71
+ File.open("#{Rails.root}/lib/csv_importers/#{csv_file}_importer.rb", 'w') do |file|
72
+ file.write generator.result(binding)
73
+ end
74
+ end
30
75
  end
31
76
  end
32
- end
33
77
 
34
- # This task is kind of naive, because some imports could be order dependent. Use at your own risk.
35
- namespace :import do
36
- desc "Import all from /lib/csv_importers/*.rb"
78
+ # rake csv:export
79
+ desc 'Export all database tables to /tmp/csv_exports/*.csv'
37
80
 
38
- task :all => :environment do
39
- Dir['lib/csv_importers/*.rb'].each do |file|
40
- importer = file.sub('lib/csv_importers/', '').sub('_importer.rb', '')
41
- csv_file = "lib/csv_importers/data/#{importer}.csv"
42
- next unless File.exists?(csv_file)
81
+ task :export => :environment do
82
+ require 'csv'
43
83
 
44
- Rake::Task["import:#{importer}"].invoke
84
+ path = Rails.root.to_s + '/tmp/csv_exports/'
85
+ FileUtils.mkdir_p(path) unless File.directory?(path)
86
+
87
+ (ActiveRecord::Base.connection.tables - ['schema_migrations']).each do |table|
88
+ records = ActiveRecord::Base.connection.exec_query("SELECT * FROM #{table} ORDER BY id")
89
+
90
+ CSV.open(path + "#{table}.csv", 'wb') do |csv|
91
+ csv << records.columns
92
+ records.rows.each { |row| csv << row }
93
+ end
45
94
  end
95
+
96
+ puts "Successfully csv exported #{ActiveRecord::Base.connection.tables.length} tables to #{path}"
46
97
  end
47
98
 
48
99
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_developer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-28 00:00:00.000000000 Z
11
+ date: 2016-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -40,6 +40,7 @@ files:
40
40
  - lib/effective_developer.rb
41
41
  - lib/effective_developer/engine.rb
42
42
  - lib/effective_developer/version.rb
43
+ - lib/generators/effective_developer/csv_importer.rb.erb
43
44
  - lib/generators/effective_developer/install_generator.rb
44
45
  - lib/tasks/effective_csv_importer.rake
45
46
  - lib/tasks/pg_pull.rake