sheet_mapper 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ # http://about.travis-ci.org/docs/user/build-configuration/
2
+ before_script: "git submodule update --init"
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - rbx
8
+ notifications:
9
+ recipients:
10
+ - nesquena@gmail.com
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Nathan Esquenazi
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.md CHANGED
@@ -16,7 +16,7 @@ and then `require 'sheet_mapper'` and you are done!
16
16
 
17
17
  ## Usage
18
18
 
19
- First, define yourself an object mapper:
19
+ First, you describe how to map a spreadsheet into data rows with a sheet object mapper:
20
20
 
21
21
  ```ruby
22
22
  class SomeMapper < SheetMapper::Base
@@ -36,20 +36,65 @@ class SomeMapper < SheetMapper::Base
36
36
  end
37
37
  ```
38
38
 
39
- This describes the column mappings and transformations to turn a spreadsheet row into a ruby object. Then you can use
40
- a mapper within a worksheet collection:
39
+ The mapper describes the column mappings and transformations to turn a spreadsheet row into a ruby object. Then you can apply
40
+ a mapper to any worksheet (collection):
41
41
 
42
42
  ```ruby
43
- sheet = SheetMapper::Worksheet.new(:mapper => SomeMapper, :key => 'k', :login => 'u', :password => 'p')
43
+ # Login and access a particular spreadsheet by key
44
+ sheet = SheetMapper::Spreadsheet.new(:mapper=>SomeMapper, :key=>'k', :login => 'u', :password => 'p')
45
+ # Find a particular worksheet (collection) by title
44
46
  collection = sheet.find_collection_by_title('title')
47
+ # Iterate over the records within the worksheet
45
48
  records = collection.each do |record|
46
49
  p record.attributes
47
50
  # => { :foo => "...", :bar => false, ... }
48
51
  end
49
52
  ```
50
53
 
51
- You can then work with the objects within the collection and access their attributes.
54
+ You can then work with objects within the collection and access their attributes. You can also modify objects and
55
+ persist the changes back to the collection (worksheet):
56
+
57
+ ```ruby
58
+ # Fetch the second data row from the spreadsheet
59
+ record = collection.records[1]
60
+ record[:foo] = "other"
61
+ # Persist change of value to worksheet
62
+ collection.save
63
+ # or more explicitly collection.save(record)
64
+ ```
65
+
66
+ If you want to reset changes made to your records, just use the reload method:
67
+
68
+ ```ruby
69
+ # Fetch the second data row from the spreadsheet
70
+ record = collection.records[1]
71
+ record[:foo] = "other"
72
+ # Reset unsaved changes
73
+ collection.reload
74
+ ```
75
+
76
+ You may also come across situations where you need access to 'meta' information associated with the collection.
77
+ Use the 'cell' method to access arbitrary data points:
78
+
79
+ ```ruby
80
+ # Accesses row 1, column 2 within the worksheet
81
+ collection.cell(1, 2) => "foo"
82
+ ```
52
83
 
53
84
  ## Contributors
54
85
 
55
- SheetMapper was created by [Nathan Esquenazi](http://github.com/nesquena) at Miso in 2012.
86
+ SheetMapper was created by [Nathan Esquenazi](http://github.com/nesquena) at Miso in 2012.
87
+
88
+ ## Tasks
89
+
90
+ SheetMapper is a new gem and I would love any feedback and/or pull requests.
91
+
92
+ ## Continuous Integration ##
93
+
94
+ [![Continuous Integration status](https://secure.travis-ci.org/nesquena/sheet_mapper.png)](http://travis-ci.org/nesquena/sheet_mapper)
95
+
96
+ CI is hosted by [travis-ci.org](http://travis-ci.org).
97
+
98
+ ## License
99
+
100
+ Check `LICENSE` but of course feel free to use this in any projects.
data/Rakefile CHANGED
@@ -5,4 +5,7 @@ Rake::TestTask.new(:test) do |test|
5
5
  test.libs << 'lib' << 'test'
6
6
  test.pattern = 'test/*_test.rb'
7
7
  test.ruby_opts = ['-rubygems']
8
- end
8
+ end
9
+
10
+ desc "Run tests for rabl"
11
+ task :default => :test
data/example.rb CHANGED
@@ -1,53 +1,36 @@
1
- =begin
2
-
3
- class BubbleMapper < SheetMapper::Base
4
- attributes :offset_seconds, :body, :link_url, :category
5
- end
6
-
7
- sheet = SheetMapper::Worksheet.new(:mapper => BubbleMapper, :key => 'sheet_key', :login => 'user', :password => 'pass')
8
- collection = sheet.find_collection_by_title('title')
9
- bubbles = collection.each do |bubble|
10
- p bubble.to_hash
11
- end
12
-
13
- =end
14
-
15
1
  require 'rubygems'
16
2
  require 'active_support/all'
17
3
  require 'google_spreadsheet'
18
4
  require File.expand_path('lib/sheet_mapper')
19
5
 
20
- class BubbleMapper < SheetMapper::Base
21
- columns :offset_seconds, :is_notable, :category, :body, :image_url, :link_text, :link_url
6
+ class GradeMapper < SheetMapper::Base
7
+ columns :topic, :grade, :score
22
8
 
23
9
  def valid_row?
24
- self[:body].present? && @pos > 7
25
- end
26
-
27
- def offset_seconds
28
- return unless self[:offset_seconds].strip =~ /^[\d\:]+$/ # Only return offset if valid digits
29
- offset = self[:offset_seconds].strip.split(':')
30
- (offset[0].to_i * 60) + offset[1].to_i
10
+ self[:topic].present? && @pos > 4
31
11
  end
32
12
 
33
13
  # Convert is_notable to boolean
34
- def is_notable
35
- self[:is_notable].to_s.match(/true/i).present?
14
+ def score
15
+ self[:score].to_i
36
16
  end
37
17
  end
38
18
 
39
- sheet = SheetMapper::Spreadsheet.new(:mapper => BubbleMapper, :key => ENV['SHEET_KEY'], :login => ENV['SHEET_LOGIN'], :password => ENV['SHEET_PASS'])
40
- collection = sheet.find_collection_by_title('s2e5')
19
+ sheet = SheetMapper::Spreadsheet.new(:mapper => GradeMapper, :key => ENV['SHEET_KEY'], :login => ENV['SHEET_LOGIN'], :password => ENV['SHEET_PASS'])
20
+ collection = sheet.find_collection_by_title('data')
41
21
 
42
- media_id = collection.cell(2, 2)
43
- season_num = collection.cell(3, 2)
44
- episode_num = collection.cell(4, 2)
45
- user_id = collection.cell(5, 2)
46
- duration = collection.cell(6, 2)
22
+ name = collection.cell(1, 2)
23
+ age = collection.cell(2, 2)
47
24
 
48
- puts "Media: #{media_id}, User: #{user_id}"
25
+ puts "Name: #{name}, Age: #{age}"
49
26
 
50
27
  bubbles = collection.each do |bubble|
51
- p bubble.attributes
28
+ p bubble.attributes # => { :topic => "...", :grade => "...", :score => "..." }
52
29
  end
53
30
 
31
+ b = collection.records[1]
32
+ b[:grade] = "B"
33
+ b[:score] = 86
34
+
35
+ collection.save
36
+
@@ -1,7 +1,9 @@
1
1
  module SheetMapper
2
2
  class Base
3
3
 
4
- # SheetMapper::Base.new(0, ["foo", "bar"])
4
+ attr_reader :pos
5
+
6
+ # SheetMapper::Base.new(1, ["foo", "bar"])
5
7
  def initialize(pos, data=[])
6
8
  @pos = pos
7
9
  @data = data
@@ -39,6 +41,18 @@ module SheetMapper
39
41
  true
40
42
  end
41
43
 
44
+ # Returns an array of data values based on the order of the spreadsheet
45
+ # @record.to_a => ["Foo", "Bar", 15]
46
+ def attribute_values
47
+ self.column_order.inject([]) { |res, name| res << @attrs[name]; res }
48
+ end
49
+
50
+ # Returns true if a record has been modified from original state
51
+ # @record.changed? => true
52
+ def changed?
53
+ @data != self.attribute_values
54
+ end
55
+
42
56
  protected
43
57
 
44
58
  # column_order => [:offset_seconds, :body, :link_url, :category]
@@ -60,16 +74,15 @@ module SheetMapper
60
74
  # Process all columns into an attribute hash
61
75
  def process_data
62
76
  m = HashWithIndifferentAccess.new
63
- column_order.each { |name| m[name.to_s] = self.attribute_value(name) }
77
+ column_order.each { |name| m[name.to_s] = self.fetch_data_value(name) }
64
78
  m
65
79
  end
66
80
 
67
- # attribute_value(:body, 1, 1) => "Foo"
68
- # attribute_value(:image_url, 1, 3) => nil
69
- # attribute_value(:link_text, 2) => "Article"
70
- # Create a method "format_<name>" to transform the column value (or pass the value directly)
71
- # Column position defaults to matching named column in `column_order`
72
- def attribute_value(name)
81
+ # fetch_data_value(:body) => "Foo"
82
+ # fetch_data_value(:image_url) => nil
83
+ # fetch_data_value(:link_text) => "Article"
84
+ # Column position is found by matching named column in `column_order`
85
+ def fetch_data_value(name)
73
86
  val = @data[column_pos(name)]
74
87
  val = val.to_i if val && name.to_s =~ /_(id|num)/
75
88
  val
@@ -31,6 +31,20 @@ module SheetMapper
31
31
  @worksheet[row, col]
32
32
  end
33
33
 
34
+ # Saves the records that have changed back to spreadsheet
35
+ # @collection.save
36
+ # @collection.save(@record)
37
+ def save(records=@records)
38
+ Array(records).each { |r| @worksheet.update_cells(r.pos, 1, [r.attribute_values]) if r.changed? }
39
+ @worksheet.save
40
+ end
41
+
42
+ # Reload worksheet discarding changes not saved
43
+ def reload
44
+ @worksheet.reload
45
+ @records = process_records!
46
+ end
47
+
34
48
  protected
35
49
 
36
50
  # Converts all valid raw data hashes into mapped records
@@ -38,7 +52,7 @@ module SheetMapper
38
52
  def process_records!
39
53
  records = []
40
54
  @worksheet.rows.each_with_index do |record, index|
41
- record = @mapper.new(index, record)
55
+ record = @mapper.new(index + 1, record)
42
56
  records << record if record.valid_row?
43
57
  end
44
58
  records
@@ -1,3 +1,3 @@
1
1
  module SheetMapper
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/test/base_test.rb CHANGED
@@ -17,10 +17,15 @@ describe "Base" do
17
17
  @data = ["Bob", 21, "Red"]
18
18
  end
19
19
 
20
- should "support columns accessor" do
20
+ should "support columns class accessor" do
21
21
  assert_equal [:name, :age, :color], TestBase.columns
22
22
  end
23
23
 
24
+ should "support pos instances accessor" do
25
+ @test = TestBase.new(1, @data)
26
+ assert_equal 1, @test.pos
27
+ end
28
+
24
29
  context "for attributes method" do
25
30
  setup do
26
31
  @test = TestBase.new(1, @data)
@@ -53,4 +58,31 @@ describe "Base" do
53
58
  assert_equal 45, @test[:age]
54
59
  end
55
60
  end # assign []=
61
+
62
+ context "for attribute_values method" do
63
+ setup do
64
+ @test = TestBase.new(1, @data)
65
+ @test[:age] = 45
66
+ end
67
+
68
+ should "return attribute values in proper order as list" do
69
+ assert_equal ["Bob", 45, "Red"], @test.attribute_values
70
+ end
71
+ end # attribute_values
72
+
73
+ context "for changed? method" do
74
+ setup do
75
+ @test = TestBase.new(1, @data)
76
+ end
77
+
78
+ should "return false if not changed" do
79
+ @test[:age] = 21
80
+ assert_equal false, @test.changed?
81
+ end
82
+
83
+ should "return true if changed" do
84
+ @test[:age] = 45
85
+ assert_equal true, @test.changed?
86
+ end
87
+ end # attribute_values
56
88
  end # Base
@@ -12,6 +12,9 @@ class TestMapper
12
12
  def name; record[:name]; end
13
13
  def age; record[:age]; end
14
14
  def valid_row?; true; end
15
+ def pos; 3; end
16
+ def attribute_values; tuple; end
17
+ def changed?; true; end
15
18
  end # TestMapper
16
19
 
17
20
  describe "Collection" do
@@ -82,4 +85,35 @@ describe "Collection" do
82
85
  assert_equal ["Joey", 67], rows[2].tuple
83
86
  end
84
87
  end # records
88
+
89
+ context "for save method" do
90
+ setup do
91
+ @collection = SheetMapper::Collection.new(@spreadsheet, @worksheet)
92
+ @rows = @collection.records
93
+ end
94
+
95
+ should "persist back to worksheet all rows" do
96
+ @rows.each { |r| @worksheet.expects(:update_cells).with(r.pos, 1, [r.attribute_values]) }
97
+ @worksheet.expects(:save).once
98
+ @collection.save
99
+ end
100
+
101
+ should "persist back to worksheet one row" do
102
+ first_row = @collection.records.first
103
+ @worksheet.expects(:update_cells).with(first_row.pos, 1, [first_row.attribute_values])
104
+ @worksheet.expects(:save).once
105
+ @collection.save(first_row)
106
+ end
107
+ end # save
108
+
109
+ context "for reload method" do
110
+ setup do
111
+ @collection = SheetMapper::Collection.new(@spreadsheet, @worksheet)
112
+ end
113
+
114
+ should "persist back to worksheet" do
115
+ @worksheet.expects(:reload).once
116
+ @collection.reload
117
+ end
118
+ end # reload
85
119
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sheet_mapper
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 0
9
8
  - 1
10
- version: 0.0.1
9
+ - 0
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nathan Esquenazi
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-05-15 00:00:00 -07:00
19
- default_executable:
18
+ date: 2012-05-20 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: google-spreadsheet-ruby
@@ -117,11 +116,12 @@ extra_rdoc_files: []
117
116
 
118
117
  files:
119
118
  - .gitignore
119
+ - .travis.yml
120
120
  - Gemfile
121
+ - LICENSE
121
122
  - README.md
122
123
  - Rakefile
123
124
  - example.rb
124
- - example2.rb
125
125
  - lib/core_ext/hash_ext.rb
126
126
  - lib/core_ext/object_ext.rb
127
127
  - lib/sheet_mapper.rb
@@ -135,7 +135,6 @@ files:
135
135
  - test/spreadsheet_test.rb
136
136
  - test/test_config.rb
137
137
  - test/test_helpers/mini_shoulda.rb
138
- has_rdoc: true
139
138
  homepage: ""
140
139
  licenses: []
141
140
 
@@ -165,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
164
  requirements: []
166
165
 
167
166
  rubyforge_project: sheet_mapper
168
- rubygems_version: 1.6.2
167
+ rubygems_version: 1.8.21
169
168
  signing_key:
170
169
  specification_version: 3
171
170
  summary: Map google spreadsheets to ruby objects
data/example2.rb DELETED
@@ -1,30 +0,0 @@
1
- require 'rubygems'
2
- require 'active_support/all'
3
- require 'google_spreadsheet'
4
- require File.expand_path('lib/sheet_mapper')
5
-
6
- class GradeMapper < SheetMapper::Base
7
- columns :topic, :grade, :score
8
-
9
- def valid_row?
10
- self[:topic].present? && @pos > 3
11
- end
12
-
13
- # Convert is_notable to boolean
14
- def score
15
- self[:score].to_i
16
- end
17
- end
18
-
19
- sheet = SheetMapper::Spreadsheet.new(:mapper => GradeMapper, :key => ENV['SHEET_KEY'], :login => ENV['SHEET_LOGIN'], :password => ENV['SHEET_PASS'])
20
- collection = sheet.find_collection_by_title('data')
21
-
22
- name = collection.cell(1, 2)
23
- age = collection.cell(2, 2)
24
-
25
- puts "Name: #{name}, Age: #{age}"
26
-
27
- bubbles = collection.each do |bubble|
28
- p bubble.attributes
29
- end
30
-