crhym3-imexport 0.1.2 → 0.1.3
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.
- data/CHANGELOG +9 -2
- data/README.rdoc +18 -5
- data/Rakefile +5 -1
- data/imexport.gemspec +2 -2
- data/lib/imexport.rb +132 -94
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
v0.1.3 (Aprile 29th, 2009)
|
2
|
+
|
3
|
+
* Bugfixing (wasn't saving the last record).
|
4
|
+
* Adding :verbose option (see README.rdoc).
|
5
|
+
* ImExport::import() is now oboslete. Use ImExport::Import.from_file().
|
6
|
+
|
7
|
+
v0.1.2
|
8
|
+
|
9
|
+
* Adding support for models within a namespace (using eval() now with a regexp).
|
3
10
|
|
data/README.rdoc
CHANGED
@@ -153,11 +153,12 @@ So, the complete rake task would look like this:
|
|
153
153
|
Also, you can pass a block to ImExport::import. In that case you'll have to
|
154
154
|
call model.save or model.update_attributes(...) yourself:
|
155
155
|
|
156
|
-
ImExport::
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
156
|
+
ImExport::Import.from_file(ENV['FROM_FILE'], {
|
157
|
+
:class_name => 'Seminar',
|
158
|
+
:find_by => 'title',
|
159
|
+
:db_columns_prefix => 'COLUMN_',
|
160
|
+
:verbose => false,
|
161
|
+
:map => COLUMNS_TO_MODEL_MAP}) do |seminar|
|
161
162
|
|
162
163
|
# do something with seminar object here, e.g.
|
163
164
|
# seminar.save
|
@@ -182,6 +183,14 @@ call model.save or model.update_attributes(...) yourself:
|
|
182
183
|
Column name prefix that should be skipped while looking for the corresponding
|
183
184
|
model attribute name. Againg, considering previous example, +COLUMN_title+
|
184
185
|
actually means +title+ attribute of +Seminar+ model.
|
186
|
+
|
187
|
+
+verbose+::
|
188
|
+
+true+, +false+ or +Proc+.new { |model| }.
|
189
|
+
In cases where model.valid? returns false ImExport might output an error
|
190
|
+
(WARNING) message. +Verbose+ option tells it whether do it or not. In case
|
191
|
+
+verbose+ is a Proc, the latter being passed the model in question and should
|
192
|
+
return +true+ or +false+.
|
193
|
+
Default is +true+.
|
185
194
|
|
186
195
|
+map+::
|
187
196
|
Hash.
|
@@ -211,6 +220,10 @@ call model.save or model.update_attributes(...) yourself:
|
|
211
220
|
|
212
221
|
sudo gem install crhym3-imexport
|
213
222
|
|
223
|
+
If that fails execute the following and try it again.
|
224
|
+
|
225
|
+
gem sources -a http://gems.github.com/
|
226
|
+
|
214
227
|
== License
|
215
228
|
|
216
229
|
Copyright (c) 2009 Alex Vagin, released under the MIT license.
|
data/Rakefile
CHANGED
@@ -2,13 +2,17 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('imexport', '0.1.
|
5
|
+
Echoe.new('imexport', '0.1.3') do |p|
|
6
6
|
p.description = "Simple import from a text file generated by mysql -E ..."
|
7
7
|
p.url = "http://github.com/crhym3/imexport"
|
8
8
|
p.author = "alex"
|
9
9
|
p.email = "alex@digns.com"
|
10
10
|
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
11
|
p.development_dependencies = []
|
12
|
+
|
13
|
+
# github can't sign gems, yet
|
14
|
+
p.certificate_chain = nil
|
15
|
+
p.private_key = nil
|
12
16
|
end
|
13
17
|
|
14
18
|
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/imexport.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{imexport}
|
5
|
-
s.version = "0.1.
|
5
|
+
s.version = "0.1.3"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["alex"]
|
9
|
-
s.date = %q{2009-04-
|
9
|
+
s.date = %q{2009-04-29}
|
10
10
|
s.description = %q{Simple import from a text file generated by mysql -E ...}
|
11
11
|
s.email = %q{alex@digns.com}
|
12
12
|
s.extra_rdoc_files = ["CHANGELOG", "TODO", "lib/imexport.rb", "README.rdoc"]
|
data/lib/imexport.rb
CHANGED
@@ -1,107 +1,145 @@
|
|
1
1
|
module ImExport
|
2
|
-
def self.import(file_name, options
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# instead of save() in that case
|
12
|
-
find_method = "find_by_#{options[:find_by]}"
|
13
|
-
|
14
|
-
## helps to distinguish between real columns and column content
|
15
|
-
# could be something like "COLUMN_"
|
16
|
-
columns_prefix = options[:db_columns_prefix].to_s
|
17
|
-
|
18
|
-
## for each line, this is how we check for a new column or cont.
|
19
|
-
# from the previous line
|
20
|
-
scan_regexp = Regexp.new("^\\s*#{columns_prefix}(\\w+)\\: (.*)")
|
21
|
-
|
22
|
-
## table columns --> model attributes mapping
|
23
|
-
# if an attribute is not specified and present as a column,
|
24
|
-
# it'll try figure it out:
|
25
|
-
# { 'title' => :title } - works w/o expicit mapping
|
26
|
-
attr_map = options[:map] || {}
|
27
|
-
|
28
|
-
## Read the file
|
29
|
-
# we assume it's been created with vertial columns layout (mysql -E ...)
|
30
|
-
IO.foreach(file_name) do |line|
|
31
|
-
## mysql -E ... does this:
|
32
|
-
# ********* 1. row **********
|
33
|
-
# column: value
|
34
|
-
# another_column: value
|
35
|
-
# ********* 2. row **********
|
36
|
-
unless (line =~ /^\*+ \d+\. row \*+$/).nil?
|
37
|
-
## We've got a new row
|
38
|
-
unless block_given?
|
39
|
-
## Save previously created object ...
|
40
|
-
if !@model.nil? and @model.valid?
|
41
|
-
if (s = model.send("#{find_method}", @model.title))
|
42
|
-
s.update_attributes(@model.attributes.reject{|k,v| v.nil?})
|
43
|
-
else
|
44
|
-
@model.save
|
45
|
-
end
|
46
|
-
else
|
47
|
-
$stderr.puts "\n>>> ERRORS while storing #{options[:class_name]}: #{@model.errors.full_messages.join('; ')}\n#{@model.inspect}" unless @model.nil?
|
48
|
-
end
|
49
|
-
else
|
50
|
-
## or pass it to a block
|
51
|
-
# in this case we don't do any validations
|
52
|
-
yield(@model)
|
53
|
-
end
|
54
|
-
|
55
|
-
# ... then create a new one
|
56
|
-
#$stderr.puts "\n###################################"
|
57
|
-
@model = model.new
|
58
|
-
@last_column_name = nil
|
59
|
-
@last_column_content = nil
|
60
|
-
next
|
2
|
+
def self.import(file_name, options)
|
3
|
+
puts "WARNING: ImExport::import() method is obsolete.\nYou should use ImExport::Import.from_file() instead.\n"
|
4
|
+
Import.from_file file_name, options
|
5
|
+
end
|
6
|
+
|
7
|
+
class Import
|
8
|
+
def self.from_file(file_name, options = {})
|
9
|
+
if options[:class_name] =~ /[^a-zA-Z\:]+/
|
10
|
+
raise "#{options[:class_name]} doesn't look like a class name"
|
61
11
|
end
|
62
12
|
|
63
|
-
##
|
64
|
-
|
65
|
-
## Parse it and add set the corresponding attribute in the model
|
66
|
-
line.chomp!
|
67
|
-
column = line.scan(scan_regexp)
|
68
|
-
#$stderr.puts column.inspect
|
13
|
+
## e.g. :seminar => "seminar" => "Seminar" => Seminar
|
14
|
+
model = eval(options[:class_name].to_s.classify)
|
69
15
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
16
|
+
## used to check for an existing record. so we do update_attributes()
|
17
|
+
# instead of save() in that case
|
18
|
+
#
|
19
|
+
# Example:
|
20
|
+
# :find_by => :title # model.find_by_title
|
21
|
+
@@find_by_attribute = options[:find_by].to_s
|
22
|
+
|
23
|
+
## helps to distinguish between real columns and column content
|
24
|
+
# could be something like "COLUMN_"
|
25
|
+
columns_prefix = options[:db_columns_prefix].to_s
|
26
|
+
|
27
|
+
## for each line, this is how we check for a new column or cont.
|
28
|
+
# from the previous line
|
29
|
+
scan_regexp = Regexp.new("^\\s*#{columns_prefix}(\\w+)\\: (.*)")
|
75
30
|
|
76
|
-
##
|
77
|
-
#
|
78
|
-
#
|
79
|
-
|
80
|
-
|
81
|
-
@last_column_content = column[1].gsub(/\t+/, " ").strip.gsub(/^\n+$/, "").gsub(/\n+/, "<br/>")
|
31
|
+
## table columns --> model attributes mapping
|
32
|
+
# if an attribute is not specified and present as a column,
|
33
|
+
# it'll try figure it out:
|
34
|
+
# { 'title' => :title } - works w/o expicit mapping
|
35
|
+
attr_map = options[:map] || {}
|
82
36
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
37
|
+
## verbose option
|
38
|
+
# is a Proc type. Should return true or false.
|
39
|
+
# If true a warning message outputs to stderr in case !model.valid?
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
# verbose => Proc.new { |seminar| seminar.date.future? }
|
43
|
+
#
|
44
|
+
# verbose => nil or not specifying this options at all makes it silent
|
45
|
+
@@verbose = options.include?(:verbose) ? options[:verbose] : true # default is verbose
|
46
|
+
|
47
|
+
## Read the file
|
48
|
+
# we assume it's been created with vertial columns layout (mysql -E ...)
|
49
|
+
model_inst = nil
|
50
|
+
IO.foreach(file_name) do |line|
|
51
|
+
## mysql -E ... does this:
|
52
|
+
# ********* 1. row **********
|
53
|
+
# column: value
|
54
|
+
# another_column: value
|
55
|
+
# ...
|
56
|
+
# ********* 2. row **********
|
57
|
+
unless (line =~ /^\*+ \d+\. row \*+$/).nil?
|
58
|
+
## We've got a new row
|
59
|
+
# save or update previously created object ...
|
60
|
+
unless block_given?
|
61
|
+
save_or_update(model_inst) if model_inst
|
93
62
|
else
|
94
|
-
|
63
|
+
## or pass it to a block
|
64
|
+
# in this case we don't do any validations
|
65
|
+
yield(model_inst)
|
66
|
+
end
|
67
|
+
|
68
|
+
# ... then create a new one
|
69
|
+
#$stderr.puts "\n###################################"
|
70
|
+
model_inst = model.new
|
71
|
+
@last_column_name = nil
|
72
|
+
@last_column_content = nil
|
73
|
+
next
|
74
|
+
end
|
75
|
+
|
76
|
+
## Cont. of the same row.
|
77
|
+
## It's one table column at a time (or more lines).
|
78
|
+
## Parse it and add set the corresponding attribute in the model
|
79
|
+
line.chomp!
|
80
|
+
column = line.scan(scan_regexp)
|
81
|
+
#$stderr.puts column.inspect
|
82
|
+
|
83
|
+
unless column.size > 0
|
84
|
+
## this is a column continuation of the previous line
|
85
|
+
@last_column_content << '<br/>' << line
|
86
|
+
next
|
95
87
|
end
|
96
88
|
|
97
|
-
|
98
|
-
|
99
|
-
|
89
|
+
## this is a new model attribute
|
90
|
+
# set last attribute in the model if defined
|
91
|
+
# we should have at least two items in the array
|
92
|
+
column.flatten!
|
93
|
+
@last_column_name = column.first
|
94
|
+
@last_column_content = column[1].gsub(/\t+/, " ").strip.gsub(/^\n+$/, "").gsub(/\n+/, "<br/>")
|
100
95
|
|
101
|
-
|
96
|
+
# in column-to-model map
|
97
|
+
if attr_map.include?(@last_column_name)
|
98
|
+
mattr = attr_map[@last_column_name]
|
99
|
+
case
|
100
|
+
when mattr.kind_of?(Symbol)
|
101
|
+
model_inst.send("#{mattr.to_s}=", @last_column_content)
|
102
|
+
when mattr.kind_of?(Proc)
|
103
|
+
mattr.call(@last_column_content, model_inst)
|
104
|
+
when mattr.kind_of?(Hash)
|
105
|
+
model_inst.send("#{mattr.keys.first}=", mattr.values.first.call(@last_column_content))
|
106
|
+
else
|
107
|
+
$stderr.puts "WARNING: don't know how to handle #{mattr.inspect}"
|
108
|
+
end
|
109
|
+
|
110
|
+
# simple auto-mapping
|
111
|
+
elsif model_inst.respond_to?("#{@last_column_name}=")
|
112
|
+
model_inst.send("#{@last_column_name}=", @last_column_content)
|
113
|
+
|
114
|
+
# otherwise we don't know how to do it
|
115
|
+
else
|
116
|
+
$stderr.puts "WARNING: don't know how to set :#{@last_column_name}"
|
117
|
+
end
|
118
|
+
end # IO.foreach
|
119
|
+
|
120
|
+
# TODO: more DRY
|
121
|
+
# save the last one
|
122
|
+
unless block_given?
|
123
|
+
save_or_update(model_inst) if model_inst
|
102
124
|
else
|
103
|
-
|
125
|
+
## or pass it to a block
|
126
|
+
# in this case we don't do any validations
|
127
|
+
yield(model_inst)
|
104
128
|
end
|
105
|
-
end
|
106
|
-
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.save_or_update(model)
|
132
|
+
# save it if valid or say an error
|
133
|
+
if !model.nil? and model.valid?
|
134
|
+
if (s = model.class.send("find_by_#{@@find_by_attribute}", model.read_attribute(@@find_by_attribute)))
|
135
|
+
s.update_attributes(model.attributes.reject{|k,v| v.nil?})
|
136
|
+
else
|
137
|
+
model.save
|
138
|
+
end
|
139
|
+
elsif (@@verbose.kind_of?(Proc) ? @@verbose.call(model) : @@verbose)
|
140
|
+
$stderr.puts "\n>>> ERRORS while storing #{model.class.to_s}: #{model.errors.full_messages.join('; ')}\n#{model.inspect}" unless model.nil?
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end # class Import
|
144
|
+
|
107
145
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crhym3-imexport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- alex
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-29 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|