effective_developer 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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