rose 0.0.5

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.
@@ -0,0 +1,75 @@
1
+ require 'rose/proxy'
2
+
3
+ module Rose
4
+ # Defines the Rose DSL
5
+ class Seedling
6
+ # @return [Rose::Proxy::Row] the row proxy containing an array of Attributes
7
+ attr_reader :row
8
+
9
+ # @return [Rose::Proxy::Root] the root proxy containing attribute finder and updater
10
+ attr_reader :root
11
+
12
+ # @return [ObjectAdapter] the adapter
13
+ attr_reader :adapter
14
+
15
+ # @return [Hash] the options used by the adapter
16
+ attr_reader :options
17
+
18
+ # @return [Array] the alterations to be applied to the sprouted Seedling (the report)
19
+ attr_reader :alterations
20
+
21
+ # @param [ObjectAdapter] adapter An ObjectAdapter capable of sprouting a Seedling
22
+ # (running the report)
23
+ # @param [Hash] options
24
+ # @option options [Class] :class (nil) Used during by the adapter to enforce items types
25
+ def initialize(adapter, options={})
26
+ @adapter = adapter
27
+ @options = options
28
+ @alterations = @options[:alterations] = []
29
+ end
30
+
31
+ # @yield Proxy::Row
32
+ def rows(&blk)
33
+ proxy = Proxy::Row.new
34
+ proxy.instance_eval(&blk)
35
+ @row = proxy
36
+ end
37
+
38
+ # @param [String, Symbol] column_name the column to sort by
39
+ # @param [:ascending, :descending] order the order to sort by
40
+ def sort(column_name, order = :ascending, &sort_block)
41
+ @options[:sort] = Attribute::Sort.new(column_name, order, &sort_block)
42
+ @alterations << @options[:sort]
43
+ end
44
+
45
+ # @yield Rose::Attribute::Filter
46
+ def filter(&filter_block)
47
+ @options[:filter] = Attribute::Filter.new(nil, nil, filter_block)
48
+ @alterations << @options[:filter]
49
+ end
50
+
51
+ # @param [String, Symbol] column_name the column to group by
52
+ # @yield Proxy::Summary
53
+ def summary(column_name, &blk)
54
+ proxy = Proxy::Summary.new(column_name)
55
+ proxy.instance_eval(&blk)
56
+ @options[:summary] = proxy
57
+ @alterations << @options[:summary]
58
+ end
59
+
60
+ # @param [String, Symbol] group_column the column to use for row data
61
+ # @param [String, Symbol] pivot_column the column to use for column data
62
+ # @param [Proc] value_block the block used to evalue the value data
63
+ def pivot(group_column, pivot_column, &value_block)
64
+ @options[:pivot] = Attribute::Pivot.new(group_column, pivot_column, value_block)
65
+ @alterations << @options[:pivot]
66
+ end
67
+
68
+ # @yield Proxy::Root
69
+ def roots(&blk)
70
+ proxy = Proxy::Root.new
71
+ proxy.instance_eval(&blk)
72
+ @root = proxy
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,40 @@
1
+ module Rose
2
+ # This class is a wrapper around Seedling that provides methods
3
+ # we don't want invoked within the Rose#make DSL.
4
+ #
5
+ # A seedling with a shell is still a seedling. Hence, it should
6
+ # be have as such!
7
+ class Shell
8
+ # @return [Rose::Seedling] the current seedling
9
+ attr_reader :seedling
10
+
11
+ def initialize(seedling)
12
+ @seedling = seedling
13
+ end
14
+
15
+ # Provides bulk exporting
16
+ # @param [Array] items the items to sprout the seedling with
17
+ # @return [Ruport::Data::RoseTable] the resulting table
18
+ def bloom(items=[], options={})
19
+ @seedling.adapter.sprout(@seedling, @seedling.options.merge(options).merge(
20
+ :attributes => @seedling.row.attributes,
21
+ :items => items
22
+ ))
23
+ end
24
+
25
+ # Provides bulk importing
26
+ # @param [Hash] options
27
+ # @option options [Hash,String] :with (required) a Hash of identity (id) => attribute pairs, or a String to a CSV file to update the seedling with
28
+ # @option options [true, false] :preview (false) whether or not to use the previewer
29
+ def photosynthesize(items=[], options={})
30
+ @seedling.adapter.osmosis(@seedling, @seedling.options.merge(options).merge(
31
+ :items => items
32
+ ))
33
+ end
34
+
35
+ # Delegates methods to the current seedling
36
+ def method_missing(*args, &blk)
37
+ @seedling.send(*args, &blk)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,86 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rose}
8
+ s.version = "0.0.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Henry Hsu"]
12
+ s.date = %q{2010-07-07}
13
+ s.description = %q{A slick Ruby DSL for reporting.}
14
+ s.email = %q{henry@qlane.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".autotest",
21
+ ".document",
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.markdown",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "features/rose.feature",
28
+ "features/step_definitions/rose_steps.rb",
29
+ "features/support/env.rb",
30
+ "lib/rose.rb",
31
+ "lib/rose/active_record.rb",
32
+ "lib/rose/attribute.rb",
33
+ "lib/rose/core_extensions.rb",
34
+ "lib/rose/object.rb",
35
+ "lib/rose/proxy.rb",
36
+ "lib/rose/ruport.rb",
37
+ "lib/rose/seedling.rb",
38
+ "lib/rose/shell.rb",
39
+ "rose.gemspec",
40
+ "spec/core_extensions_spec.rb",
41
+ "spec/db/schema.rb",
42
+ "spec/examples/update_flowers.csv",
43
+ "spec/examples/update_posts.csv",
44
+ "spec/rose/active_record_spec.rb",
45
+ "spec/rose/object_spec.rb",
46
+ "spec/rose_spec.rb",
47
+ "spec/spec.opts",
48
+ "spec/spec_helper.rb"
49
+ ]
50
+ s.homepage = %q{http://github.com/hsume2/rose}
51
+ s.rdoc_options = ["--charset=UTF-8"]
52
+ s.require_paths = ["lib"]
53
+ s.rubygems_version = %q{1.3.7}
54
+ s.summary = %q{Reporting like a spring rose, rows and rows of it}
55
+ s.test_files = [
56
+ "spec/core_extensions_spec.rb",
57
+ "spec/db/schema.rb",
58
+ "spec/rose/active_record_spec.rb",
59
+ "spec/rose/object_spec.rb",
60
+ "spec/rose_spec.rb",
61
+ "spec/spec_helper.rb"
62
+ ]
63
+
64
+ if s.respond_to? :specification_version then
65
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
66
+ s.specification_version = 3
67
+
68
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69
+ s.add_runtime_dependency(%q<ruport>, [">= 1.6.3"])
70
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
71
+ s.add_development_dependency(%q<yard>, [">= 0"])
72
+ s.add_development_dependency(%q<cucumber>, [">= 0"])
73
+ else
74
+ s.add_dependency(%q<ruport>, [">= 1.6.3"])
75
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
76
+ s.add_dependency(%q<yard>, [">= 0"])
77
+ s.add_dependency(%q<cucumber>, [">= 0"])
78
+ end
79
+ else
80
+ s.add_dependency(%q<ruport>, [">= 1.6.3"])
81
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
82
+ s.add_dependency(%q<yard>, [">= 0"])
83
+ s.add_dependency(%q<cucumber>, [">= 0"])
84
+ end
85
+ end
86
+
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ class MyObject
4
+ include Rose::CoreExtensions
5
+ end
6
+
7
+ describe "CoreExtensions" do
8
+ before do
9
+ @o = MyObject.new
10
+ end
11
+
12
+ describe "#require_keys" do
13
+ it "should raise exception if missing required key" do
14
+ lambda {
15
+ @o.require_keys({:name => 'foo'}, :value)
16
+ }.should raise_error(ArgumentError, "Missing required key(s): value")
17
+ end
18
+
19
+ it "should not raise exception if not missing required key" do
20
+ lambda {
21
+ @o.require_keys({:name => 'foo', :value => 'bar'}, :value)
22
+ }.should_not raise_error
23
+ end
24
+
25
+ it "should raise exception if missing required keys" do
26
+ lambda {
27
+ @o.require_keys({}, :name, :value)
28
+ }.should raise_error(ArgumentError, "Missing required key(s): name, value")
29
+ end
30
+
31
+ it "should not raise exception if not missing required keys" do
32
+ lambda {
33
+ @o.require_keys({:name => 'foo', :value => 'bar'}, :name, :value)
34
+ }.should_not raise_error
35
+ end
36
+ end
37
+
38
+ describe "#required_values" do
39
+ it "should return values of required keys" do
40
+ @o.expects(:require_keys).with({:name => 'foo', :value => 'bar'}, :name, :value)
41
+ foo, bar = @o.required_values({:name => 'foo', :value => 'bar'}, :name, :value)
42
+ foo.should == 'foo'
43
+ bar.should == 'bar'
44
+ end
45
+
46
+ it "should return value of required key" do
47
+ @o.expects(:require_keys).with({:name => 'foo', :value => 'bar'}, :name)
48
+ foo = @o.required_values({:name => 'foo', :value => 'bar'}, :name)
49
+ foo.should == 'foo'
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :people, :force => true do |t|
3
+ t.column :name, :string
4
+ t.column :type, :string
5
+ t.column :password, :string
6
+ t.column :admin, :boolean, :default => false
7
+ end
8
+
9
+ create_table :posts, :force => true do |t|
10
+ t.column :guid, :string
11
+ t.column :title, :string
12
+ t.column :body, :text
13
+ t.column :published, :boolean, :default => true
14
+ end
15
+
16
+ create_table :comments, :force => true do |t|
17
+ t.column :post_id, :integer
18
+ t.column :author_id, :integer
19
+ t.column :body, :text
20
+ end
21
+
22
+ create_table :subjects, :force => true do |t|
23
+ t.column :name, :string
24
+ end
25
+
26
+ create_table :tests, :force => true do |t|
27
+ t.column :student_id, :integer
28
+ t.column :subject_id, :integer
29
+ t.column :score, :decimal
30
+ t.timestamps
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ ID,Type,Color,Age
2
+ 0,roses,blue,1
3
+ 1,violets,red,2
4
+ 2,roses,green,3
@@ -0,0 +1,5 @@
1
+ ID,Title,Comments
2
+ P1,Post #1,2
3
+ P2,Post #2,1
4
+ P3,Third Post,0
5
+ P4,Fourth Post,0
@@ -0,0 +1,434 @@
1
+ require 'spec_helper'
2
+ require 'rose/active_record'
3
+
4
+ module RoseActiveRecordSpecs
5
+ class Person < ActiveRecord::Base
6
+ attr_protected :password
7
+ end
8
+
9
+ class Admin < Person
10
+ end
11
+
12
+ class Post < ActiveRecord::Base
13
+ has_many :comments
14
+ end
15
+
16
+ class Comment < ActiveRecord::Base
17
+ belongs_to :post
18
+ belongs_to :author, :class_name => "Person"
19
+ end
20
+
21
+ class Subject < ActiveRecord::Base
22
+ has_many :tests
23
+ end
24
+
25
+ class Test < ActiveRecord::Base
26
+ belongs_to :subject
27
+ belongs_to :student, :class_name => "Person"
28
+ end
29
+
30
+ describe Rose, "ActiveRecord adapter" do
31
+ before(:suite) do
32
+ # ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/log/test.log")
33
+ ActiveRecord::Migration.verbose = false
34
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
35
+ load(File.dirname(__FILE__) + "/../db/schema.rb")
36
+
37
+ person_1 = Person.create(:name => "Person #1")
38
+ person_2 = Person.create(:name => "Person #2")
39
+
40
+ post_1 = Post.create(:title => "Post #1", :guid => "P1")
41
+ post_1.comments.create(:author => person_1)
42
+ post_1.comments.create(:author => person_2)
43
+ post_2 = Post.create(:title => "Post #2", :guid => "P2")
44
+ post_2.comments.create(:author => person_2)
45
+ end
46
+
47
+ describe "make report" do
48
+ before do
49
+ Post.rose(:post_comments) do
50
+ rows do
51
+ column("Post", &:title)
52
+ column("Comments") { |item| item.comments.size }
53
+ end
54
+ end
55
+
56
+ Post.rose(:post_comments_sorted_asc) do
57
+ rows do
58
+ column("Post", &:title)
59
+ column("Comments") { |item| item.comments.size }
60
+ end
61
+ sort("Comments", :ascending)
62
+ end
63
+
64
+ Post.rose(:post_comments_sorted_desc) do
65
+ rows do
66
+ column("Post", &:title)
67
+ column("Comments") { |item| item.comments.size }
68
+ end
69
+ sort("Comments", :descending)
70
+ end
71
+ end
72
+
73
+ it "should report on posts' comments" do
74
+ Post.rose_for(:post_comments).should match_table <<-eo_table.gsub(%r{^ }, '')
75
+ +--------------------+
76
+ | Post | Comments |
77
+ +--------------------+
78
+ | Post #1 | 2 |
79
+ | Post #2 | 1 |
80
+ +--------------------+
81
+ eo_table
82
+ end
83
+
84
+ it "should order by comments size" do
85
+ Post.rose_for(:post_comments_sorted_asc).should match_table <<-eo_table.gsub(%r{^ }, '')
86
+ +--------------------+
87
+ | Post | Comments |
88
+ +--------------------+
89
+ | Post #2 | 1 |
90
+ | Post #1 | 2 |
91
+ +--------------------+
92
+ eo_table
93
+
94
+ Post.rose_for(:post_comments_sorted_desc).should match_table <<-eo_table.gsub(%r{^ }, '')
95
+ +--------------------+
96
+ | Post | Comments |
97
+ +--------------------+
98
+ | Post #1 | 2 |
99
+ | Post #2 | 1 |
100
+ +--------------------+
101
+ eo_table
102
+ end
103
+
104
+ it "should report only posts with #2" do
105
+ Post.rose_for(:post_comments, :conditions => ["title like ?", "%#2%"]).should match_table <<-eo_table.gsub(%r{^ }, '')
106
+ +--------------------+
107
+ | Post | Comments |
108
+ +--------------------+
109
+ | Post #2 | 1 |
110
+ +--------------------+
111
+ eo_table
112
+ end
113
+ end
114
+
115
+ describe "make report with sigma" do
116
+ before do
117
+ Comment.rose(:author_comments) do
118
+ rows do
119
+ column("Author") { |item| item.author.name }
120
+ column("Posts") { |item| item.post.title }
121
+ column("Comments") { |item| true }
122
+ end
123
+ summary("Author") do
124
+ column("Posts") { |posts| posts.join(", ") }
125
+ column("Comments") { |comments| comments.size }
126
+ end
127
+ end
128
+ end
129
+
130
+ it "should get authors and their comments" do
131
+ Comment.rose_for(:author_comments).should match_table <<-eo_table.gsub(%r{^ }, '')
132
+ +-----------------------------------------+
133
+ | Author | Posts | Comments |
134
+ +-----------------------------------------+
135
+ | Person #1 | Post #1 | 1 |
136
+ | Person #2 | Post #1, Post #2 | 2 |
137
+ +-----------------------------------------+
138
+ eo_table
139
+ end
140
+ end
141
+
142
+ describe "make report with pivot" do
143
+ it "should make report with pivot" do
144
+ elisa = Person.create(:name => "Elisa")
145
+ mary = Person.create(:name => "Mary")
146
+ english = Subject.create(:name => "English")
147
+ math = Subject.create(:name => "Math")
148
+ science = Subject.create(:name => "Science")
149
+ art = Subject.create(:name => "Art")
150
+ history = Subject.create(:name => "History")
151
+ french = Subject.create(:name => "French")
152
+
153
+ Test.create(:student => elisa, :subject => english, :score => 87, :created_at => Date.parse('January 2010'))
154
+ Test.create(:student => elisa, :subject => math, :score => 65, :created_at => Date.parse('January 2010'))
155
+ Test.create(:student => elisa, :subject => science, :score => 58, :created_at => Date.parse('January 2010'))
156
+ Test.create(:student => elisa, :subject => art, :score => 89, :created_at => Date.parse('January 2010'))
157
+ Test.create(:student => elisa, :subject => history, :score => 81, :created_at => Date.parse('January 2010'))
158
+ Test.create(:student => elisa, :subject => french, :score => 62, :created_at => Date.parse('January 2010'))
159
+
160
+ Test.create(:student => elisa, :subject => english, :score => 51, :created_at => Date.parse('February 2010'))
161
+ Test.create(:student => elisa, :subject => math, :score => 72, :created_at => Date.parse('February 2010'))
162
+ Test.create(:student => elisa, :subject => science, :score => 89, :created_at => Date.parse('February 2010'))
163
+ Test.create(:student => elisa, :subject => art, :score => 83, :created_at => Date.parse('February 2010'))
164
+ Test.create(:student => elisa, :subject => history, :score => 84, :created_at => Date.parse('February 2010'))
165
+ Test.create(:student => elisa, :subject => french, :score => 57, :created_at => Date.parse('February 2010'))
166
+
167
+ Test.create(:student => elisa, :subject => english, :score => 41, :created_at => Date.parse('March 2010'))
168
+ Test.create(:student => elisa, :subject => math, :score => 71, :created_at => Date.parse('March 2010'))
169
+ Test.create(:student => elisa, :subject => science, :score => 41, :created_at => Date.parse('March 2010'))
170
+ Test.create(:student => elisa, :subject => art, :score => 92, :created_at => Date.parse('March 2010'))
171
+ Test.create(:student => elisa, :subject => history, :score => 91, :created_at => Date.parse('March 2010'))
172
+ Test.create(:student => elisa, :subject => french, :score => 56, :created_at => Date.parse('March 2010'))
173
+
174
+ Test.create(:student => mary, :subject => english, :score => 87, :created_at => Date.parse('January 2010'))
175
+ Test.create(:student => mary, :subject => math, :score => 53, :created_at => Date.parse('January 2010'))
176
+ Test.create(:student => mary, :subject => science, :score => 35, :created_at => Date.parse('January 2010'))
177
+ Test.create(:student => mary, :subject => art, :score => 61, :created_at => Date.parse('January 2010'))
178
+ Test.create(:student => mary, :subject => history, :score => 58, :created_at => Date.parse('January 2010'))
179
+ Test.create(:student => mary, :subject => french, :score => 92, :created_at => Date.parse('January 2010'))
180
+
181
+ Test.create(:student => mary, :subject => english, :score => 68, :created_at => Date.parse('February 2010'))
182
+ Test.create(:student => mary, :subject => math, :score => 54, :created_at => Date.parse('February 2010'))
183
+ Test.create(:student => mary, :subject => science, :score => 56, :created_at => Date.parse('February 2010'))
184
+ Test.create(:student => mary, :subject => art, :score => 59, :created_at => Date.parse('February 2010'))
185
+ Test.create(:student => mary, :subject => history, :score => 61, :created_at => Date.parse('February 2010'))
186
+ Test.create(:student => mary, :subject => french, :score => 93, :created_at => Date.parse('February 2010'))
187
+
188
+ Test.create(:student => mary, :subject => english, :score => 41, :created_at => Date.parse('March 2010'))
189
+ Test.create(:student => mary, :subject => math, :score => 35, :created_at => Date.parse('March 2010'))
190
+ Test.create(:student => mary, :subject => science, :score => 41, :created_at => Date.parse('March 2010'))
191
+ Test.create(:student => mary, :subject => art, :score => 48, :created_at => Date.parse('March 2010'))
192
+ Test.create(:student => mary, :subject => history, :score => 67, :created_at => Date.parse('March 2010'))
193
+ Test.create(:student => mary, :subject => french, :score => 90, :created_at => Date.parse('March 2010'))
194
+
195
+ Test.rose(:scores) do
196
+ rows do
197
+ column("Month") { |test| I18n.l(test.created_at, :format => :short) }
198
+ column("Subject") { |test| test.subject.name }
199
+ column("Student") { |test| test.student.name }
200
+ column(:score => "Score")
201
+ end
202
+ pivot("Month", "Subject") do |matching_rows|
203
+ matching_rows.map(&:Score).map(&:to_f).sum
204
+ end
205
+ end
206
+
207
+ Test.rose_for(:scores).should match_table <<-eo_table.gsub(%r{^ }, '')
208
+ +---------------------------------------------------------------------+
209
+ | Month | Art | Science | English | French | Math | History |
210
+ +---------------------------------------------------------------------+
211
+ | 01 Jan 00:00 | 150.0 | 93.0 | 174.0 | 154.0 | 118.0 | 139.0 |
212
+ | 01 Feb 00:00 | 142.0 | 145.0 | 119.0 | 150.0 | 126.0 | 145.0 |
213
+ | 01 Mar 00:00 | 140.0 | 82.0 | 82.0 | 146.0 | 106.0 | 158.0 |
214
+ +---------------------------------------------------------------------+
215
+ eo_table
216
+ end
217
+ end
218
+
219
+ describe "run report" do
220
+ before do
221
+ Comment.rose(:author_comments) do
222
+ rows do
223
+ column("Author") { |item| item.author.name }
224
+ column("Posts") { |item| item.post.title }
225
+ column("Comments") { |item| item.destroy }
226
+ end
227
+ end
228
+ end
229
+
230
+ it "should run report within transaction" do
231
+ all_sizes = lambda { [Person.count, Post.count, Comment.count] }
232
+
233
+ lambda {
234
+ Comment.rose_for(:author_comments)
235
+ }.should_not change(all_sizes, :call)
236
+ end
237
+ end
238
+
239
+ describe "import report" do
240
+ before do
241
+ Post.rose(:with_update) {
242
+ rows do
243
+ identity(:guid => "ID")
244
+ column("Title", &:title)
245
+ column("Comments") { |item| item.comments.size }
246
+ end
247
+
248
+ sort("Comments", :descending)
249
+
250
+ roots do
251
+ find do |items, idy|
252
+ items.find { |item| item.guid == idy }
253
+ end
254
+ update do |record, updates|
255
+ record.update_attribute(:title, updates["Title"])
256
+ end
257
+ end
258
+ }
259
+
260
+ @post_3 = Post.create(:title => "Post #3", :guid => "P3")
261
+ @post_4 = Post.create(:title => "Post #4", :guid => "P4")
262
+ end
263
+
264
+ after do
265
+ @post_3.destroy; @post_4.destroy
266
+ end
267
+
268
+ it "should update report" do
269
+ Post.root_for(:with_update, {
270
+ :with => {
271
+ "P3" => { "Title" => "Third Post", "something" => "else" },
272
+ "P4" => { "Title" => "Fourth Post" }
273
+ }
274
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
275
+ +-----------------------------+
276
+ | ID | Title | Comments |
277
+ +-----------------------------+
278
+ | P1 | Post #1 | 2 |
279
+ | P2 | Post #2 | 1 |
280
+ | P4 | Fourth Post | 0 |
281
+ | P3 | Third Post | 0 |
282
+ +-----------------------------+
283
+ eo_table
284
+
285
+ Post.all.map(&:title).should == ["Post #1", "Post #2", "Third Post", "Fourth Post"]
286
+ end
287
+
288
+ it "should update report with conditions" do
289
+ Post.root_for(:with_update, {
290
+ :with => {
291
+ "P3" => { "Title" => "Third Post", "something" => "else" },
292
+ "P4" => { "Title" => "Fourth Post" }
293
+ }
294
+ }, { :conditions => ["title like ?", "%#3%"] }).should match_table <<-eo_table.gsub(%r{^ }, '')
295
+ +----------------------------+
296
+ | ID | Title | Comments |
297
+ +----------------------------+
298
+ | P3 | Third Post | 0 |
299
+ +----------------------------+
300
+ eo_table
301
+
302
+ Post.all.map(&:title).should == ["Post #1", "Post #2", "Third Post", "Post #4"]
303
+ end
304
+
305
+ it "should update report from CSV" do
306
+ Post.root_for(:with_update, {
307
+ :with => "spec/examples/update_posts.csv"
308
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
309
+ +-----------------------------+
310
+ | ID | Title | Comments |
311
+ +-----------------------------+
312
+ | P1 | Post #1 | 2 |
313
+ | P2 | Post #2 | 1 |
314
+ | P4 | Fourth Post | 0 |
315
+ | P3 | Third Post | 0 |
316
+ +-----------------------------+
317
+ eo_table
318
+
319
+ Post.all.map(&:title).should == ["Post #1", "Post #2", "Third Post", "Fourth Post"]
320
+ end
321
+ end
322
+
323
+ describe "preview import report" do
324
+ before do
325
+ Post.rose(:for_import) {
326
+ rows do
327
+ identity(:guid => "ID")
328
+ column("Title", &:title)
329
+ column("Comments") { |item| item.comments.size }
330
+ end
331
+
332
+ sort("Comments", :descending)
333
+
334
+ roots do
335
+ find do |items, idy|
336
+ items.find { |item| item.guid == idy }
337
+ end
338
+ preview_update do |record, updates|
339
+ record.title = updates["Title"]
340
+ end
341
+ update { raise Exception, "not me!" }
342
+ end
343
+ }
344
+ end
345
+
346
+ it "should preview changes" do
347
+ Post.root_for(:for_import, {
348
+ :with => {
349
+ "P1" => { "Title" => "First Post", "something" => "else" },
350
+ "P2" => { "Title" => "Second Post" }
351
+ },
352
+ :preview => true
353
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
354
+ +-----------------------------+
355
+ | ID | Title | Comments |
356
+ +-----------------------------+
357
+ | P1 | First Post | 2 |
358
+ | P2 | Second Post | 1 |
359
+ +-----------------------------+
360
+ eo_table
361
+
362
+ Post.all.map(&:title).should == ["Post #1", "Post #2"]
363
+ end
364
+ end
365
+
366
+ describe "create import report" do
367
+ before do
368
+ Post.rose(:for_create) {
369
+ rows do
370
+ identity(:guid => "ID")
371
+ column("Title", &:title)
372
+ column("Comments") { |item| item.comments.size }
373
+ end
374
+
375
+ sort("Comments", :descending)
376
+
377
+ roots do
378
+ find do |items, idy|
379
+ items.find { |item| item.guid == idy }
380
+ end
381
+ preview_create do |idy, updates|
382
+ post = Post.new(:guid => idy)
383
+ post.title = updates["Title"]
384
+ post
385
+ end
386
+ create do |idy, updates|
387
+ post = create_previewer.call(idy, updates)
388
+ post.save!
389
+ post
390
+ end
391
+ end
392
+ }
393
+ end
394
+
395
+ it "should preview create" do
396
+ Post.root_for(:for_create, {
397
+ :with => {
398
+ "P3" => { "Title" => "Post #3" }
399
+ },
400
+ :preview => true
401
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
402
+ +-------------------------+
403
+ | ID | Title | Comments |
404
+ +-------------------------+
405
+ | P1 | Post #1 | 2 |
406
+ | P2 | Post #2 | 1 |
407
+ | P3 | Post #3 | 0 |
408
+ +-------------------------+
409
+ eo_table
410
+ end
411
+
412
+ it "should create" do
413
+ Post.root_for(:for_create, {
414
+ :with => {
415
+ "P3" => { "Title" => "Post #3" }
416
+ }
417
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
418
+ +-------------------------+
419
+ | ID | Title | Comments |
420
+ +-------------------------+
421
+ | P1 | Post #1 | 2 |
422
+ | P2 | Post #2 | 1 |
423
+ | P3 | Post #3 | 0 |
424
+ +-------------------------+
425
+ eo_table
426
+
427
+ Post.last.title.should == "Post #3"
428
+
429
+ Post.last.destroy
430
+ end
431
+ end
432
+ end
433
+
434
+ end