map_fields 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/.autotest +2 -0
  2. data/.gitignore +2 -0
  3. data/README.rdoc +107 -0
  4. data/Rakefile +16 -0
  5. data/VERSION +1 -0
  6. data/announcement.txt +12 -0
  7. data/init.rb +1 -0
  8. data/lib/map_fields.rb +197 -0
  9. data/spec/application_controller.rb +2 -0
  10. data/spec/controllers/test_controller_spec.rb +56 -0
  11. data/spec/lib/params_parser_spec.rb +99 -0
  12. data/spec/rails_root/app/controllers/application_controller.rb +10 -0
  13. data/spec/rails_root/app/controllers/import_controller.rb +30 -0
  14. data/spec/rails_root/app/helpers/application_helper.rb +3 -0
  15. data/spec/rails_root/app/helpers/import_helper.rb +2 -0
  16. data/spec/rails_root/config/boot.rb +110 -0
  17. data/spec/rails_root/config/environment.rb +41 -0
  18. data/spec/rails_root/config/environments/development.rb +17 -0
  19. data/spec/rails_root/config/environments/production.rb +28 -0
  20. data/spec/rails_root/config/environments/test.rb +28 -0
  21. data/spec/rails_root/config/initializers/backtrace_silencers.rb +7 -0
  22. data/spec/rails_root/config/initializers/inflections.rb +10 -0
  23. data/spec/rails_root/config/initializers/mime_types.rb +5 -0
  24. data/spec/rails_root/config/initializers/new_rails_defaults.rb +19 -0
  25. data/spec/rails_root/config/initializers/session_store.rb +15 -0
  26. data/spec/rails_root/config/routes.rb +3 -0
  27. data/spec/rails_root/test/functional/import_controller_test.rb +8 -0
  28. data/spec/rails_root/test/performance/browsing_test.rb +9 -0
  29. data/spec/rails_root/test/test_helper.rb +38 -0
  30. data/spec/rails_root/test/unit/helpers/import_helper_test.rb +4 -0
  31. data/spec/spec_helper.rb +45 -0
  32. data/spec/test-file.csv +3 -0
  33. data/views/map_fields/_map_fields.erb +31 -0
  34. metadata +97 -0
data/.autotest ADDED
@@ -0,0 +1,2 @@
1
+ # -*- ruby -*-
2
+ require 'autotest/redgreen'
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg/
2
+ tmp/
data/README.rdoc ADDED
@@ -0,0 +1,107 @@
1
+ = map-fields
2
+
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
+ == FEATURES/PROBLEMS:
10
+
11
+ * Captures the post and provides an intermediate view where a user can map their data to the expected schema
12
+ * Provides a default mapping view that can be customised
13
+ * Allows the import to be part of a larger form (The form fields are remembered through the mapping)
14
+
15
+ == SYNOPSIS:
16
+
17
+ ===Lists controller
18
+ class ListsController < AppliactionController
19
+ map_fields :create, ['Title', 'First name', 'Last name'], :file_field => :file, :params => [:list]
20
+
21
+ def index
22
+ @lists = List.find(:all)
23
+ end
24
+
25
+ def new
26
+ @list = List.new
27
+ end
28
+
29
+ def create
30
+ @list = List.new(params[:list])
31
+ if fields_mapped?
32
+ mapped_fields.each do |row|
33
+ @list.contact.create(:title => row[0],
34
+ :first_name => row[1],
35
+ :last_name => row[2])
36
+ end
37
+ flash[:notice] = 'Contact list created'
38
+ redirect_to :action => :index
39
+ else
40
+ render
41
+ end
42
+ rescue MapFields::InconsistentStateError
43
+ flash[:error] = 'Please try again'
44
+ redirect_to :action => :new
45
+ rescue MapFields::MissingFileContentsError
46
+ flash[:error] = 'Please upload a file'
47
+ redirect_to :action => :new
48
+ end
49
+ end
50
+
51
+ ===New view (new.html.erb)
52
+ <h1>Import a new List</h1>
53
+ <% form_for :list, :html => {:multipart => true} do |form| %>
54
+ <div class="field">
55
+ <%= form.label :name %>
56
+ <%= form.text_field :name %>
57
+ </div>
58
+ <div class="field">
59
+ <label for="file">File</label>
60
+ <%= file_field_tag 'file' %>
61
+ </div>
62
+ <div class="buttons">
63
+ <%= form.submit 'Import' %>
64
+ </div>
65
+ <% end %>
66
+
67
+ ===Create view (create.html.erb)
68
+ <h1>Import a new List</h1>
69
+ <p>Please map the details you're importing</p>
70
+ =render :partial => 'map_fields/map_fields'
71
+
72
+ == REQUIREMENTS:
73
+
74
+ * FasterCSV
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
83
+
84
+ == LICENSE:
85
+
86
+ (The MIT License)
87
+
88
+ Copyright (c) 2009
89
+
90
+ Permission is hereby granted, free of charge, to any person obtaining
91
+ a copy of this software and associated documentation files (the
92
+ 'Software'), to deal in the Software without restriction, including
93
+ without limitation the rights to use, copy, modify, merge, publish,
94
+ distribute, sublicense, and/or sell copies of the Software, and to
95
+ permit persons to whom the Software is furnished to do so, subject to
96
+ the following conditions:
97
+
98
+ The above copyright notice and this permission notice shall be
99
+ included in all copies or substantial portions of the Software.
100
+
101
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
102
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
103
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
104
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
105
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
106
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
107
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
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/andrewtimberlake/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 ADDED
@@ -0,0 +1 @@
1
+ 1.0.1
data/announcement.txt ADDED
@@ -0,0 +1,12 @@
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 ADDED
@@ -0,0 +1 @@
1
+ require 'map_fields'
data/lib/map_fields.rb ADDED
@@ -0,0 +1,197 @@
1
+ require 'fastercsv'
2
+
3
+ module MapFields
4
+ VERSION = '1.0.0'
5
+
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ def map_fields
11
+ default_options = {
12
+ :file_field => 'file',
13
+ :params => []
14
+ }
15
+ options = default_options.merge(
16
+ self.class.read_inheritable_attribute(:map_fields_options)
17
+ )
18
+
19
+ RAILS_DEFAULT_LOGGER.debug("session[:map_fields]: #{session[:map_fields]}")
20
+ RAILS_DEFAULT_LOGGER.debug("params[options[:file_field]]: #{params[options[:file_field]]}")
21
+ if session[:map_fields].nil? || !params[options[:file_field]].blank?
22
+ session[:map_fields] = {}
23
+ if params[options[:file_field]].blank?
24
+ @map_fields_error = MissingFileContentsError
25
+ return
26
+ end
27
+
28
+ file_field = params[options[:file_field]]
29
+
30
+ temp_path = File.join(Dir::tmpdir, "map_fields_#{Time.now.to_i}_#{$$}")
31
+ File.open(temp_path, 'wb') do |f|
32
+ f.write file_field.read
33
+ end
34
+
35
+ session[:map_fields][:file] = temp_path
36
+ else
37
+ if session[:map_fields][:file].nil? || params[:fields].nil?
38
+ session[:map_fields] = nil
39
+ @map_fields_error = InconsistentStateError
40
+ else
41
+ expected_fields = self.class.read_inheritable_attribute(:map_fields_fields)
42
+ if expected_fields.respond_to?(:call)
43
+ expected_fields = expected_fields.call(params)
44
+ end
45
+ @mapped_fields = MappedFields.new(session[:map_fields][:file],
46
+ expected_fields,
47
+ params[:fields],
48
+ params[:ignore_first_row])
49
+ end
50
+ end
51
+
52
+ @rows = []
53
+ begin
54
+ FasterCSV.foreach(session[:map_fields][:file]) do |row|
55
+ @rows << row
56
+ break if @rows.size == 10
57
+ end
58
+ rescue FasterCSV::MalformedCSVError => e
59
+ @map_fields_error = e
60
+ end
61
+ expected_fields = self.class.read_inheritable_attribute(:map_fields_fields)
62
+ if expected_fields.respond_to?(:call)
63
+ expected_fields = expected_fields.call(params)
64
+ end
65
+ @fields = ([nil] + expected_fields).inject([]){ |o, e| o << [e, o.size]}
66
+ @parameters = []
67
+ options[:params].each do |param|
68
+ @parameters += ParamsParser.parse(params, param)
69
+ end
70
+ end
71
+
72
+ def mapped_fields
73
+ @mapped_fields
74
+ end
75
+
76
+ def fields_mapped?
77
+ raise @map_fields_error if @map_fields_error
78
+ @mapped_fields
79
+ end
80
+
81
+ def map_field_parameters(&block)
82
+
83
+ end
84
+
85
+ def map_fields_cleanup
86
+ if @mapped_fields
87
+ if session[:map_fields][:file]
88
+ File.delete(session[:map_fields][:file])
89
+ end
90
+ session[:map_fields] = nil
91
+ @mapped_fields = nil
92
+ @map_fields_error = nil
93
+ end
94
+ end
95
+
96
+ module ClassMethods
97
+ def map_fields(action, fields, options = {})
98
+ write_inheritable_attribute(:map_fields_fields, fields)
99
+ write_inheritable_attribute(:map_fields_options, options)
100
+ before_filter :map_fields, :only => action
101
+ after_filter :map_fields_cleanup, :only => action
102
+ end
103
+ end
104
+
105
+ class MappedFields
106
+ attr_reader :mapping, :ignore_first_row, :file
107
+
108
+ def initialize(file, fields, mapping, ignore_first_row)
109
+ @file = file
110
+ @fields = fields
111
+ @mapping = {}
112
+ @ignore_first_row = ignore_first_row
113
+
114
+ mapping.each do |k,v|
115
+ unless v.to_i == 0
116
+ #Numeric mapping
117
+ @mapping[v.to_i - 1] = k.to_i - 1
118
+ #Text mapping
119
+ @mapping[fields[v.to_i-1]] = k.to_i - 1
120
+ #Symbol mapping
121
+ sym_key = fields[v.to_i-1].downcase.
122
+ gsub(/[-\s\/]+/, '_').
123
+ gsub(/[^a-zA-Z0-9_]+/, '').
124
+ to_sym
125
+ @mapping[sym_key] = k.to_i - 1
126
+ end
127
+ end
128
+ end
129
+
130
+ def is_mapped?(field)
131
+ !@mapping[field].nil?
132
+ end
133
+
134
+ def each
135
+ row_number = 1
136
+ FasterCSV.foreach(@file) do |csv_row|
137
+ unless row_number == 1 && @ignore_first_row
138
+ row = {}
139
+ @mapping.each do |k,v|
140
+ row[k] = csv_row[v]
141
+ end
142
+ row.class.send(:define_method, :number) { row_number }
143
+ yield(row)
144
+ end
145
+ row_number += 1
146
+ end
147
+ end
148
+ end
149
+
150
+ class InconsistentStateError < StandardError
151
+ end
152
+
153
+ class MissingFileContentsError < StandardError
154
+ end
155
+
156
+ class ParamsParser
157
+ def self.parse(params, field = nil)
158
+ result = []
159
+ params.each do |key,value|
160
+ if field.nil? || field.to_s == key.to_s
161
+ check_values(value) do |k,v|
162
+ result << ["#{key.to_s}#{k}", v]
163
+ end
164
+ end
165
+ end
166
+ result
167
+ end
168
+
169
+ private
170
+ def self.check_values(value, &block)
171
+ result = []
172
+ if value.kind_of?(Hash)
173
+ value.each do |k,v|
174
+ check_values(v) do |k2,v2|
175
+ result << ["[#{k.to_s}]#{k2}", v2]
176
+ end
177
+ end
178
+ elsif value.kind_of?(Array)
179
+ value.each do |v|
180
+ check_values(v) do |k2, v2|
181
+ result << ["[]#{k2}", v2]
182
+ end
183
+ end
184
+ else
185
+ result << ["", value]
186
+ end
187
+ result.each do |arr|
188
+ yield arr[0], arr[1]
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ if defined?(Rails) and defined?(ActionController)
195
+ ActionController::Base.send(:include, MapFields)
196
+ ActionController::Base.view_paths.push File.expand_path(File.join(File.dirname(__FILE__), '..', 'views'))
197
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,56 @@
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
@@ -0,0 +1,99 @@
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
@@ -0,0 +1,10 @@
1
+ # Filters added to this controller apply to all controllers in the application.
2
+ # Likewise, all the methods added will be available for all controllers.
3
+
4
+ class ApplicationController < ActionController::Base
5
+ helper :all # include all helpers, all the time
6
+ protect_from_forgery # See ActionController::RequestForgeryProtection for details
7
+
8
+ # Scrub sensitive parameters from your log
9
+ # filter_parameter_logging :password
10
+ end
@@ -0,0 +1,30 @@
1
+ class ImportController < ApplicationController
2
+ map_fields :create, ['Title', 'First name', 'Last name', 'Email'], :file_field => 'file', :params => [:name]
3
+
4
+ def new
5
+ render
6
+ end
7
+
8
+ def create
9
+ begin
10
+ if fields_mapped?
11
+ #Process import
12
+
13
+ render :done
14
+ else
15
+ render
16
+ end
17
+ rescue MapFields::InconsistentStateError
18
+ logger.debug 'catching InconsistentStateError'
19
+ redirect_to :action => :new, :status => 303
20
+ return
21
+ rescue MapFields::MissingFileContentsError
22
+ logger.debug 'catching MissingFileContentsError'
23
+ flash[:error] = 'Please upload a file'
24
+ render :action => :new
25
+ return
26
+ end
27
+
28
+ logger.debug 'here'
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ # Methods added to this helper will be available to all templates in the application.
2
+ module ApplicationHelper
3
+ end
@@ -0,0 +1,2 @@
1
+ module ImportHelper
2
+ end
@@ -0,0 +1,110 @@
1
+ # Don't change this file!
2
+ # Configure your app in config/environment.rb and config/environments/*.rb
3
+
4
+ RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
5
+
6
+ module Rails
7
+ class << self
8
+ def boot!
9
+ unless booted?
10
+ preinitialize
11
+ pick_boot.run
12
+ end
13
+ end
14
+
15
+ def booted?
16
+ defined? Rails::Initializer
17
+ end
18
+
19
+ def pick_boot
20
+ (vendor_rails? ? VendorBoot : GemBoot).new
21
+ end
22
+
23
+ def vendor_rails?
24
+ File.exist?("#{RAILS_ROOT}/vendor/rails")
25
+ end
26
+
27
+ def preinitialize
28
+ load(preinitializer_path) if File.exist?(preinitializer_path)
29
+ end
30
+
31
+ def preinitializer_path
32
+ "#{RAILS_ROOT}/config/preinitializer.rb"
33
+ end
34
+ end
35
+
36
+ class Boot
37
+ def run
38
+ load_initializer
39
+ Rails::Initializer.run(:set_load_path)
40
+ end
41
+ end
42
+
43
+ class VendorBoot < Boot
44
+ def load_initializer
45
+ require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
46
+ Rails::Initializer.run(:install_gem_spec_stubs)
47
+ Rails::GemDependency.add_frozen_gem_path
48
+ end
49
+ end
50
+
51
+ class GemBoot < Boot
52
+ def load_initializer
53
+ self.class.load_rubygems
54
+ load_rails_gem
55
+ require 'initializer'
56
+ end
57
+
58
+ def load_rails_gem
59
+ if version = self.class.gem_version
60
+ gem 'rails', version
61
+ else
62
+ gem 'rails'
63
+ end
64
+ rescue Gem::LoadError => load_error
65
+ $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
66
+ exit 1
67
+ end
68
+
69
+ class << self
70
+ def rubygems_version
71
+ Gem::RubyGemsVersion rescue nil
72
+ end
73
+
74
+ def gem_version
75
+ if defined? RAILS_GEM_VERSION
76
+ RAILS_GEM_VERSION
77
+ elsif ENV.include?('RAILS_GEM_VERSION')
78
+ ENV['RAILS_GEM_VERSION']
79
+ else
80
+ parse_gem_version(read_environment_rb)
81
+ end
82
+ end
83
+
84
+ def load_rubygems
85
+ require 'rubygems'
86
+ min_version = '1.3.1'
87
+ unless rubygems_version >= min_version
88
+ $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
89
+ exit 1
90
+ end
91
+
92
+ rescue LoadError
93
+ $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
94
+ exit 1
95
+ end
96
+
97
+ def parse_gem_version(text)
98
+ $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
99
+ end
100
+
101
+ private
102
+ def read_environment_rb
103
+ File.read("#{RAILS_ROOT}/config/environment.rb")
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # All that for this:
110
+ Rails.boot!
@@ -0,0 +1,41 @@
1
+ # Be sure to restart your server when you modify this file
2
+
3
+ # Specifies gem version of Rails to use when vendor/rails is not present
4
+ RAILS_GEM_VERSION = '2.3.2' unless defined? RAILS_GEM_VERSION
5
+
6
+ # Bootstrap the Rails environment, frameworks, and default configuration
7
+ require File.join(File.dirname(__FILE__), 'boot')
8
+
9
+ Rails::Initializer.run do |config|
10
+ # Settings in config/environments/* take precedence over those specified here.
11
+ # Application configuration should go into files in config/initializers
12
+ # -- all .rb files in that directory are automatically loaded.
13
+
14
+ # Add additional load paths for your own custom dirs
15
+ # config.load_paths += %W( #{RAILS_ROOT}/extras )
16
+
17
+ # Specify gems that this application depends on and have them installed with rake gems:install
18
+ # config.gem "bj"
19
+ # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
20
+ # config.gem "sqlite3-ruby", :lib => "sqlite3"
21
+ # config.gem "aws-s3", :lib => "aws/s3"
22
+
23
+ # Only load the plugins named here, in the order given (default is alphabetical).
24
+ # :all can be used as a placeholder for all plugins not explicitly named
25
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
26
+
27
+ # Skip frameworks you're not going to use. To use Rails without a database,
28
+ # you must remove the Active Record framework.
29
+ # config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
30
+
31
+ # Activate observers that should always be running
32
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
33
+
34
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
35
+ # Run "rake -D time" for a list of tasks for finding time zone names.
36
+ config.time_zone = 'UTC'
37
+
38
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
39
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
40
+ # config.i18n.default_locale = :de
41
+ end
@@ -0,0 +1,17 @@
1
+ # Settings specified here will take precedence over those in config/environment.rb
2
+
3
+ # In the development environment your application's code is reloaded on
4
+ # every request. This slows down response time but is perfect for development
5
+ # since you don't have to restart the webserver when you make code changes.
6
+ config.cache_classes = false
7
+
8
+ # Log error messages when you accidentally call methods on nil.
9
+ config.whiny_nils = true
10
+
11
+ # Show full error reports and disable caching
12
+ config.action_controller.consider_all_requests_local = true
13
+ config.action_view.debug_rjs = true
14
+ config.action_controller.perform_caching = false
15
+
16
+ # Don't care if the mailer can't send
17
+ config.action_mailer.raise_delivery_errors = false
@@ -0,0 +1,28 @@
1
+ # Settings specified here will take precedence over those in config/environment.rb
2
+
3
+ # The production environment is meant for finished, "live" apps.
4
+ # Code is not reloaded between requests
5
+ config.cache_classes = true
6
+
7
+ # Full error reports are disabled and caching is turned on
8
+ config.action_controller.consider_all_requests_local = false
9
+ config.action_controller.perform_caching = true
10
+ config.action_view.cache_template_loading = true
11
+
12
+ # See everything in the log (default is :info)
13
+ # config.log_level = :debug
14
+
15
+ # Use a different logger for distributed setups
16
+ # config.logger = SyslogLogger.new
17
+
18
+ # Use a different cache store in production
19
+ # config.cache_store = :mem_cache_store
20
+
21
+ # Enable serving of images, stylesheets, and javascripts from an asset server
22
+ # config.action_controller.asset_host = "http://assets.example.com"
23
+
24
+ # Disable delivery errors, bad email addresses will be ignored
25
+ # config.action_mailer.raise_delivery_errors = false
26
+
27
+ # Enable threaded mode
28
+ # config.threadsafe!
@@ -0,0 +1,28 @@
1
+ # Settings specified here will take precedence over those in config/environment.rb
2
+
3
+ # The test environment is used exclusively to run your application's
4
+ # test suite. You never need to work with it otherwise. Remember that
5
+ # your test database is "scratch space" for the test suite and is wiped
6
+ # and recreated between test runs. Don't rely on the data there!
7
+ config.cache_classes = true
8
+
9
+ # Log error messages when you accidentally call methods on nil.
10
+ config.whiny_nils = true
11
+
12
+ # Show full error reports and disable caching
13
+ config.action_controller.consider_all_requests_local = true
14
+ config.action_controller.perform_caching = false
15
+ config.action_view.cache_template_loading = true
16
+
17
+ # Disable request forgery protection in test environment
18
+ config.action_controller.allow_forgery_protection = false
19
+
20
+ # Tell Action Mailer not to deliver emails to the real world.
21
+ # The :test delivery method accumulates sent emails in the
22
+ # ActionMailer::Base.deliveries array.
23
+ config.action_mailer.delivery_method = :test
24
+
25
+ # Use SQL instead of Active Record's schema dumper when creating the test database.
26
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
27
+ # like if you have constraints or database-specific column types
28
+ # config.active_record.schema_format = :sql
@@ -0,0 +1,7 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4
+ # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5
+
6
+ # You can also remove all the silencers if you're trying do debug a problem that might steem from framework code.
7
+ # Rails.backtrace_cleaner.remove_silencers!
@@ -0,0 +1,10 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new inflection rules using the following format
4
+ # (all these examples are active by default):
5
+ # ActiveSupport::Inflector.inflections do |inflect|
6
+ # inflect.plural /^(ox)$/i, '\1en'
7
+ # inflect.singular /^(ox)en/i, '\1'
8
+ # inflect.irregular 'person', 'people'
9
+ # inflect.uncountable %w( fish sheep )
10
+ # end
@@ -0,0 +1,5 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new mime types for use in respond_to blocks:
4
+ # Mime::Type.register "text/richtext", :rtf
5
+ # Mime::Type.register_alias "text/html", :iphone
@@ -0,0 +1,19 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # These settings change the behavior of Rails 2 apps and will be defaults
4
+ # for Rails 3. You can remove this initializer when Rails 3 is released.
5
+
6
+ if defined?(ActiveRecord)
7
+ # Include Active Record class name as root for JSON serialized output.
8
+ ActiveRecord::Base.include_root_in_json = true
9
+
10
+ # Store the full class name (including module namespace) in STI type column.
11
+ ActiveRecord::Base.store_full_sti_class = true
12
+ end
13
+
14
+ # Use ISO 8601 format for JSON serialized times and dates.
15
+ ActiveSupport.use_standard_json_time_format = true
16
+
17
+ # Don't escape HTML entities in JSON, leave that for the #json_escape helper.
18
+ # if you're including raw json in an HTML page.
19
+ ActiveSupport.escape_html_entities_in_json = false
@@ -0,0 +1,15 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Your secret key for verifying cookie session data integrity.
4
+ # If you change this key, all old sessions will become invalid!
5
+ # Make sure the secret is at least 30 characters and all random,
6
+ # no regular words or you'll be exposed to dictionary attacks.
7
+ ActionController::Base.session = {
8
+ :key => '_rails_root_session',
9
+ :secret => 'f38f519843de9979c5fe159cd03b57bb30bd8591d9d3f7e9d8e0ad00b477c06c6bf884f0c8095928f57fec7c841225144f1150bab3053a66d84e5b6acdbd56ab'
10
+ }
11
+
12
+ # Use the database for sessions instead of the cookie-based default,
13
+ # which shouldn't be used to store highly confidential information
14
+ # (create the session table with "rake db:sessions:create")
15
+ # ActionController::Base.session_store = :active_record_store
@@ -0,0 +1,3 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.resources :import
3
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class ImportControllerTest < ActionController::TestCase
4
+ # Replace this with your real tests.
5
+ test "the truth" do
6
+ assert true
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+ require 'performance_test_help'
3
+
4
+ # Profiling results for each test method are written to tmp/performance.
5
+ class BrowsingTest < ActionController::PerformanceTest
6
+ def test_homepage
7
+ get '/'
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
3
+ require 'test_help'
4
+
5
+ class ActiveSupport::TestCase
6
+ # Transactional fixtures accelerate your tests by wrapping each test method
7
+ # in a transaction that's rolled back on completion. This ensures that the
8
+ # test database remains unchanged so your fixtures don't have to be reloaded
9
+ # between every test method. Fewer database queries means faster tests.
10
+ #
11
+ # Read Mike Clark's excellent walkthrough at
12
+ # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
13
+ #
14
+ # Every Active Record database supports transactions except MyISAM tables
15
+ # in MySQL. Turn off transactional fixtures in this case; however, if you
16
+ # don't care one way or the other, switching from MyISAM to InnoDB tables
17
+ # is recommended.
18
+ #
19
+ # The only drawback to using transactional fixtures is when you actually
20
+ # need to test transactions. Since your test is bracketed by a transaction,
21
+ # any transactions started in your code will be automatically rolled back.
22
+ self.use_transactional_fixtures = true
23
+
24
+ # Instantiated fixtures are slow, but give you @david where otherwise you
25
+ # would need people(:david). If you don't want to migrate your existing
26
+ # test cases which use the @david style and don't mind the speed hit (each
27
+ # instantiated fixtures translates to a database query per test method),
28
+ # then set this back to true.
29
+ self.use_instantiated_fixtures = false
30
+
31
+ # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
32
+ #
33
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
34
+ # -- they do not yet inherit this setting
35
+ fixtures :all
36
+
37
+ # Add more helper methods to be used by all tests here...
38
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class ImportHelperTest < ActionView::TestCase
4
+ end
@@ -0,0 +1,45 @@
1
+ RAILS_ROOT = File.join(File.dirname(__FILE__), 'rails_root')
2
+ RAILS_ENV = 'test'
3
+
4
+ #Needed for autospec
5
+ $LOAD_PATH << "spec"
6
+ $LOAD_PATH << "lib"
7
+
8
+ require 'rubygems'
9
+ #Needed for rspec_rails
10
+ require 'rails/version'
11
+ require 'action_controller'
12
+ require 'action_controller/test_process'
13
+ require 'spec/rails'
14
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'map_fields')
15
+
16
+ require File.join(File.dirname(__FILE__), '..', 'init')
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
45
+ end
@@ -0,0 +1,3 @@
1
+ Title,Lastname,Firstname,Email
2
+ Mr,John,Doe,john.doe@example.com
3
+ Mrs,Jane,Doe,jane.doe@example.com
@@ -0,0 +1,31 @@
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 -%>
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: map_fields
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 1
9
+ version: 1.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Andrew Timberlake
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-12 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email: andrew@andrewtimberlake.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - .autotest
31
+ - .gitignore
32
+ - README.rdoc
33
+ - Rakefile
34
+ - VERSION
35
+ - announcement.txt
36
+ - init.rb
37
+ - lib/map_fields.rb
38
+ - spec/application_controller.rb
39
+ - spec/controllers/test_controller_spec.rb
40
+ - spec/lib/params_parser_spec.rb
41
+ - spec/spec_helper.rb
42
+ - spec/test-file.csv
43
+ - views/map_fields/_map_fields.erb
44
+ has_rdoc: true
45
+ homepage: http://github.com/andrewtimberlake/map-fields
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.3.6
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Rails plugin to allow a user to map the fields of a CSV to an expected list of fields
74
+ test_files:
75
+ - spec/controllers/test_controller_spec.rb
76
+ - spec/lib/params_parser_spec.rb
77
+ - spec/spec_helper.rb
78
+ - spec/rails_root/app/controllers/import_controller.rb
79
+ - spec/rails_root/app/controllers/application_controller.rb
80
+ - spec/rails_root/app/helpers/import_helper.rb
81
+ - spec/rails_root/app/helpers/application_helper.rb
82
+ - spec/rails_root/test/test_helper.rb
83
+ - spec/rails_root/test/unit/helpers/import_helper_test.rb
84
+ - spec/rails_root/test/performance/browsing_test.rb
85
+ - spec/rails_root/test/functional/import_controller_test.rb
86
+ - spec/rails_root/config/environments/test.rb
87
+ - spec/rails_root/config/environments/development.rb
88
+ - spec/rails_root/config/environments/production.rb
89
+ - spec/rails_root/config/boot.rb
90
+ - spec/rails_root/config/routes.rb
91
+ - spec/rails_root/config/initializers/inflections.rb
92
+ - spec/rails_root/config/initializers/backtrace_silencers.rb
93
+ - spec/rails_root/config/initializers/session_store.rb
94
+ - spec/rails_root/config/initializers/new_rails_defaults.rb
95
+ - spec/rails_root/config/initializers/mime_types.rb
96
+ - spec/rails_root/config/environment.rb
97
+ - spec/application_controller.rb