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 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,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
@@ -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
@@ -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