map_fields 1.0.4 → 2.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +23 -30
- data/lib/map_fields/controller.rb +15 -0
- data/lib/map_fields/mapper.rb +108 -0
- data/lib/map_fields/mapping.rb +26 -0
- data/lib/map_fields/params_parser.rb +39 -0
- data/lib/map_fields/railtie.rb +5 -0
- data/lib/map_fields.rb +202 -201
- data/spec/spec_helper.rb +9 -41
- metadata +28 -44
- data/.autotest +0 -2
- data/Rakefile +0 -16
- data/VERSION +0 -1
- data/announcement.txt +0 -12
- data/init.rb +0 -1
- data/spec/application_controller.rb +0 -2
- data/spec/controllers/test_controller_spec.rb +0 -56
- data/spec/lib/params_parser_spec.rb +0 -99
- data/spec/test-file.csv +0 -3
- data/views/map_fields/_map_fields.erb +0 -31
data/README.rdoc
CHANGED
@@ -4,48 +4,48 @@
|
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
A Rails
|
7
|
+
A Rails gem which provides a hook to preview and map the fields of an uploaded CSV file to a pre-defined schema
|
8
8
|
|
9
9
|
== FEATURES/PROBLEMS:
|
10
10
|
|
11
11
|
* Captures the post and provides an intermediate view where a user can map their data to the expected schema
|
12
12
|
* Provides a default mapping view that can be customised
|
13
13
|
* Allows the import to be part of a larger form (The form fields are remembered through the mapping)
|
14
|
+
* Can only handle a single uploaded file at a time
|
14
15
|
|
15
|
-
==
|
16
|
+
== INSTALLATION:
|
16
17
|
|
17
|
-
|
18
|
-
class ListsController < AppliactionController
|
19
|
-
map_fields :create, ['Title', 'First name', 'Last name'], :file_field => :file, :params => [:list]
|
18
|
+
gem install map_fields
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
== EXAMPLE:
|
21
|
+
|
22
|
+
===TestController
|
23
|
+
class TestController < AppliactionController
|
24
24
|
|
25
25
|
def new
|
26
26
|
@list = List.new
|
27
27
|
end
|
28
28
|
|
29
29
|
def create
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@list.contact.create(:title => row[
|
34
|
-
:first_name => row[
|
35
|
-
:last_name => row[
|
30
|
+
map_fields :get_fields, params[:file] do
|
31
|
+
@list = List.create(params[:list])
|
32
|
+
@mapping.rows.each do |row|
|
33
|
+
@list.contact.create(:title => row[:title],
|
34
|
+
:first_name => row[:first_name],
|
35
|
+
:last_name => row[:last_name])
|
36
36
|
end
|
37
37
|
flash[:notice] = 'Contact list created'
|
38
38
|
redirect_to :action => :index
|
39
|
-
else
|
40
|
-
render
|
41
39
|
end
|
42
|
-
rescue MapFields::
|
43
|
-
flash[:error] = 'Please try again'
|
44
|
-
redirect_to :action => :new
|
45
|
-
rescue MapFields::MissingFileContentsError
|
40
|
+
rescue MapFields::MissingFileError
|
46
41
|
flash[:error] = 'Please upload a file'
|
47
42
|
redirect_to :action => :new
|
48
43
|
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def get_fields
|
47
|
+
['Title', 'First name', 'Last name']
|
48
|
+
end
|
49
49
|
end
|
50
50
|
|
51
51
|
===New view (new.html.erb)
|
@@ -67,19 +67,12 @@ A Rails plugin which provides a hook to preview and map the fields of an uploade
|
|
67
67
|
===Create view (create.html.erb)
|
68
68
|
<h1>Import a new List</h1>
|
69
69
|
<p>Please map the details you're importing</p>
|
70
|
-
=render :partial => 'map_fields/
|
70
|
+
=render :partial => 'map_fields/mapping'
|
71
71
|
|
72
72
|
== REQUIREMENTS:
|
73
73
|
|
74
|
-
*
|
75
|
-
|
76
|
-
== INSTALL:
|
77
|
-
|
78
|
-
sudo gem install map-fields
|
79
|
-
|
80
|
-
or
|
81
|
-
|
82
|
-
./script/plugin install git://github.com/internuity/map-fields.git
|
74
|
+
* Rails 3.1
|
75
|
+
* Ruby 1.9.3 (Not tested on 1.9.1 or 1.9.2
|
83
76
|
|
84
77
|
== LICENSE:
|
85
78
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'map_fields/mapper'
|
2
|
+
|
3
|
+
module MapFields
|
4
|
+
module Controller
|
5
|
+
def map_fields(fields, file, &block)
|
6
|
+
@mapper = Mapper.new(self, fields, file)
|
7
|
+
if @mapper.mapped?
|
8
|
+
block.call
|
9
|
+
else
|
10
|
+
append_view_path File.expand_path('../../../views/', __FILE__)
|
11
|
+
render
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'map_fields/mapping'
|
4
|
+
require 'map_fields/params_parser'
|
5
|
+
|
6
|
+
module MapFields
|
7
|
+
class MissingFileError < StandardError; end
|
8
|
+
|
9
|
+
class Mapper
|
10
|
+
def initialize(controller, fields, file)
|
11
|
+
params = controller.params
|
12
|
+
@fields = get_fields(controller, fields)
|
13
|
+
@params = ParamsParser.parse(params)
|
14
|
+
|
15
|
+
if file
|
16
|
+
file = save_file controller, file
|
17
|
+
@rows = parse_first_few_lines file
|
18
|
+
else
|
19
|
+
raise MissingFileError unless controller.session[:map_fields_file] && File.exist?(controller.session[:map_fields_file])
|
20
|
+
@mapped = true
|
21
|
+
@rows = map_fields(controller, params.delete(:mapped_fields), fields)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
attr_reader :rows, :fields, :params
|
25
|
+
|
26
|
+
def mapped?
|
27
|
+
@mapped
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def parse_params(params)
|
32
|
+
params = params.except(:controller, :action)
|
33
|
+
end
|
34
|
+
|
35
|
+
def map_fields(controller, mapped_fields, fields)
|
36
|
+
ignore_first_row = mapped_fields.delete(:ignore_first_row)
|
37
|
+
mapping = Mapping.new(mapped_fields, fields)
|
38
|
+
CSVReader.new(controller.session[:map_fields_file], mapping, ignore_first_row)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_fields(controller, fields)
|
42
|
+
if fields.is_a?(Symbol)
|
43
|
+
controller.send(fields)
|
44
|
+
elsif fields.respond_to?(:call)
|
45
|
+
fields.call
|
46
|
+
else
|
47
|
+
fields
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def save_file(controller, file)
|
52
|
+
Tempfile.open(['map_fields', '.csv']) do |tmpfile|
|
53
|
+
tmpfile.write file.read
|
54
|
+
controller.session[:map_fields_file] = tmpfile.path
|
55
|
+
tmpfile.path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_first_few_lines(file)
|
60
|
+
rows = []
|
61
|
+
rowcount = 0
|
62
|
+
CSV.foreach(file) do |row|
|
63
|
+
rows << row
|
64
|
+
break if (rowcount += 1) >= 10
|
65
|
+
end
|
66
|
+
rows
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class CSVReader
|
71
|
+
def initialize(file, mapping, ignore_first_row)
|
72
|
+
@mapping = mapping
|
73
|
+
@ignore_first_row = ignore_first_row
|
74
|
+
@csv = CSV.open(file, :headers => ignore_first_row)
|
75
|
+
@rows = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def [](index)
|
79
|
+
get_row index
|
80
|
+
end
|
81
|
+
|
82
|
+
def each(&block)
|
83
|
+
@csv.each do |row|
|
84
|
+
block.call CSVRow.new(row, @mapping)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def get_row(index)
|
90
|
+
CSVRow.new((@rows ||= parse_csv)[index], @mapping)
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_csv
|
94
|
+
@csv.read
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class CSVRow
|
99
|
+
def initialize(row, mapping)
|
100
|
+
@row = row
|
101
|
+
@mapping = mapping
|
102
|
+
end
|
103
|
+
|
104
|
+
def [](index)
|
105
|
+
@row[@mapping[index]]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MapFields
|
2
|
+
class Mapping
|
3
|
+
def initialize(mapping, fields)
|
4
|
+
@mapping = mapping
|
5
|
+
@fields = fields
|
6
|
+
|
7
|
+
@mapping = mapping.each_with_object({}){ |arr, hash|
|
8
|
+
key = arr[0].to_i
|
9
|
+
value = arr[1].to_i
|
10
|
+
|
11
|
+
hash[key] = value
|
12
|
+
hash[@fields[key]] = value
|
13
|
+
hash[field_to_symbol(@fields[key])] = value
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](index)
|
18
|
+
@mapping[index]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def field_to_symbol(field)
|
23
|
+
field.to_s.downcase.gsub(/[^a-z0-9]+/, '_').to_sym
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module MapFields
|
2
|
+
class ParamsParser
|
3
|
+
def self.parse(params, field = nil)
|
4
|
+
result = []
|
5
|
+
params.each do |key, value|
|
6
|
+
next if [:controller, :action].include?(key.to_sym)
|
7
|
+
if field.nil? || field.to_s == key.to_s
|
8
|
+
check_values(value) do |k,v|
|
9
|
+
result << ["#{key.to_s}#{k}", v]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def self.check_values(value, &block)
|
18
|
+
result = []
|
19
|
+
if value.kind_of?(Hash)
|
20
|
+
value.each do |k,v|
|
21
|
+
check_values(v) do |k2,v2|
|
22
|
+
result << ["[#{k.to_s}]#{k2}", v2]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
elsif value.kind_of?(Array)
|
26
|
+
value.each do |v|
|
27
|
+
check_values(v) do |k2, v2|
|
28
|
+
result << ["[]#{k2}", v2]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
else
|
32
|
+
result << ["", value] unless value.respond_to?(:read)
|
33
|
+
end
|
34
|
+
result.each do |arr|
|
35
|
+
yield arr[0], arr[1]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/map_fields.rb
CHANGED
@@ -1,201 +1,202 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
end
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
1
|
+
require 'map_fields/controller'
|
2
|
+
require 'map_fields/railtie' if defined?(Rails)
|
3
|
+
|
4
|
+
# module MapFields
|
5
|
+
# VERSION = '1.0.0'
|
6
|
+
|
7
|
+
# def self.included(base)
|
8
|
+
# base.extend(ClassMethods)
|
9
|
+
# end
|
10
|
+
|
11
|
+
# def map_fields
|
12
|
+
# default_options = {
|
13
|
+
# :file_field => 'file',
|
14
|
+
# :params => []
|
15
|
+
# }
|
16
|
+
# options = default_options.merge(
|
17
|
+
# self.class.read_inheritable_attribute(:map_fields_options)
|
18
|
+
# )
|
19
|
+
|
20
|
+
# RAILS_DEFAULT_LOGGER.debug("session[:map_fields]: #{session[:map_fields]}")
|
21
|
+
# RAILS_DEFAULT_LOGGER.debug("params[options[:file_field]]: #{params[options[:file_field]]}")
|
22
|
+
# if session[:map_fields].nil? || !params[options[:file_field]].blank?
|
23
|
+
# session[:map_fields] = {}
|
24
|
+
# if params[options[:file_field]].blank?
|
25
|
+
# @map_fields_error = MissingFileContentsError
|
26
|
+
# return
|
27
|
+
# end
|
28
|
+
|
29
|
+
# file_field = params[options[:file_field]]
|
30
|
+
|
31
|
+
# temp_path = File.join(Dir::tmpdir, "map_fields_#{Time.now.to_i}_#{$$}")
|
32
|
+
# File.open(temp_path, 'wb') do |f|
|
33
|
+
# f.write file_field.read
|
34
|
+
# end
|
35
|
+
|
36
|
+
# session[:map_fields][:file] = temp_path
|
37
|
+
# else
|
38
|
+
# if session[:map_fields][:file].nil? || params[:fields].nil?
|
39
|
+
# session[:map_fields] = nil
|
40
|
+
# @map_fields_error = InconsistentStateError
|
41
|
+
# else
|
42
|
+
# expected_fields = self.class.read_inheritable_attribute(:map_fields_fields)
|
43
|
+
# if expected_fields.respond_to?(:call)
|
44
|
+
# expected_fields = expected_fields.call(params)
|
45
|
+
# end
|
46
|
+
# @mapped_fields = MappedFields.new(session[:map_fields][:file],
|
47
|
+
# expected_fields,
|
48
|
+
# params[:fields],
|
49
|
+
# params[:ignore_first_row])
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
|
53
|
+
# unless @map_fields_error
|
54
|
+
# @rows = []
|
55
|
+
# begin
|
56
|
+
# FasterCSV.foreach(session[:map_fields][:file]) do |row|
|
57
|
+
# @rows << row
|
58
|
+
# break if @rows.size == 10
|
59
|
+
# end
|
60
|
+
# rescue FasterCSV::MalformedCSVError => e
|
61
|
+
# @map_fields_error = e
|
62
|
+
# end
|
63
|
+
# expected_fields = self.class.read_inheritable_attribute(:map_fields_fields)
|
64
|
+
# if expected_fields.respond_to?(:call)
|
65
|
+
# expected_fields = expected_fields.call(params)
|
66
|
+
# end
|
67
|
+
# @fields = ([nil] + expected_fields).inject([]){ |o, e| o << [e, o.size]}
|
68
|
+
# @parameters = []
|
69
|
+
# options[:params].each do |param|
|
70
|
+
# @parameters += ParamsParser.parse(params, param)
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
|
75
|
+
# def mapped_fields
|
76
|
+
# @mapped_fields
|
77
|
+
# end
|
78
|
+
|
79
|
+
# def fields_mapped?
|
80
|
+
# raise @map_fields_error if @map_fields_error
|
81
|
+
# @mapped_fields
|
82
|
+
# end
|
83
|
+
|
84
|
+
# def map_field_parameters(&block)
|
85
|
+
|
86
|
+
# end
|
87
|
+
|
88
|
+
# def map_fields_cleanup
|
89
|
+
# if @mapped_fields
|
90
|
+
# if session[:map_fields][:file]
|
91
|
+
# File.delete(session[:map_fields][:file])
|
92
|
+
# end
|
93
|
+
# session[:map_fields] = nil
|
94
|
+
# @mapped_fields = nil
|
95
|
+
# @map_fields_error = nil
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
|
99
|
+
# module ClassMethods
|
100
|
+
# def map_fields(actions, fields, options = {})
|
101
|
+
# write_inheritable_attribute(:map_fields_fields, fields)
|
102
|
+
# write_inheritable_attribute(:map_fields_options, options)
|
103
|
+
# before_filter :map_fields, :only => actions
|
104
|
+
# after_filter :map_fields_cleanup, :only => actions
|
105
|
+
# end
|
106
|
+
# end
|
107
|
+
|
108
|
+
# class MappedFields
|
109
|
+
# attr_reader :mapping, :ignore_first_row, :file
|
110
|
+
|
111
|
+
# def initialize(file, fields, mapping, ignore_first_row)
|
112
|
+
# @file = file
|
113
|
+
# @fields = fields
|
114
|
+
# @mapping = {}
|
115
|
+
# @ignore_first_row = ignore_first_row == '1'
|
116
|
+
|
117
|
+
# mapping.each do |k,v|
|
118
|
+
# unless v.to_i == 0
|
119
|
+
# #Numeric mapping
|
120
|
+
# @mapping[v.to_i - 1] = k.to_i - 1
|
121
|
+
# #Text mapping
|
122
|
+
# @mapping[fields[v.to_i-1]] = k.to_i - 1
|
123
|
+
# #Symbol mapping
|
124
|
+
# sym_key = fields[v.to_i-1].downcase.
|
125
|
+
# gsub(/[-\s\/]+/, '_').
|
126
|
+
# gsub(/[^a-zA-Z0-9_]+/, '').
|
127
|
+
# to_sym
|
128
|
+
# @mapping[sym_key] = k.to_i - 1
|
129
|
+
# end
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
|
133
|
+
# def is_mapped?(field)
|
134
|
+
# !@mapping[field].nil?
|
135
|
+
# end
|
136
|
+
|
137
|
+
# def each
|
138
|
+
# row_number = 1
|
139
|
+
# FasterCSV.foreach(@file) do |csv_row|
|
140
|
+
# unless row_number == 1 && @ignore_first_row
|
141
|
+
# row = {}
|
142
|
+
# @mapping.each do |k,v|
|
143
|
+
# row[k] = csv_row[v]
|
144
|
+
# end
|
145
|
+
# row.class.send(:define_method, :number) { row_number }
|
146
|
+
# yield(row)
|
147
|
+
# end
|
148
|
+
# row_number += 1
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
# end
|
152
|
+
|
153
|
+
# class InconsistentStateError < StandardError
|
154
|
+
# end
|
155
|
+
|
156
|
+
# class MissingFileContentsError < StandardError
|
157
|
+
# end
|
158
|
+
|
159
|
+
# class ParamsParser
|
160
|
+
# def self.parse(params, field = nil)
|
161
|
+
# result = []
|
162
|
+
# params.each do |key,value|
|
163
|
+
# if field.nil? || field.to_s == key.to_s
|
164
|
+
# check_values(value) do |k,v|
|
165
|
+
# result << ["#{key.to_s}#{k}", v]
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
# end
|
169
|
+
# result
|
170
|
+
# end
|
171
|
+
|
172
|
+
# private
|
173
|
+
# def self.check_values(value, &block)
|
174
|
+
# result = []
|
175
|
+
# if value.kind_of?(Hash)
|
176
|
+
# value.each do |k,v|
|
177
|
+
# check_values(v) do |k2,v2|
|
178
|
+
# result << ["[#{k.to_s}]#{k2}", v2]
|
179
|
+
# end
|
180
|
+
# end
|
181
|
+
# elsif value.kind_of?(Array)
|
182
|
+
# value.each do |v|
|
183
|
+
# check_values(v) do |k2, v2|
|
184
|
+
# result << ["[]#{k2}", v2]
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
# else
|
188
|
+
# result << ["", value]
|
189
|
+
# end
|
190
|
+
# result.each do |arr|
|
191
|
+
# yield arr[0], arr[1]
|
192
|
+
# end
|
193
|
+
# end
|
194
|
+
# end
|
195
|
+
# end
|
196
|
+
|
197
|
+
# if defined?(Rails) and defined?(ActionController)
|
198
|
+
# ActionController::Base.send(:include, MapFields)
|
199
|
+
# ActionController::Base.view_paths.push File.expand_path(File.join(File.dirname(__FILE__), '..', 'views'))
|
200
|
+
# #This is a hack but the above code is not enough when using bundler and Rails 2.3.5
|
201
|
+
# ActionController::Base.view_paths.push "app/views"
|
202
|
+
# end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,45 +1,13 @@
|
|
1
|
-
|
2
|
-
RAILS_ENV = 'test'
|
1
|
+
ENV['RAILS_ENV'] = 'test'
|
3
2
|
|
4
|
-
|
5
|
-
$LOAD_PATH << "spec"
|
6
|
-
$LOAD_PATH << "lib"
|
3
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
7
4
|
|
8
|
-
require '
|
9
|
-
|
10
|
-
require 'rails/
|
11
|
-
require '
|
12
|
-
require '
|
13
|
-
require 'spec/rails'
|
14
|
-
require File.join(File.dirname(__FILE__), '..', 'lib', 'map_fields')
|
5
|
+
require 'bundler'
|
6
|
+
require File.expand_path('../../rails_app/config/environment.rb', __FILE__)
|
7
|
+
require 'rails/test_help'
|
8
|
+
require 'rspec/rails'
|
9
|
+
require 'map_fields'
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
Spec::Runner.configure do |config|
|
19
|
-
#config.use_transactional_fixtures = true
|
20
|
-
end
|
21
|
-
|
22
|
-
class TestController < ApplicationController
|
23
|
-
map_fields :create, ['Title', 'First name', 'Last name'], :params => [:user]
|
24
|
-
|
25
|
-
def new
|
26
|
-
end
|
27
|
-
|
28
|
-
def create
|
29
|
-
if fields_mapped?
|
30
|
-
mapped_fields.each do |row|
|
31
|
-
#deal with the data
|
32
|
-
row[0] #=> will be Title
|
33
|
-
row[1] #=> will be First name
|
34
|
-
row[2] #=> will be Last name
|
35
|
-
end
|
36
|
-
render :text => ''
|
37
|
-
else
|
38
|
-
render 'map_fields/_map_fields'
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
ActionController::Routing::Routes.draw do |map|
|
44
|
-
map.resources :test
|
11
|
+
RailsApp::Application.routes.draw do
|
12
|
+
resources :test
|
45
13
|
end
|
metadata
CHANGED
@@ -1,71 +1,55 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: map_fields
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0.beta
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Andrew Timberlake
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
date: 2011-06-13 00:00:00 +02:00
|
14
|
-
default_executable:
|
12
|
+
date: 2011-06-13 00:00:00.000000000 Z
|
15
13
|
dependencies: []
|
16
|
-
|
17
14
|
description:
|
18
15
|
email: andrew@andrewtimberlake.com
|
19
16
|
executables: []
|
20
|
-
|
21
17
|
extensions: []
|
22
|
-
|
23
|
-
extra_rdoc_files:
|
18
|
+
extra_rdoc_files:
|
24
19
|
- README.rdoc
|
25
|
-
files:
|
26
|
-
- .autotest
|
20
|
+
files:
|
27
21
|
- README.rdoc
|
28
|
-
-
|
29
|
-
-
|
30
|
-
-
|
31
|
-
-
|
22
|
+
- lib/map_fields/controller.rb
|
23
|
+
- lib/map_fields/mapper.rb
|
24
|
+
- lib/map_fields/mapping.rb
|
25
|
+
- lib/map_fields/params_parser.rb
|
26
|
+
- lib/map_fields/railtie.rb
|
32
27
|
- lib/map_fields.rb
|
33
|
-
- spec/application_controller.rb
|
34
|
-
- spec/controllers/test_controller_spec.rb
|
35
|
-
- spec/lib/params_parser_spec.rb
|
36
28
|
- spec/spec_helper.rb
|
37
|
-
- spec/test-file.csv
|
38
|
-
- views/map_fields/_map_fields.erb
|
39
|
-
has_rdoc: true
|
40
29
|
homepage: http://github.com/internuity/map-fields
|
41
30
|
licenses: []
|
42
|
-
|
43
31
|
post_install_message:
|
44
32
|
rdoc_options: []
|
45
|
-
|
46
|
-
require_paths:
|
33
|
+
require_paths:
|
47
34
|
- lib
|
48
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
36
|
none: false
|
50
|
-
requirements:
|
51
|
-
- -
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version:
|
54
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
42
|
none: false
|
56
|
-
requirements:
|
57
|
-
- -
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
version:
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
60
47
|
requirements: []
|
61
|
-
|
62
48
|
rubyforge_project:
|
63
|
-
rubygems_version: 1.
|
49
|
+
rubygems_version: 1.8.10
|
64
50
|
signing_key:
|
65
51
|
specification_version: 3
|
66
|
-
summary: Rails
|
67
|
-
|
68
|
-
|
69
|
-
- spec/controllers/test_controller_spec.rb
|
70
|
-
- spec/lib/params_parser_spec.rb
|
52
|
+
summary: Rails gem to allow a user to map the fields of a CSV to an expected list
|
53
|
+
of fields
|
54
|
+
test_files:
|
71
55
|
- spec/spec_helper.rb
|
data/.autotest
DELETED
data/Rakefile
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rake'
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'jeweler'
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "map_fields"
|
8
|
-
gem.summary = %Q{Rails plugin to allow a user to map the fields of a CSV to an expected list of fields}
|
9
|
-
gem.email = "andrew@andrewtimberlake.com"
|
10
|
-
gem.homepage = "http://github.com/internuity/map-fields"
|
11
|
-
gem.authors = ["Andrew Timberlake"]
|
12
|
-
end
|
13
|
-
Jeweler::GemcutterTasks.new
|
14
|
-
rescue LoadError
|
15
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
16
|
-
end
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.0.4
|
data/announcement.txt
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
map-fields version 1.0.0
|
2
|
-
by Andrew Timberlake
|
3
|
-
http://github.com/internuity/map-fields
|
4
|
-
|
5
|
-
== DESCRIPTION
|
6
|
-
|
7
|
-
A Rails plugin which provides a hook to preview and map the fields of an uploaded CSV file to a pre-defined schema
|
8
|
-
|
9
|
-
== CHANGES
|
10
|
-
|
11
|
-
* Initial release
|
12
|
-
|
data/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'map_fields'
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
|
3
|
-
describe TestController do
|
4
|
-
|
5
|
-
context "on upload" do
|
6
|
-
context "with an attached file" do
|
7
|
-
before do
|
8
|
-
test_file = ActionController::TestUploadedFile.new(File.join(File.dirname(__FILE__), '..', 'test-file.csv'))
|
9
|
-
|
10
|
-
File.should_receive(:open) do |path, flags|
|
11
|
-
path.should match(Regexp.new(Dir::tmpdir))
|
12
|
-
flags.should == 'wb'
|
13
|
-
end
|
14
|
-
FasterCSV.should_receive(:foreach)
|
15
|
-
|
16
|
-
post :create, :file => test_file, :user => {:first_name => 'Test', :last_name => 'User'}
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should assign to @fields" do
|
20
|
-
assigns[:fields].should_not be_blank
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should store the file location in the session" do
|
24
|
-
session[:map_fields][:file].should_not be_blank
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should assign to @parameters" do
|
28
|
-
assigns[:parameters].should_not be_blank
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should have the first name parameter" do
|
32
|
-
assigns[:parameters].should include(['user[first_name]', 'Test'])
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should have the last name parameters" do
|
36
|
-
assigns[:parameters].should include(['user[last_name]', 'User'])
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context "without an attached file" do
|
41
|
-
it "should raise an error" do
|
42
|
-
lambda { post :create }.should raise_error
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context "on second post" do
|
48
|
-
it "should map the fields" do
|
49
|
-
session[:map_fields] = {:file => '/tmp/test'}
|
50
|
-
FasterCSV.expects(:foreach)
|
51
|
-
File.expects(:delete).with('/tmp/test')
|
52
|
-
|
53
|
-
post :create, :fields => {"1" => "2", "2" => "3", "3" => "1"}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,99 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
|
3
|
-
describe MapFields::ParamsParser do
|
4
|
-
context "with no specific field specified" do
|
5
|
-
it "should be able to parse params[:id] = '1'" do
|
6
|
-
params = {:id => '1'}
|
7
|
-
|
8
|
-
MapFields::ParamsParser.parse(params).should == [["id", "1"]]
|
9
|
-
end
|
10
|
-
|
11
|
-
it "should be able to parse params[:user][:id] = '1'" do
|
12
|
-
params = {:user => {:id => '1'}}
|
13
|
-
|
14
|
-
MapFields::ParamsParser.parse(params).should == [["user[id]", "1"]]
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should be able to parse params[:user][] = '1'" do
|
18
|
-
params = {:user => ['1']}
|
19
|
-
|
20
|
-
MapFields::ParamsParser.parse(params).should == [["user[]", "1"]]
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should be able to parse params[:user][] = '1' with multiple entries" do
|
24
|
-
params = {:user => ['1', '2', '3']}
|
25
|
-
|
26
|
-
MapFields::ParamsParser.parse(params).size.should == 3
|
27
|
-
MapFields::ParamsParser.parse(params)[0].should == ["user[]", "1"]
|
28
|
-
MapFields::ParamsParser.parse(params)[1].should == ["user[]", "2"]
|
29
|
-
MapFields::ParamsParser.parse(params)[2].should == ["user[]", "3"]
|
30
|
-
end
|
31
|
-
|
32
|
-
it "should be able to parse params[:user][:sub][:id] = '1'" do
|
33
|
-
params = {:user => {:sub => {:id => '1'}}}
|
34
|
-
|
35
|
-
MapFields::ParamsParser.parse(params).should == [["user[sub][id]", "1"]]
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should be able to parse params[:user][:sub][] = '1'" do
|
39
|
-
params = {:user => {:sub => ['1']}}
|
40
|
-
|
41
|
-
MapFields::ParamsParser.parse(params).should == [["user[sub][]", "1"]]
|
42
|
-
end
|
43
|
-
|
44
|
-
it "should be able to parse params[:user][:sub][] = '1' with multiple entries" do
|
45
|
-
params = {:user => {:sub => ['1', '2', '3']}}
|
46
|
-
|
47
|
-
MapFields::ParamsParser.parse(params).size.should == 3
|
48
|
-
MapFields::ParamsParser.parse(params)[0].should == ["user[sub][]", "1"]
|
49
|
-
MapFields::ParamsParser.parse(params)[1].should == ["user[sub][]", "2"]
|
50
|
-
MapFields::ParamsParser.parse(params)[2].should == ["user[sub][]", "3"]
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should be able to parse params[:user][:sub][:sub2][] = '1'" do
|
54
|
-
params = {:user => {:sub => {:sub2 => ['1']}}}
|
55
|
-
|
56
|
-
MapFields::ParamsParser.parse(params).should == [["user[sub][sub2][]", "1"]]
|
57
|
-
end
|
58
|
-
|
59
|
-
it "should be able to parse a complicated collection of parameters" do
|
60
|
-
params = {:user => {:sub => {:sub2 => ['1']}},
|
61
|
-
:test => '1',
|
62
|
-
:other => ['collection', 'of', 'parameters'],
|
63
|
-
:checking => {:if => ['it', 'can'],
|
64
|
-
:handle => '1',
|
65
|
-
:big => {:bunch => {:of => {:rubish => ['thrown', 'at'],
|
66
|
-
:it => 'yes'}}}
|
67
|
-
}
|
68
|
-
}
|
69
|
-
|
70
|
-
MapFields::ParamsParser.parse(params).should include(["user[sub][sub2][]", "1"])
|
71
|
-
MapFields::ParamsParser.parse(params).should include(["test", "1"])
|
72
|
-
MapFields::ParamsParser.parse(params).should include(["other[]", "collection"])
|
73
|
-
MapFields::ParamsParser.parse(params).should include(["other[]", "of"])
|
74
|
-
MapFields::ParamsParser.parse(params).should include(["other[]", "parameters"])
|
75
|
-
MapFields::ParamsParser.parse(params).should include(["checking[if][]", "it"])
|
76
|
-
MapFields::ParamsParser.parse(params).should include(["checking[if][]", "can"])
|
77
|
-
MapFields::ParamsParser.parse(params).should include(["checking[handle]", "1"])
|
78
|
-
MapFields::ParamsParser.parse(params).should include(["checking[big][bunch][of][rubish][]", "thrown"])
|
79
|
-
MapFields::ParamsParser.parse(params).should include(["checking[big][bunch][of][rubish][]", "at"])
|
80
|
-
MapFields::ParamsParser.parse(params).should include(["checking[big][bunch][of][it]", "yes"])
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
context "with a specified parameter" do
|
85
|
-
it "should be able to get only the requested field" do
|
86
|
-
params = {:user => {:sub => {:sub2 => ['1']}},
|
87
|
-
:test => ['another', 'parameter']
|
88
|
-
}
|
89
|
-
|
90
|
-
MapFields::ParamsParser.parse(params, :user).should == [["user[sub][sub2][]", "1"]]
|
91
|
-
end
|
92
|
-
|
93
|
-
it "should be able to parse params[:user][:sub][:sub2][] = '1'" do
|
94
|
-
params = {:user => {:sub => {:sub2 => ['1']}}}
|
95
|
-
|
96
|
-
MapFields::ParamsParser.parse(params, :user).should == [["user[sub][sub2][]", "1"]]
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
data/spec/test-file.csv
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
<% form_tag nil, :id => 'map_fields_form', :method => :post do -%>
|
2
|
-
<% @parameters.each do |arr| -%>
|
3
|
-
<%= hidden_field_tag arr[0], arr[1] %>
|
4
|
-
<% end -%>
|
5
|
-
<div class="map_fields">
|
6
|
-
<table cellspacing="0">
|
7
|
-
<thead>
|
8
|
-
<tr>
|
9
|
-
<% (1..@rows[0].size).each do |c| -%>
|
10
|
-
<th><%= select_tag "fields[#{c}]", options_for_select(@fields), :include_blank => true, :class => 'field_options' %></th>
|
11
|
-
<% end -%>
|
12
|
-
</tr>
|
13
|
-
</thead>
|
14
|
-
<tbody>
|
15
|
-
<% @rows.each do |row| -%>
|
16
|
-
<tr>
|
17
|
-
<% row.each do |column| -%>
|
18
|
-
<td><%= h(column) %></td>
|
19
|
-
<% end -%>
|
20
|
-
</tr>
|
21
|
-
<% end -%>
|
22
|
-
</tbody>
|
23
|
-
</table>
|
24
|
-
</div>
|
25
|
-
<div class="option">
|
26
|
-
<%= check_box_tag 'ignore_first_row', '1', true, :id => 'ignore_first_row_option' %><label for="ignore_first_row_option">Ignore the first row (headings)</label>
|
27
|
-
</div>
|
28
|
-
<div class="action">
|
29
|
-
<%= submit_tag 'Import' %>
|
30
|
-
</div>
|
31
|
-
<% end -%>
|