seamusabshere-csv_bootstrap 0.1.0
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/LICENSE +20 -0
- data/README.rdoc +45 -0
- data/Rakefile +56 -0
- data/VERSION.yml +4 -0
- data/lib/csv_bootstrap.rb +108 -0
- data/test/csv_bootstrap_ext_test.rb +30 -0
- data/test/csv_bootstrap_test.rb +46 -0
- data/test/test_helper.rb +64 -0
- metadata +63 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Seamus Abshere
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
= csv_bootstrap
|
2
|
+
|
3
|
+
class FuelType
|
4
|
+
csv_bootstrap :url => 'http://spreadsheets.google.com/pub?key=p70r3FHguhimIdBKyVz3iPA'
|
5
|
+
end
|
6
|
+
|
7
|
+
This will bootstrap your ActiveRecord model with sheet 1 of the Google Docs spreadsheet.
|
8
|
+
|
9
|
+
>> FuelType.bootstrap!
|
10
|
+
=> true
|
11
|
+
|
12
|
+
You can specify other sheets (starts at 1):
|
13
|
+
|
14
|
+
class FuelType
|
15
|
+
csv_bootstrap :url => 'http://spreadsheets.google.com/pub?key=p70r3FHguhimIdBKyVz3iPA',
|
16
|
+
:sheet => 1
|
17
|
+
end
|
18
|
+
|
19
|
+
The gem will always request the CSV-formatted version of the document in question.
|
20
|
+
|
21
|
+
== Spreadsheet structure
|
22
|
+
|
23
|
+
The first column is always the key.
|
24
|
+
|
25
|
+
All of the other columns will be included as attributes, based on the column headers.
|
26
|
+
|
27
|
+
name emission_factor units
|
28
|
+
coal 5246.89 lbs/short ton
|
29
|
+
natural gas 1.25 pounds / therm (nat gas)
|
30
|
+
fuel oil 22.51 lbs/gallon
|
31
|
+
|
32
|
+
would generate
|
33
|
+
|
34
|
+
a = FuelType.find_or_create_by_name('coal')
|
35
|
+
a.update_attributes(:name => 'coal', :emission_factor => '5246.89', :units => 'lbs/short ton')
|
36
|
+
|
37
|
+
a = FuelType.find_or_create_by_name('natural gas')
|
38
|
+
a.update_attributes(:name => 'coal', :emission_factor => '1.25', :units => 'pounds / therm (nat gas)')
|
39
|
+
|
40
|
+
a = FuelType.find_or_create_by_name('fuel oil')
|
41
|
+
a.update_attributes(:name => 'coal', :emission_factor => '22.51', :units => 'lbs/gallon')
|
42
|
+
|
43
|
+
== Copyright
|
44
|
+
|
45
|
+
Copyright (c) 2009 Seamus Abshere. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "csv_bootstrap"
|
8
|
+
gem.summary = %Q{TODO}
|
9
|
+
gem.email = "seamus@abshere.net"
|
10
|
+
gem.homepage = "http://github.com/seamusabshere/csv_bootstrap"
|
11
|
+
gem.authors = ["Seamus Abshere"]
|
12
|
+
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
if File.exist?('VERSION.yml')
|
45
|
+
config = YAML.load(File.read('VERSION.yml'))
|
46
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "csv_bootstrap #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
56
|
+
|
data/VERSION.yml
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fastercsv'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'active_support'
|
5
|
+
|
6
|
+
class CsvBootstrap
|
7
|
+
attr_accessor :url, :target_class_name, :key_sprintf, :sheet # set in options
|
8
|
+
attr_accessor :key_name, :headers, :dictionary, :width
|
9
|
+
|
10
|
+
def bootstrap!
|
11
|
+
load_dictionary unless dictionary_loaded?
|
12
|
+
dictionary.each do |k, v|
|
13
|
+
instance_of_target_class = target_class.send("find_or_create_by_#{key_name}", v[key_name])
|
14
|
+
instance_of_target_class.update_attributes(v.except(key_name))
|
15
|
+
end
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
@sheet = interpret_sheet(options[:sheet])
|
23
|
+
@url = interpret_url(options[:url])
|
24
|
+
@target_class_name = options[:target_class_name]
|
25
|
+
@key_sprintf = options[:key_sprintf] || '%s'
|
26
|
+
end
|
27
|
+
|
28
|
+
def dictionary_loaded?
|
29
|
+
!dictionary.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_dictionary
|
33
|
+
self.dictionary = {}
|
34
|
+
open(url) do |data|
|
35
|
+
FasterCSV.parse(data, :headers => :first_row) do |row|
|
36
|
+
self.width ||= row.fields.size
|
37
|
+
self.key_name ||= row.headers[0].to_sym
|
38
|
+
self.headers ||= row.headers
|
39
|
+
next if row.fields[0].blank?
|
40
|
+
key = interpret_key(row.fields[0])
|
41
|
+
dictionary[key] = interpret_columns(row.fields[0..width])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def interpret_sheet(sheet)
|
47
|
+
sheet.to_i >= 1 ? sheet.to_i - 1 : 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def interpret_url(url)
|
51
|
+
if url.include?('spreadsheets.google.com')
|
52
|
+
if /\&gid=(.*)(\&|$)/.match(url)
|
53
|
+
raise "Your Google Docs URL includes a sheet number (&gid=#{$1}). Please remove it and set the sheet with :sheet => #{$1.to_i + 1}"
|
54
|
+
end
|
55
|
+
url = url.gsub(/\&output=.*(\&|$)/, '')
|
56
|
+
url << "&output=csv&gid=#{sheet}"
|
57
|
+
end
|
58
|
+
url
|
59
|
+
end
|
60
|
+
|
61
|
+
def interpret_key(k)
|
62
|
+
k = k.to_s.strip
|
63
|
+
k = k.to_i if /\%[0-9\.]*d/.match(key_sprintf)
|
64
|
+
key_sprintf % k
|
65
|
+
end
|
66
|
+
|
67
|
+
def interpret_columns(columns)
|
68
|
+
memo = {}
|
69
|
+
columns.each_with_index do |column, index|
|
70
|
+
# puts "memo.merge!(headers[#{index}].to_sym => column) as memo.merge!(#{headers[index].to_sym} => #{column})"
|
71
|
+
memo.merge!(headers[index].to_sym => column)
|
72
|
+
end
|
73
|
+
memo
|
74
|
+
end
|
75
|
+
|
76
|
+
def target_class
|
77
|
+
target_class_name.constantize
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module ActiveRecord
|
82
|
+
module CsvBootstrapExt
|
83
|
+
def self.included(klass)
|
84
|
+
klass.class_eval do
|
85
|
+
self.class_inheritable_accessor :csv_bootstraps
|
86
|
+
extend ClassMethods
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module ClassMethods
|
91
|
+
def csv_bootstrap(options = {})
|
92
|
+
self.csv_bootstraps ||= []
|
93
|
+
options[:target_class_name] ||= self.class_name
|
94
|
+
csv_bootstraps << CsvBootstrap.new(options)
|
95
|
+
end
|
96
|
+
|
97
|
+
def clear_bootstraps
|
98
|
+
self.csv_bootstraps = []
|
99
|
+
end
|
100
|
+
|
101
|
+
def bootstrap!
|
102
|
+
csv_bootstraps.each { |b| b.bootstrap! }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
ActiveRecord::Base.send :include, ActiveRecord::CsvBootstrapExt
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CsvBootstrapExtTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
CsvBootstrapTestHelper.setup_database
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
Scrum.clear_bootstraps
|
10
|
+
CsvBootstrapTestHelper.teardown_database
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_should_mixin_methods
|
14
|
+
assert Scrum.respond_to?(:csv_bootstrap)
|
15
|
+
assert Scrum.respond_to?(:clear_bootstraps)
|
16
|
+
assert Scrum.respond_to?(:bootstrap!)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_should_set_options
|
20
|
+
options = { :target_class_name => 'Scrum', :url => CSV_URL, :sheet => 1 }
|
21
|
+
CsvBootstrap.expects(:new).with(options)
|
22
|
+
Scrum.csv_bootstrap options
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_bootstrap
|
26
|
+
Scrum.csv_bootstrap :url => CSV_URL
|
27
|
+
CsvBootstrap.any_instance.expects(:bootstrap!)
|
28
|
+
Scrum.bootstrap!
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CsvBootstrapTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
CsvBootstrapTestHelper.setup_database
|
6
|
+
@bootstrap = CsvBootstrap.new(:target_class_name => 'Scrum', :url => GDOCS_URL, :sheet => 1)
|
7
|
+
end
|
8
|
+
|
9
|
+
def teardown
|
10
|
+
Scrum.clear_bootstraps
|
11
|
+
CsvBootstrapTestHelper.teardown_database
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_should_have_target_class_name
|
15
|
+
assert_equal @bootstrap.target_class_name, 'Scrum'
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_have_zero_based_sheet
|
19
|
+
assert_equal @bootstrap.sheet, 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_should_set_url
|
23
|
+
bootstrap = CsvBootstrap.new(:target_class_name => 'Scrum', :url => CSV_URL)
|
24
|
+
assert_equal bootstrap.url, CSV_URL
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_should_set_and_modify_gdocs_url
|
28
|
+
assert_equal @bootstrap.url, GDOCS_URL + '&output=csv&gid=0'
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_should_raise_if_gdocs_url_includes_gid
|
32
|
+
assert_raise RuntimeError do
|
33
|
+
CsvBootstrap.new(:target_class_name => 'Scrum', :url => GDOCS_URL + '&gid=1')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_should_bootstrap_keys
|
38
|
+
Scrum.expects(:find_or_create_by_name).at_least_once.returns(Scrum.new)
|
39
|
+
@bootstrap.bootstrap!
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_should_bootstrap_values
|
43
|
+
Scrum.any_instance.expects(:update_attributes).at_least_once
|
44
|
+
@bootstrap.bootstrap!
|
45
|
+
end
|
46
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module CsvBootstrapTestHelper
|
2
|
+
VENDOR_RAILS = File.expand_path('../../../../rails', __FILE__)
|
3
|
+
OTHER_RAILS = File.expand_path('../../../rails', __FILE__)
|
4
|
+
PLUGIN_ROOT = File.expand_path('../../', __FILE__)
|
5
|
+
|
6
|
+
def self.rails_directory
|
7
|
+
if File.exist?(VENDOR_RAILS)
|
8
|
+
VENDOR_RAILS
|
9
|
+
elsif File.exist?(OTHER_RAILS)
|
10
|
+
OTHER_RAILS
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load_dependencies
|
15
|
+
if rails_directory
|
16
|
+
$:.unshift(File.join(rails_directory, 'activesupport', 'lib'))
|
17
|
+
$:.unshift(File.join(rails_directory, 'activerecord', 'lib'))
|
18
|
+
else
|
19
|
+
require 'rubygems' rescue LoadError
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'active_record'
|
23
|
+
require 'test/unit'
|
24
|
+
require 'mocha'
|
25
|
+
|
26
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
27
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
28
|
+
require 'csv_bootstrap'
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.configure_database
|
32
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
33
|
+
ActiveRecord::Migration.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.setup_database
|
37
|
+
ActiveRecord::Schema.define do
|
38
|
+
create_table :scrums do |t|
|
39
|
+
t.string :name
|
40
|
+
t.string :emission_factor
|
41
|
+
t.string :units
|
42
|
+
t.timestamps
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.teardown_database
|
48
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
49
|
+
ActiveRecord::Base.connection.drop_table(table)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.start
|
54
|
+
load_dependencies
|
55
|
+
configure_database
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
CsvBootstrapTestHelper.start
|
60
|
+
|
61
|
+
class Scrum < ActiveRecord::Base; end
|
62
|
+
|
63
|
+
GDOCS_URL = 'http://spreadsheets.google.com/pub?key=p70r3FHguhimIdBKyVz3iPA'
|
64
|
+
CSV_URL = 'http://static.brighterplanet.com/science/data/transport/air/iata_codes-bts_aircraft_types.csv'
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: seamusabshere-csv_bootstrap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Seamus Abshere
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-07 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: seamus@abshere.net
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- LICENSE
|
27
|
+
- README.rdoc
|
28
|
+
- Rakefile
|
29
|
+
- VERSION.yml
|
30
|
+
- lib/csv_bootstrap.rb
|
31
|
+
- test/csv_bootstrap_ext_test.rb
|
32
|
+
- test/csv_bootstrap_test.rb
|
33
|
+
- test/test_helper.rb
|
34
|
+
has_rdoc: true
|
35
|
+
homepage: http://github.com/seamusabshere/csv_bootstrap
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --charset=UTF-8
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.2.0
|
57
|
+
signing_key:
|
58
|
+
specification_version: 2
|
59
|
+
summary: TODO
|
60
|
+
test_files:
|
61
|
+
- test/csv_bootstrap_ext_test.rb
|
62
|
+
- test/csv_bootstrap_test.rb
|
63
|
+
- test/test_helper.rb
|