vibedeck-comma 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ # Include dependencies from gemspec.
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ comma (0.4.1)
5
+ activesupport (>= 2.2.2)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activesupport (3.1.0)
11
+ multi_json (~> 1.0)
12
+ diff-lcs (1.1.2)
13
+ multi_json (1.0.3)
14
+ rspec (2.0.1)
15
+ rspec-core (~> 2.0.1)
16
+ rspec-expectations (~> 2.0.1)
17
+ rspec-mocks (~> 2.0.1)
18
+ rspec-core (2.0.1)
19
+ rspec-expectations (2.0.1)
20
+ diff-lcs (>= 1.1.2)
21
+ rspec-mocks (2.0.1)
22
+ rspec-core (~> 2.0.1)
23
+ rspec-expectations (~> 2.0.1)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ comma!
30
+ rspec (>= 1.2.9)
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Marcus Crafter
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,218 @@
1
+ = COMMA
2
+
3
+ http://github.com/crafterm/comma
4
+
5
+ == DESCRIPTION:
6
+
7
+ Comma is a CSV (ie. comma separated values) generation extension for Ruby objects, that lets you seamlessly define a CSV output format via a small DSL. Comma works well on pure Ruby objects with attributes, as well as complex ones such as ActiveRecord objects with associations, extensions, etc. It doesn't distinguish between attributes, methods, associations, extensions, etc. - they all are considered equal and invoked identically via the Comma DSL description. Multiple different CSV output descriptions can also be defined.
8
+
9
+ When multiple objects in an Array are converted to CSV, the output includes generation of a header row reflected from names of the properties requested, or specified via the DSL.
10
+
11
+ CSV can be a bit of a boring format - the motivation behind Comma was to have a CSV extension that was simple, flexible, and would treat attributes, methods, associations, etc., all the same without the need for any complex configuration, and also work on Ruby objects, not just ActiveRecord or other base class derivatives.
12
+
13
+ An example Comma CSV enabled ActiveRecord class:
14
+
15
+ class Book < ActiveRecord::Base
16
+
17
+ # ================
18
+ # = Associations =
19
+ # ================
20
+ has_many :pages
21
+ has_one :isbn
22
+ belongs_to :publisher
23
+
24
+ # ===============
25
+ # = CSV support =
26
+ # ===============
27
+ comma do
28
+
29
+ name
30
+ description
31
+
32
+ pages :size => 'Pages'
33
+ publisher :name
34
+ isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'
35
+ blurb 'Summary'
36
+
37
+ end
38
+
39
+ end
40
+
41
+ Annotated, the comma description is as follows:
42
+
43
+ # starts a Comma description block, generating 2 methods #to_comma and
44
+ # #to_comma_headers for this class.
45
+ comma do
46
+
47
+ # name, description are attributes of Book with the header being reflected as
48
+ # 'Name', 'Description'
49
+ name
50
+ description
51
+
52
+ # pages is an association returning an array, :size is called on the
53
+ # association results, with the header name specified as 'Pages'
54
+ pages :size => 'Pages'
55
+
56
+ # publisher is an association returning an object, :name is called on the
57
+ # associated object, with the reflected header 'Name'
58
+ publisher :name
59
+
60
+ # isbn is an association returning an object, :number_10 and :number_13 are
61
+ # called on the object with the specified headers 'ISBN-10' and 'ISBN-13'
62
+ isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'
63
+
64
+ # blurb is an attribute of Book, with the header being specified directly
65
+ # as 'Summary'
66
+ blurb 'Summary'
67
+
68
+ end
69
+
70
+ In the above example, any of the declarations (name, description, pages, publisher, isbn, blurb, etc), could be methods, attributes, associations, etc - no distinction during configuration is required, as everything is invoked via Ruby's #send method.
71
+
72
+ You can get the CSV representation of any object by calling the to_comma method, optionally providing a CSV description name to use.
73
+
74
+ Object values are automatically converted to strings via to_s allowing you to reuse any existing to_s methods on your objects (instead of having to call particular properties or define CSV specific output methods). Header names are also automatically humanized when reflected (eg. Replacing _ characters with whitespace). The 'isbn' example above shows how multiple values can be added to the CSV output.
75
+
76
+ Multiple CSV descriptions can also be specified for the same class, eg:
77
+
78
+ class Book < ActiveRecord::Base
79
+
80
+ # ================
81
+ # = Associations =
82
+ # ================
83
+ has_many :pages
84
+ has_one :isbn
85
+ belongs_to :publisher
86
+
87
+ # ===============
88
+ # = CSV support =
89
+ # ===============
90
+ comma do
91
+
92
+ name
93
+ description
94
+
95
+ pages :size => 'Pages'
96
+ publisher :name
97
+ isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'
98
+ blurb 'Summary'
99
+
100
+ end
101
+
102
+ comma :brief do
103
+
104
+ name
105
+ description
106
+ blurb 'Summary'
107
+
108
+ end
109
+
110
+ end
111
+
112
+ You can specify which output format you would like to use via an optional parameter to to_comma:
113
+
114
+ Book.limited(10).to_comma(:brief)
115
+
116
+ Specifying no description name to to_comma is equivalent to specifying :default as the description name.
117
+
118
+ You can pass options for FasterCSV, e.g.
119
+
120
+ Book.limited(10).to_comma(:style => :brief,
121
+ :col_sep => ';',
122
+ :force_quotes => true)
123
+
124
+ You can pass the :filename option and have Comma writes the CSV output to this file:
125
+
126
+ Book.limited(10).to_comma(:filename => 'books.csv')
127
+
128
+ == Using blocks
129
+
130
+ For more complex relationships you can pass blocks for calculated values, or related values. Following the previous example here is a comma set using blocks (both with and without labels for your CSV headings):
131
+
132
+ class Publisher < ActiveRecord::Base
133
+ # ================
134
+ # = Associations =
135
+ # ================
136
+ has_one :primary_contact, :class_name => "User" #(basic user with a name)
137
+ has_many :users
138
+ end
139
+
140
+ class Book < ActiveRecord::Base
141
+
142
+ # ================
143
+ # = Associations =
144
+ # ================
145
+ has_many :pages
146
+ has_one :isbn
147
+ belongs_to :publisher
148
+
149
+ # ===============
150
+ # = CSV support =
151
+ # ===============
152
+ comma do
153
+ name
154
+ description
155
+
156
+ pages :size => 'Pages'
157
+ publisher :name
158
+ publisher { |publisher| publisher.primary_contact.name.to_s.titleize }
159
+ publisher 'Number of publisher users' do |publisher| publisher.users.size end
160
+ isbn :number_10 => 'ISBN-10', :number_13 => 'ISBN-13'
161
+ blurb 'Summary'
162
+
163
+ end
164
+
165
+ end
166
+
167
+
168
+ In the preceding example, the 2 new fields added (both based on the publisher relationship) mean that the following will be added:
169
+ - the first example 'publishers_contact' is loaded straight as a block. The value returned by the lambda is displayed with a header value of 'Publisher'
170
+ - the second example 'total_publishers_users' is sent via a hash and a custom label is set, if used in the first examples method the header would be 'Publisher', but sent as a hash the header is 'Number of publisher users'.
171
+
172
+ === USING WITH RAILS
173
+
174
+ When used with Rails (ie. add 'comma' as a gem dependency), Comma automatically adds support for rendering CSV output in your controllers:
175
+
176
+ class BooksController < ApplicationController
177
+
178
+ def index
179
+ respond_to do |format|
180
+ format.csv { render :csv => Book.limited(50) }
181
+ end
182
+ end
183
+
184
+ end
185
+
186
+ You can specify which output format you would like to use by specifying a style parameter:
187
+ class BooksController < ApplicationController
188
+
189
+ def index
190
+ respond_to do |format|
191
+ format.csv { render :csv => Book.limited(50), :style => :brief }
192
+ end
193
+ end
194
+
195
+ end
196
+
197
+ When used with Rails 2.3.*, Comma also adds support for exporting named scopes:
198
+
199
+ class Book < ActiveRecord::Base
200
+ named_scope :recent,
201
+ lambda { { :conditions => ['created_at > ?', 1.month.ago] } }
202
+
203
+ # ...
204
+ end
205
+
206
+ Calling the to_comma method on the named scope will internally use Rails' find_each method, instantiating only 1,000 ActiveRecord objects at a time:
207
+
208
+ Book.recent.to_comma
209
+
210
+ == DEPENDENCIES
211
+
212
+ If you're on Ruby 1.8.*, the FasterCSV gem is recommended for performance reasons.
213
+
214
+ gem install fastercsv
215
+
216
+ If you have any questions or suggestions for Comma, please feel free to contact me at crafterm@redartisan.com, all feedback welcome!
217
+
218
+
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "vibedeck-comma"
8
+ gem.summary = "Ruby Comma Seperated Values generation library"
9
+ gem.description = "Ruby Comma Seperated Values generation library"
10
+ gem.email = "crafterm@redartisan.com"
11
+ gem.rubyforge_project = 'comma'
12
+ gem.homepage = "http://github.com/crafterm/comma"
13
+ gem.authors = ["Marcus Crafter"]
14
+ gem.add_development_dependency "rspec", ">= 1.2.9"
15
+ gem.add_dependency("activesupport", ">= 2.2.2")
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ # require 'spec/rake/spectask'
24
+ # Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ # spec.libs << 'lib' << 'spec'
26
+ # spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ # end
28
+ #
29
+ # Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ # spec.libs << 'lib' << 'spec'
31
+ # spec.pattern = 'spec/**/*_spec.rb'
32
+ # spec.rcov = true
33
+ # end
34
+ #
35
+ # task :spec => :check_dependencies
36
+ #
37
+ # task :default => :spec
38
+
39
+ require 'rake/rdoctask'
40
+ Rake::RDocTask.new do |rdoc|
41
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "comma #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.2
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'comma'
data/lib/comma.rb ADDED
@@ -0,0 +1,40 @@
1
+ # conditional loading of activesupport
2
+ if defined? Rails and (Rails.version.split('.').map(&:to_i) <=> [2,3,5]) < 0
3
+ require 'activesupport'
4
+ else
5
+ require 'active_support/core_ext/class/inheritable_attributes'
6
+ end
7
+
8
+ # load the right csv library
9
+ if RUBY_VERSION >= '1.9'
10
+ require 'csv'
11
+ FasterCSV = CSV
12
+ else
13
+ begin
14
+ # try faster csv
15
+ require 'fastercsv'
16
+ rescue Exception => e
17
+ if defined? Rails
18
+ Rails.logger.info "FasterCSV not installed, falling back on CSV"
19
+ else
20
+ puts "FasterCSV not installed, falling back on CSV"
21
+ end
22
+ require 'csv'
23
+ FasterCSV = CSV
24
+ end
25
+ end
26
+
27
+ require 'comma/extractors'
28
+ require 'comma/generator'
29
+ require 'comma/array'
30
+ require 'comma/object'
31
+ require 'comma/render_as_csv'
32
+
33
+ if defined?(ActiveRecord)
34
+ require 'comma/named_scope'
35
+ require 'comma/association_proxy'
36
+ end
37
+
38
+ if defined?(RenderAsCSV) && defined?(ActionController)
39
+ ActionController::Base.send :include, RenderAsCSV
40
+ end
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def to_comma(style = :default)
3
+ Comma::Generator.new(self, style).run(:each)
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class ActiveRecord::Associations::AssociationProxy
2
+ def to_comma(style = :default)
3
+ #Bug in Rails 2.3.5, this is a workaround as association_proxy.rb doesn't pass the &block in the send method so it silently fails
4
+ Comma::Generator.new(Array(self), style).run(:each)
5
+ end
6
+ end
@@ -0,0 +1,74 @@
1
+ module Comma
2
+
3
+ class Extractor
4
+
5
+ def initialize(instance, &block)
6
+ @instance = instance
7
+ @block = block
8
+ @results = []
9
+ end
10
+
11
+ def results
12
+ instance_eval &@block
13
+ @results
14
+ end
15
+
16
+ def id(*args)
17
+ method_missing(:id, *args)
18
+ end
19
+ end
20
+
21
+ class HeaderExtractor < Extractor
22
+
23
+ def method_missing(sym, *args, &block)
24
+ @results << sym.to_s.humanize if args.blank?
25
+ args.each do |arg|
26
+ case arg
27
+ when Hash
28
+ arg.each do |k, v|
29
+ @results << ((v.is_a? String) ? v : v.to_s.humanize)
30
+ end
31
+ when Symbol
32
+ @results << arg.to_s.humanize
33
+ when String
34
+ @results << arg
35
+ else
36
+ raise "Unknown header symbol #{arg.inspect}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ class DataExtractor < Extractor
43
+
44
+ def method_missing(sym, *args, &block)
45
+ if args.blank?
46
+ result = block ? yield(@instance.send(sym)) : @instance.send(sym)
47
+ @results << result.to_s
48
+ end
49
+
50
+ args.each do |arg|
51
+ case arg
52
+ when Hash
53
+ arg.each do |k, v|
54
+ if block
55
+ @results << (@instance.send(sym).nil? ? '' : yield(@instance.send(sym).send(k)).to_s )
56
+ else
57
+ @results << (@instance.send(sym).nil? ? '' : @instance.send(sym).send(k).to_s )
58
+ end
59
+ end
60
+ when Symbol
61
+ if block
62
+ @results << (@instance.send(sym).nil? ? '' : yield(@instance.send(sym).send(arg)).to_s)
63
+ else
64
+ @results << ( @instance.send(sym).nil? ? '' : @instance.send(sym).send(arg).to_s )
65
+ end
66
+ when String
67
+ @results << (block ? yield(@instance.send(sym)) : @instance.send(sym)).to_s
68
+ else
69
+ raise "Unknown data symbol #{arg.inspect}"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end