active_export 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/.gitignore +10 -0
- data/.yardopts +7 -0
- data/Gemfile +27 -0
- data/Guardfile +10 -0
- data/LICENSE +22 -0
- data/README.md +180 -0
- data/Rakefile +19 -0
- data/active_export.gemspec +19 -0
- data/lib/active_export/base.rb +101 -0
- data/lib/active_export/configuration.rb +19 -0
- data/lib/active_export/csv.rb +48 -0
- data/lib/active_export/version.rb +3 -0
- data/lib/active_export.rb +76 -0
- data/spec/active_export/base_spec.rb +118 -0
- data/spec/active_export/csv_spec.rb +98 -0
- data/spec/active_export/rails_support_spec.rb +8 -0
- data/spec/active_export_spec.rb +43 -0
- data/spec/factories/book.rb +5 -0
- data/spec/fixtures/csv_1.yml +12 -0
- data/spec/rails/model/author.rb +5 -0
- data/spec/rails/model/book.rb +7 -0
- data/spec/rails/model/book_category.rb +6 -0
- data/spec/rails/model/category.rb +6 -0
- data/spec/rails/rails_support.rb +24 -0
- data/spec/rails/schema.rb +20 -0
- data/spec/spec_helper.rb +33 -0
- data/tasks/yard.rake +6 -0
- metadata +102 -0
data/.gitignore
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in active_export.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development, :test do
|
7
|
+
gem 'rake', '~> 0.9.2.2', :require => false
|
8
|
+
gem 'rails', ">= #{ENV['RAILS'] || '3.0.0'}"
|
9
|
+
|
10
|
+
gem 'yard'
|
11
|
+
gem 'rdiscount'
|
12
|
+
|
13
|
+
gem 'tapp', :git => 'git://github.com/esminc/tapp.git'
|
14
|
+
|
15
|
+
gem 'sqlite3'
|
16
|
+
gem 'rspec-rails'
|
17
|
+
gem 'factory_girl_rails'
|
18
|
+
gem 'database_cleaner'
|
19
|
+
gem 'guard'
|
20
|
+
gem 'guard-rspec'
|
21
|
+
gem 'fuubar'
|
22
|
+
|
23
|
+
gem 'libnotify', :require => false
|
24
|
+
gem 'rb-inotify', :require => false
|
25
|
+
gem 'rb-fsevent', :require => false
|
26
|
+
gem 'growl', :require => false
|
27
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec', :version => 2, :cli => "--color --format Fuubar --profile", keep_failed: false, all_on_start: false, all_after_pass: false do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/active_export/(.+)\.rb$}) { |m| "spec/active_export/#{m[1]}_spec.rb" }
|
7
|
+
watch('lib/active_export.rb') { "spec" }
|
8
|
+
watch('spec/spec_helper.rb') { "spec" }
|
9
|
+
end
|
10
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 kengos
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
# ActiveExport
|
2
|
+
|
3
|
+
Export to csv from ActiveRecord or others
|
4
|
+
|
5
|
+
You do not need to write the dirty code to output the csv in your controller, model or others.
|
6
|
+
|
7
|
+
In your controller:
|
8
|
+
|
9
|
+
````ruby
|
10
|
+
ActiveExport::Csv.export(Book.all, source_name, namespace)
|
11
|
+
|
12
|
+
# it means
|
13
|
+
CSV.generate do |csv|
|
14
|
+
csv << ['Title', 'Author', 'Price(in Tax)', 'Published Date']
|
15
|
+
Book.all.each do |book|
|
16
|
+
csv << [book.name, (book.author.try(:name) || ''), book.price, book.created_at.strftime('%Y%m%d')]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
````
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Add this line to your application's Gemfile:
|
25
|
+
|
26
|
+
gem 'active_export'
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
$ bundle
|
31
|
+
|
32
|
+
Or install it yourself as:
|
33
|
+
|
34
|
+
$ gem install active_export
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Add initalizers `active_export.rb`
|
39
|
+
|
40
|
+
Create `active_export.yml` And write csv export method
|
41
|
+
|
42
|
+
[YAML file format][yaml-file-format]
|
43
|
+
|
44
|
+
Use `ActiveExport::Csv.export(data, source_name, namespace)` in your controller or others.
|
45
|
+
|
46
|
+
## Example
|
47
|
+
|
48
|
+
ActiveRecord:
|
49
|
+
|
50
|
+
````ruby
|
51
|
+
class Book < ActiveRecord::Base
|
52
|
+
belongs_to :author
|
53
|
+
end
|
54
|
+
|
55
|
+
class Author < ActiveRecord::Base
|
56
|
+
end
|
57
|
+
````
|
58
|
+
|
59
|
+
Book records:
|
60
|
+
|
61
|
+
| id | name | author_id | price | created_at |
|
62
|
+
|:--:|:----:|:---------:|:-----:|:----------:|
|
63
|
+
| 1 | Ruby | 1 | 50 | 2012/08/01 00:00:00 UTC |
|
64
|
+
| 2 | Java | 2 | 30 | 2012/08/02 00:00:00 UTC |
|
65
|
+
|
66
|
+
Author records:
|
67
|
+
|
68
|
+
| id | name |
|
69
|
+
|:--:|:-----:|
|
70
|
+
| 1 | Bob |
|
71
|
+
| 2 | Alice |
|
72
|
+
|
73
|
+
`en.yml`:
|
74
|
+
|
75
|
+
<pre>
|
76
|
+
activerecord:
|
77
|
+
attributes:
|
78
|
+
book:
|
79
|
+
name: 'Title'
|
80
|
+
price: 'Price(in Tax)'
|
81
|
+
created_at: 'Published Date'
|
82
|
+
author:
|
83
|
+
name: 'Author'
|
84
|
+
</pre>
|
85
|
+
|
86
|
+
`config/initializers/active_export.rb`:
|
87
|
+
|
88
|
+
````ruby
|
89
|
+
ActiveExportconfigure do |config|
|
90
|
+
config.sources = { default: Rails.root.join('config', 'active_export.yml') }
|
91
|
+
## option fields
|
92
|
+
# config.default_csv_optoins = { col_sep: ',', row_sep: "\n", force_quotes: true }
|
93
|
+
# config.always_reload = false # default
|
94
|
+
# config.no_source_raise_error = false # default
|
95
|
+
end
|
96
|
+
````
|
97
|
+
|
98
|
+
`config/active_export.yml`:
|
99
|
+
|
100
|
+
````
|
101
|
+
book:
|
102
|
+
label_prefix: 'book'
|
103
|
+
methods:
|
104
|
+
- name
|
105
|
+
- author.name
|
106
|
+
- price
|
107
|
+
- created_at: creaetd_at.strftime('%Y/%m/%d')
|
108
|
+
````
|
109
|
+
|
110
|
+
In your controller or others:
|
111
|
+
|
112
|
+
````ruby
|
113
|
+
ActiveExport::Csv.export(Book.all, :default, :book)
|
114
|
+
# => CSV string
|
115
|
+
# "Title","Author","Price(in Tax)","Published Date"
|
116
|
+
# "Ruby","Bob","50","2012/08/01"
|
117
|
+
# "Java","Alice","20","2012/08/02"
|
118
|
+
````
|
119
|
+
|
120
|
+
## YAML file format
|
121
|
+
|
122
|
+
```
|
123
|
+
[namespace]:
|
124
|
+
label_prefix: [label_prefix]
|
125
|
+
methods:
|
126
|
+
- [method_name]
|
127
|
+
- [label_name]: [method_name]
|
128
|
+
- ...
|
129
|
+
```
|
130
|
+
|
131
|
+
### Method_name examples
|
132
|
+
|
133
|
+
```
|
134
|
+
book:
|
135
|
+
- "author.name" # call [instance].author.name
|
136
|
+
- "price > 0" # call [instance].price > 0 # => true or false
|
137
|
+
- "price.to_f / 2.0" # call [instance].price.to_f / 2.0
|
138
|
+
- "sprintf("%#b", price)" # call sprintf("%#b", [instance].price)
|
139
|
+
```
|
140
|
+
|
141
|
+
### I18n field priority
|
142
|
+
|
143
|
+
1. `active_export.#{source_name}.#{namespace}.(label_prefix_)#{key}`
|
144
|
+
2. `activerecord.attributes.(label_prefix.)#{key}`
|
145
|
+
3. `activemodel.attributes.(label_prefix.)#{key}`
|
146
|
+
4. `#{key.to_s.gsub(".", "_").humanize}`
|
147
|
+
|
148
|
+
ex)
|
149
|
+
<pre>
|
150
|
+
key ... author.name
|
151
|
+
label_prefix ... book
|
152
|
+
source_name ... default
|
153
|
+
namespace ... book_1
|
154
|
+
|
155
|
+
1. `active_export.default.book_1.author_name`
|
156
|
+
2. `activerecord.attributes.author.name`
|
157
|
+
3. `activemode.attributes.author.name`
|
158
|
+
4. `author_name".humanize # => Author name`
|
159
|
+
</pre>
|
160
|
+
|
161
|
+
ex2)
|
162
|
+
<pre>
|
163
|
+
key ... "name"
|
164
|
+
label_prefix ... "book"
|
165
|
+
source_name ... "default"
|
166
|
+
namespace ... "book_1"
|
167
|
+
|
168
|
+
1. "active_export.default.book_1.book_name"
|
169
|
+
2. "activerecord.attributes.book.name"
|
170
|
+
3. "activemode.attributes.book.name"
|
171
|
+
4. "book_name".humanize # => Book name
|
172
|
+
</pre>
|
173
|
+
|
174
|
+
## Contributing
|
175
|
+
|
176
|
+
1. Fork it
|
177
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
178
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
179
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
180
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
def cmd(command)
|
6
|
+
puts command
|
7
|
+
raise unless system command
|
8
|
+
end
|
9
|
+
|
10
|
+
# Import all our rake tasks
|
11
|
+
FileList['tasks/**/*.rake'].each { |task| import task }
|
12
|
+
|
13
|
+
require 'rspec/core'
|
14
|
+
require 'rspec/core/rake_task'
|
15
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
16
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
17
|
+
end
|
18
|
+
# Run the specs
|
19
|
+
task :default => :spec
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/active_export/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["kengos"]
|
6
|
+
gem.email = ["kengo@kengos.jp"]
|
7
|
+
gem.description = %q{Export to CSV from ActiveRecord collections.}
|
8
|
+
gem.summary = %q{Export to CSV from ActiveRecord collections.}
|
9
|
+
gem.homepage = "https://github.com/kengos/active_export"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
|
13
|
+
gem.add_dependency("activesupport", ">= 3.0.0")
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.name = "active_export"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = ActiveExport::VERSION
|
19
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'i18n'
|
4
|
+
require 'active_support'
|
5
|
+
|
6
|
+
module ActiveExport
|
7
|
+
class Base
|
8
|
+
attr_accessor :source_name, :namespace, :label_prefix, :source, :label_keys, :eval_methods
|
9
|
+
attr_reader :config
|
10
|
+
def initialize(source_name, namespace, options = {})
|
11
|
+
@source_name = source_name.to_sym
|
12
|
+
@namespace = namespace.to_sym
|
13
|
+
@config = ::ActiveExport.configuration
|
14
|
+
@label_keys = options.has_key?(:label_keys) ? options[:label_keys] : nil
|
15
|
+
@eval_methods = options.has_key?(:eval_methods) ? options[:eval_methods] : nil
|
16
|
+
@label_prefix = options.has_key?(:label_prefix) ? options[:label_prefix] : nil
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def translate(key, scope = [])
|
21
|
+
defaults = [
|
22
|
+
:"active_export.#{scope.join('.')}.#{key.to_s.gsub('.', '_')}",
|
23
|
+
:"activerecord.attributes.#{key}",
|
24
|
+
:"activemodel.attributes.#{key}",
|
25
|
+
key.to_s.gsub('.', '_').humanize
|
26
|
+
]
|
27
|
+
I18n.translate(defaults.shift, default: defaults)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param [Array] methods
|
31
|
+
# @return [Array]
|
32
|
+
def build_label_keys_and_eval_methods(methods)
|
33
|
+
label_keys = []
|
34
|
+
eval_methods = []
|
35
|
+
methods.each do |f|
|
36
|
+
if f.is_a?(Hash)
|
37
|
+
label_keys << f.keys.first
|
38
|
+
eval_methods << f.values.first
|
39
|
+
else
|
40
|
+
label_keys << f
|
41
|
+
eval_methods << f
|
42
|
+
end
|
43
|
+
end
|
44
|
+
return label_keys, eval_methods
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Convert value for export string
|
49
|
+
# @todo refactor me
|
50
|
+
def convert(value)
|
51
|
+
if value.nil?
|
52
|
+
translate(:nil, config.default_value_label_scope)
|
53
|
+
elsif value == ''
|
54
|
+
translate(:blank, config.default_value_label_scope)
|
55
|
+
elsif value == true
|
56
|
+
translate(:true, config.default_value_label_scope)
|
57
|
+
elsif value == false
|
58
|
+
translate(:false, config.default_value_label_scope)
|
59
|
+
else
|
60
|
+
value.to_s
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def label_keys
|
65
|
+
build_label_keys_and_eval_methods! unless @label_keys
|
66
|
+
@label_keys
|
67
|
+
end
|
68
|
+
|
69
|
+
def eval_methods
|
70
|
+
build_label_keys_and_eval_methods! unless @eval_methods
|
71
|
+
@eval_methods
|
72
|
+
end
|
73
|
+
|
74
|
+
def label_prefix
|
75
|
+
@label_prefix ||= source[:label_prefix]
|
76
|
+
end
|
77
|
+
|
78
|
+
def default_scope
|
79
|
+
[self.source_name, self.namespace]
|
80
|
+
end
|
81
|
+
|
82
|
+
def key_name(key)
|
83
|
+
key.to_s.include?(".") ? key.to_s : "#{label_prefix}.#{key}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_label_keys_and_eval_methods!
|
87
|
+
@label_keys, @eval_methods = self.class.build_label_keys_and_eval_methods(source[:methods])
|
88
|
+
end
|
89
|
+
|
90
|
+
def source
|
91
|
+
@source ||= ::ActiveExport[self.source_name][self.namespace]
|
92
|
+
rescue => e
|
93
|
+
raise e if config.no_source_raise_error = true
|
94
|
+
return {}
|
95
|
+
end
|
96
|
+
|
97
|
+
def translate(key, scope = nil)
|
98
|
+
self.class.translate(key, scope || default_scope)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module ActiveExport
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :sources
|
6
|
+
attr_accessor :default_csv_options
|
7
|
+
attr_accessor :always_reload
|
8
|
+
attr_accessor :default_value_label_scope
|
9
|
+
attr_accessor :no_source_raise_error
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@sources = {}
|
13
|
+
@default_csv_options = { col_sep: ',', row_sep: "\n", force_quotes: true }
|
14
|
+
@always_reload = false
|
15
|
+
@default_value_label_scope = [:default_value_labels]
|
16
|
+
@no_source_raise_error = false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
module ActiveExport
|
6
|
+
class Csv < ::ActiveExport::Base
|
7
|
+
# @param [Array, ActiveRecord::Relation] data
|
8
|
+
# @param [Symbol, String] source
|
9
|
+
# @param [Symobl, String] namespace
|
10
|
+
# @param [Hash] options ({})
|
11
|
+
# @option options [Array] :eval_methods
|
12
|
+
# @option options [Array] :label_keys
|
13
|
+
# @option options [String] :label_prefix
|
14
|
+
# @option options [Hash] :csv_options (see http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV.html)
|
15
|
+
# @exmaple
|
16
|
+
# AcriveExport::Csv.export(data, :source, :namespace)
|
17
|
+
def self.export(data, source, namespace, options = {})
|
18
|
+
new(source, namespace, options).export(data, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def export(data, options = {})
|
22
|
+
return '' if data.length <= 0 && eval_methods.nil?
|
23
|
+
csv_options = self.config.default_csv_options.merge( options[:csv_options] || {} )
|
24
|
+
|
25
|
+
each_method_name = data.respond_to?(:find_each) ? 'find_each' : 'each'
|
26
|
+
# 1.9.2
|
27
|
+
CSV.generate(csv_options) do |csv|
|
28
|
+
csv << generate_header
|
29
|
+
data.send(each_method_name) do |f|
|
30
|
+
csv << generate_value(f)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def generate_header
|
36
|
+
self.label_keys.inject([]) {|result, key|
|
37
|
+
result << translate(key_name(key))
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_value(row)
|
42
|
+
self.eval_methods.inject([]){|result, f|
|
43
|
+
v = row.send(:eval, f) rescue nil
|
44
|
+
result << convert(v)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "active_export/version"
|
2
|
+
require 'active_export/configuration'
|
3
|
+
require 'yaml'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
module ActiveExport
|
7
|
+
autoload :Base, 'active_export/base'
|
8
|
+
autoload :Csv, 'active_export/csv'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# @example
|
12
|
+
# ActiveExportconfigure do |config|
|
13
|
+
# # ActiveExport export configuration files.
|
14
|
+
# config.sources = { default: Rails.root.join('config', 'active_export.yml') }
|
15
|
+
#
|
16
|
+
# # see CSV.new options
|
17
|
+
# config.default_csv_optoins = { col_sep: ',', row_sep: "\n", force_quotes: true }
|
18
|
+
#
|
19
|
+
# # if set 'true', ActiveExport no cached yml data. Every time load yml file.
|
20
|
+
# # if set 'false', ActiveExport cached yml data.
|
21
|
+
# config.always_reload = true # default false
|
22
|
+
#
|
23
|
+
# # if set 'true', not found sources[:source_name] to raise error
|
24
|
+
# config.no_source_raise_error = false # default
|
25
|
+
# end
|
26
|
+
def configure
|
27
|
+
yield configuration
|
28
|
+
end
|
29
|
+
|
30
|
+
# Accessor for ActiveExport::Configuration
|
31
|
+
def configuration
|
32
|
+
@configuration ||= Configuration.new
|
33
|
+
end
|
34
|
+
alias :config :configuration
|
35
|
+
|
36
|
+
def [](key)
|
37
|
+
source(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear!
|
41
|
+
@source = {}
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def load!
|
46
|
+
config.sources.each {|f| source(f) }
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def reload!
|
51
|
+
clear!
|
52
|
+
load!
|
53
|
+
end
|
54
|
+
|
55
|
+
def source(key)
|
56
|
+
if @configuration.always_reload
|
57
|
+
load!(key)
|
58
|
+
else
|
59
|
+
@sources ||= {}
|
60
|
+
@sources[key] ||= load!(key)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def include_source?(key)
|
65
|
+
@configuration.sources.has_key?(key)
|
66
|
+
end
|
67
|
+
|
68
|
+
def load!(key)
|
69
|
+
if include_source?(key)
|
70
|
+
::ActiveSupport::HashWithIndifferentAccess.new(YAML.load(ERB.new(open(@configuration.sources[key]).read).result).to_hash)
|
71
|
+
else
|
72
|
+
raise "Missing '#{key}' in sources"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActiveExport::Base do
|
6
|
+
before {
|
7
|
+
@default_locale = I18n.locale
|
8
|
+
I18n.locale = :en
|
9
|
+
|
10
|
+
ActiveExport.configure do |config|
|
11
|
+
config.sources = { default: fixture_file('csv_1.yml') }
|
12
|
+
end
|
13
|
+
}
|
14
|
+
|
15
|
+
after {
|
16
|
+
I18n.backend.reload!
|
17
|
+
I18n.locale = @default_locale
|
18
|
+
}
|
19
|
+
|
20
|
+
let(:active_export) { ActiveExport::Base.new('default', 'book_2') }
|
21
|
+
it { active_export.source_name = :default }
|
22
|
+
it { active_export.namespace = :book_2 }
|
23
|
+
it { active_export.config.should == ActiveExport.configuration }
|
24
|
+
|
25
|
+
context "with options" do
|
26
|
+
let(:active_export) { ActiveExport::Base.new('source', 'namespace', label_keys: %w(name), eval_methods: %w(price)) }
|
27
|
+
it { active_export.label_keys == %w(name) }
|
28
|
+
it { active_export.eval_methods == %w(price) }
|
29
|
+
end
|
30
|
+
|
31
|
+
it { active_export.label_keys.should == %w(name author.name price) }
|
32
|
+
it { active_export.eval_methods.should == ['name', 'author.name', '(price * 1.095).ceil.to_i'] }
|
33
|
+
it { active_export.label_prefix.should == 'book' }
|
34
|
+
it { active_export.default_scope == [:default, :book_2] }
|
35
|
+
|
36
|
+
it 'should not call build_label_keys_and_eval_methods!' do
|
37
|
+
active_export.label_keys
|
38
|
+
active_export.should_not_receive(:build_label_keys_and_eval_methods!)
|
39
|
+
active_export.label_keys
|
40
|
+
active_export.eval_methods
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#key_name" do
|
44
|
+
it { active_export.key_name('hoge').should eql 'book.hoge' }
|
45
|
+
it { active_export.key_name('author.name').should eql 'author.name' }
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#convert" do
|
49
|
+
before do
|
50
|
+
I18n.backend.store_translations :en, active_export: {
|
51
|
+
default_value_labels: { nil: 'Error', blank: 'Blank', true: 'Yes!', false: 'No!' }
|
52
|
+
}
|
53
|
+
end
|
54
|
+
it { active_export.convert(nil).should eql 'Error' }
|
55
|
+
it { active_export.convert('').should eql 'Blank' }
|
56
|
+
it { active_export.convert(true).should eql 'Yes!' }
|
57
|
+
it { active_export.convert(false).should eql 'No!' }
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#source" do
|
61
|
+
before {
|
62
|
+
active_export.source_name = :not_found
|
63
|
+
}
|
64
|
+
it { expect { active_export.source }.to raise_error RuntimeError }
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".build_label_keys_and_eval_methods" do
|
68
|
+
subject { ActiveExport::Base.build_label_keys_and_eval_methods(params) }
|
69
|
+
|
70
|
+
context "String" do
|
71
|
+
let(:params) { ["name"] }
|
72
|
+
it { should == [%w(name), %w(name)] }
|
73
|
+
end
|
74
|
+
|
75
|
+
context "Hash" do
|
76
|
+
let(:params) { [{"price"=>"price * 1.05"}] }
|
77
|
+
it { should == [%w(price), ["price * 1.05"]] }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe ".translate" do
|
82
|
+
let(:i18n_key) { 'author.name' }
|
83
|
+
subject { ActiveExport::Base.translate('author.name', [:default, :book]) }
|
84
|
+
|
85
|
+
context "active_export" do
|
86
|
+
before do
|
87
|
+
I18n.backend.store_translations :en, active_export: {
|
88
|
+
default: {
|
89
|
+
book: { author_name: 'author_name' }
|
90
|
+
}
|
91
|
+
}
|
92
|
+
end
|
93
|
+
it { should == 'author_name' }
|
94
|
+
end
|
95
|
+
|
96
|
+
context "activerecord" do
|
97
|
+
before do
|
98
|
+
I18n.backend.store_translations :en, activerecord: {
|
99
|
+
attributes: { author: { name: 'AuthorName' } }
|
100
|
+
}
|
101
|
+
end
|
102
|
+
it { should == 'AuthorName' }
|
103
|
+
end
|
104
|
+
|
105
|
+
context "activemodel" do
|
106
|
+
before do
|
107
|
+
I18n.backend.store_translations :en, activemodel: {
|
108
|
+
attributes: { author: { name: 'Author_Name' } }
|
109
|
+
}
|
110
|
+
end
|
111
|
+
it { should == 'Author_Name' }
|
112
|
+
end
|
113
|
+
|
114
|
+
context "not found" do
|
115
|
+
it { should == 'Author name' }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActiveExport::Csv do
|
6
|
+
before {
|
7
|
+
@default_locale = I18n.locale
|
8
|
+
I18n.locale = :en
|
9
|
+
}
|
10
|
+
|
11
|
+
after {
|
12
|
+
I18n.backend.reload!
|
13
|
+
I18n.locale = @default_locale
|
14
|
+
}
|
15
|
+
|
16
|
+
describe ".export" do
|
17
|
+
let(:author_1) { Author.create!(name: 'author_1') }
|
18
|
+
let(:author_2) { Author.create!(name: 'author_2') }
|
19
|
+
let!(:book_1) { Book.create!(name: 'book_1', author: author_1, price: 58) }
|
20
|
+
let!(:book_2) { Book.create!(name: 'book_2', author: author_2, price: 38) }
|
21
|
+
|
22
|
+
before {
|
23
|
+
ActiveExport.configure do |config|
|
24
|
+
config.sources = { default: fixture_file('csv_1.yml') }
|
25
|
+
config.default_csv_options = { col_sep: ',', row_sep: "\n", force_quotes: true }
|
26
|
+
end
|
27
|
+
}
|
28
|
+
|
29
|
+
context "no options" do
|
30
|
+
subject { ActiveExport::Csv.export(Book.order('id DESC').all, :default, :book_2) }
|
31
|
+
it {
|
32
|
+
should == <<-EOS
|
33
|
+
"Book name","Author name","Book price"
|
34
|
+
"book_2","author_2","42"
|
35
|
+
"book_1","author_1","64"
|
36
|
+
EOS
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
context "add some options" do
|
41
|
+
let(:csv_options) {
|
42
|
+
{ force_quotes: false, col_sep: ':'}
|
43
|
+
}
|
44
|
+
subject { ActiveExport::Csv.export(Book.order('id DESC').all, :default, :book_1, csv_options: csv_options) }
|
45
|
+
it {
|
46
|
+
should == <<-EOS
|
47
|
+
Book name:Author name:Book price
|
48
|
+
book_2:author_2:38
|
49
|
+
book_1:author_1:58
|
50
|
+
EOS
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".generate_value" do
|
56
|
+
let(:author_1) { Author.create!(name: 'author_1') }
|
57
|
+
let!(:book_1) { Book.create!(name: 'book_1', author: author_1, price: 1000) }
|
58
|
+
let(:eval_methods) { ['name', "author.name", 'price', 'deleted_at', 'price > 0', 'price > 15000'] }
|
59
|
+
let(:csv_exporter) { ActiveExport::Csv.new(:default, :book_1, eval_methods: eval_methods) }
|
60
|
+
|
61
|
+
before {
|
62
|
+
I18n.backend.store_translations :en, active_export: {
|
63
|
+
default_value_labels: { nil: 'not found', blank: 'Blank', true: 'Yes!', false: 'No!' }
|
64
|
+
}
|
65
|
+
}
|
66
|
+
subject { csv_exporter.generate_value(book_1) }
|
67
|
+
it { should eql ['book_1', 'author_1', '1000', 'not found', 'Yes!', 'No!'] }
|
68
|
+
end
|
69
|
+
|
70
|
+
describe ".generate_header" do
|
71
|
+
let(:label_keys) { %w(name author.name price) }
|
72
|
+
let(:csv_exporter) { ActiveExport::Csv.new(:default, :book_1, label_keys: label_keys, label_prefix: 'book') }
|
73
|
+
|
74
|
+
subject { csv_exporter.generate_header }
|
75
|
+
|
76
|
+
context "no language file" do
|
77
|
+
it { should eql ['Book name', 'Author name', 'Book price'] }
|
78
|
+
end
|
79
|
+
|
80
|
+
context "translate" do
|
81
|
+
before do
|
82
|
+
I18n.backend.store_translations :en, activerecord: {
|
83
|
+
attributes: {
|
84
|
+
book: { name: 'name', price: 'Price(in tax)' },
|
85
|
+
author: { name: 'Author Name' }
|
86
|
+
}
|
87
|
+
}
|
88
|
+
I18n.backend.store_translations :en, active_export: {
|
89
|
+
default: {
|
90
|
+
book_1: { book_name: 'Title' }
|
91
|
+
}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
it { should eql ['Title', 'Author Name', 'Price(in tax)'] }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe ActiveExport do
|
6
|
+
it 'should access sources' do
|
7
|
+
ActiveExport.configure do |config|
|
8
|
+
config.sources = { :default => fixture_file('csv_1.yml') }
|
9
|
+
end
|
10
|
+
ActiveExport.config.sources.should == { :default => fixture_file('csv_1.yml') }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '[](key)' do
|
14
|
+
context "configuration always_reload is false" do
|
15
|
+
before {
|
16
|
+
ActiveExport.configure do |config|
|
17
|
+
config.sources = { default: fixture_file('csv_1.yml') }
|
18
|
+
config.always_reload = false
|
19
|
+
end
|
20
|
+
ActiveExport[:default]
|
21
|
+
ActiveExport.should_not_receive(:load!).with(:default)
|
22
|
+
}
|
23
|
+
subject { ActiveExport[:default] }
|
24
|
+
it { should be_kind_of Hash }
|
25
|
+
it { subject['book_1'].should be_present }
|
26
|
+
it { subject[:book_2].should be_present }
|
27
|
+
end
|
28
|
+
|
29
|
+
context "configuration always_reload is true" do
|
30
|
+
before {
|
31
|
+
ActiveExport.configure do |config|
|
32
|
+
config.sources = { default: fixture_file('csv_1.yml') }
|
33
|
+
config.always_reload = true
|
34
|
+
end
|
35
|
+
}
|
36
|
+
it 'should call load!' do
|
37
|
+
ActiveExport[:default]
|
38
|
+
ActiveExport.should_receive(:load!).with(:default)
|
39
|
+
ActiveExport[:default]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'rails'
|
4
|
+
require 'active_record'
|
5
|
+
require 'action_controller/railtie'
|
6
|
+
|
7
|
+
module ActiveExport
|
8
|
+
class Application < Rails::Application
|
9
|
+
config.generators do |g|
|
10
|
+
g.orm :active_record
|
11
|
+
g.test_framework :rspec, fixture: false
|
12
|
+
end
|
13
|
+
config.active_support.deprecation = :notify
|
14
|
+
end
|
15
|
+
end
|
16
|
+
ActiveExport::Application.initialize!
|
17
|
+
|
18
|
+
ActiveRecord::Migration.verbose = false
|
19
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
20
|
+
|
21
|
+
support_path = File.expand_path(File.dirname(__FILE__))
|
22
|
+
require support_path + '/schema'
|
23
|
+
|
24
|
+
Dir["#{support_path}/model/**/*.rb"].each {|f| require f }
|
@@ -0,0 +1,20 @@
|
|
1
|
+
ActiveRecord::Schema.define(version: 1) do
|
2
|
+
create_table :books, force: true do |t|
|
3
|
+
t.string :name
|
4
|
+
t.integer :author_id
|
5
|
+
t.integer :price
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :authors, force: true do |t|
|
9
|
+
t.string :name
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :categories, force: true do |t|
|
13
|
+
t.string :name
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :book_categories, force: true do |t|
|
17
|
+
t.integer :book_id
|
18
|
+
t.integer :category_id
|
19
|
+
end
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'tapp'
|
2
|
+
require 'rails/rails_support'
|
3
|
+
require 'rspec'
|
4
|
+
require 'rspec/rails'
|
5
|
+
|
6
|
+
require 'factory_girl'
|
7
|
+
FactoryGirl.find_definitions
|
8
|
+
|
9
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/active_export')
|
10
|
+
|
11
|
+
require 'database_cleaner'
|
12
|
+
DatabaseCleaner.clean_with :truncation
|
13
|
+
DatabaseCleaner.strategy = :transaction
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.mock_with :rspec
|
17
|
+
config.use_transactional_fixtures = false
|
18
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
19
|
+
config.filter_run focus: true
|
20
|
+
config.run_all_when_everything_filtered = true
|
21
|
+
|
22
|
+
config.before(:each) do
|
23
|
+
DatabaseCleaner.start
|
24
|
+
end
|
25
|
+
|
26
|
+
config.after(:each) do
|
27
|
+
DatabaseCleaner.clean
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def fixture_file(filename)
|
32
|
+
File.expand_path(File.dirname(__FILE__)) + '/fixtures/' + filename
|
33
|
+
end
|
data/tasks/yard.rake
ADDED
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_export
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- kengos
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.0.0
|
30
|
+
description: Export to CSV from ActiveRecord collections.
|
31
|
+
email:
|
32
|
+
- kengo@kengos.jp
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- .yardopts
|
39
|
+
- Gemfile
|
40
|
+
- Guardfile
|
41
|
+
- LICENSE
|
42
|
+
- README.md
|
43
|
+
- Rakefile
|
44
|
+
- active_export.gemspec
|
45
|
+
- lib/active_export.rb
|
46
|
+
- lib/active_export/base.rb
|
47
|
+
- lib/active_export/configuration.rb
|
48
|
+
- lib/active_export/csv.rb
|
49
|
+
- lib/active_export/version.rb
|
50
|
+
- spec/active_export/base_spec.rb
|
51
|
+
- spec/active_export/csv_spec.rb
|
52
|
+
- spec/active_export/rails_support_spec.rb
|
53
|
+
- spec/active_export_spec.rb
|
54
|
+
- spec/factories/book.rb
|
55
|
+
- spec/fixtures/csv_1.yml
|
56
|
+
- spec/rails/model/author.rb
|
57
|
+
- spec/rails/model/book.rb
|
58
|
+
- spec/rails/model/book_category.rb
|
59
|
+
- spec/rails/model/category.rb
|
60
|
+
- spec/rails/rails_support.rb
|
61
|
+
- spec/rails/schema.rb
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
- tasks/yard.rake
|
64
|
+
homepage: https://github.com/kengos/active_export
|
65
|
+
licenses: []
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 1.8.24
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: Export to CSV from ActiveRecord collections.
|
88
|
+
test_files:
|
89
|
+
- spec/active_export/base_spec.rb
|
90
|
+
- spec/active_export/csv_spec.rb
|
91
|
+
- spec/active_export/rails_support_spec.rb
|
92
|
+
- spec/active_export_spec.rb
|
93
|
+
- spec/factories/book.rb
|
94
|
+
- spec/fixtures/csv_1.yml
|
95
|
+
- spec/rails/model/author.rb
|
96
|
+
- spec/rails/model/book.rb
|
97
|
+
- spec/rails/model/book_category.rb
|
98
|
+
- spec/rails/model/category.rb
|
99
|
+
- spec/rails/rails_support.rb
|
100
|
+
- spec/rails/schema.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
has_rdoc:
|