rexport 0.5.4 → 1.2.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.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/README.rdoc +1 -1
  3. data/Rakefile +6 -34
  4. data/app/views/export_filters/_export_filter.html.erb +2 -2
  5. data/app/views/export_items/_export_item.html.erb +4 -4
  6. data/app/views/exports/_edit.html.erb +2 -2
  7. data/app/views/exports/_filters.html.erb +1 -1
  8. data/app/views/exports/_form.html.erb +6 -6
  9. data/app/views/exports/_rexport_model.html.erb +1 -1
  10. data/app/views/exports/_show.html.erb +7 -7
  11. data/app/views/exports/edit.html.erb +1 -1
  12. data/app/views/exports/index.html.erb +5 -5
  13. data/app/views/exports/new.html.erb +2 -2
  14. data/app/views/exports/show.html.erb +1 -1
  15. data/config/routes.rb +6 -6
  16. data/db/migrate/20091105182959_create_export_tables.rb +3 -3
  17. data/lib/rexport.rb +3 -1
  18. data/lib/rexport/data_field.rb +17 -0
  19. data/lib/rexport/data_fields.rb +17 -133
  20. data/lib/rexport/export_filter_methods.rb +46 -24
  21. data/lib/rexport/export_filters_controller_methods.rb +8 -4
  22. data/lib/rexport/export_item_methods.rb +29 -34
  23. data/lib/rexport/export_items_controller_methods.rb +13 -13
  24. data/lib/rexport/export_methods.rb +160 -208
  25. data/lib/rexport/exports_controller_methods.rb +34 -35
  26. data/lib/rexport/rexport_model.rb +118 -0
  27. data/lib/rexport/tree_node.rb +13 -16
  28. data/lib/rexport/version.rb +1 -1
  29. data/test/factories.rb +58 -53
  30. data/test/test_helper.rb +36 -39
  31. data/test/unit/data_field_test.rb +11 -11
  32. data/test/unit/data_fields_test.rb +53 -95
  33. data/test/unit/export_filter_methods_test.rb +37 -0
  34. data/test/unit/export_item_methods_test.rb +21 -0
  35. data/test/unit/export_methods_test.rb +181 -59
  36. data/test/unit/rexport_model_test.rb +122 -20
  37. data/test/unit/tree_node_test.rb +20 -20
  38. metadata +25 -38
  39. data/test/jenkins.bash +0 -3
  40. data/test/log/test.log +0 -3891
@@ -3,75 +3,74 @@ module Rexport
3
3
  def index
4
4
  @exports = Export.categorical.alphabetical
5
5
  end
6
-
6
+
7
7
  def show
8
- @export = Export.find(params[:id])
9
-
8
+ export
9
+
10
10
  respond_to do |format|
11
11
  format.html # show.html.erb
12
- format.csv { send_data(@export.to_csv, :type => export_content_type, :filename => filename) }
12
+ format.csv { send_data(export.to_csv, type: export_content_type, filename: filename) }
13
13
  end
14
14
  end
15
-
15
+
16
16
  def new
17
17
  @export = Export.new(export_params)
18
18
  end
19
-
19
+
20
20
  def edit
21
- @export = Export.find(params[:id])
21
+ export
22
22
  end
23
-
23
+
24
24
  def create
25
25
  @export = params[:original_export_id] ? Export.find(params[:original_export_id]).copy : Export.new(export_params)
26
-
26
+
27
27
  if @export.save
28
28
  redirect_to @export, notice: 'Export was successfully created.'
29
29
  else
30
30
  render :new
31
31
  end
32
32
  end
33
-
33
+
34
34
  def update
35
- @export = Export.find(params[:id])
36
-
37
- if @export.update_attributes(export_params)
38
- redirect_to @export, notice: 'Export was successfully updated.'
35
+ if export.update(export_params)
36
+ redirect_to export, notice: 'Export was successfully updated.'
39
37
  else
40
38
  render :edit
41
39
  end
42
40
  end
43
-
41
+
44
42
  def destroy
45
- @export = Export.find(params[:id])
46
- @export.destroy
47
-
43
+ export.destroy
44
+
48
45
  redirect_to exports_url
49
46
  end
50
-
47
+
51
48
  private
52
-
53
- def export_params
54
- params.require(:export).permit(:name, :model_class_name, :description).merge(rexport_fields: rexport_fields, export_filter_attributes: export_filter_attributes)
55
- end
56
-
57
- def rexport_fields
58
- permit_all params[:export][:rexport_fields]
49
+
50
+ def export
51
+ @export ||= Export.find(params[:id])
59
52
  end
60
-
61
- def export_filter_attributes
62
- permit_all params[:export][:export_filter_attributes]
53
+
54
+ def export_params
55
+ params.require(:export).permit(permitted_params)
63
56
  end
64
-
65
- def permit_all(params)
66
- params ? params.permit! : []
57
+
58
+ def permitted_params
59
+ [
60
+ :name,
61
+ :model_class_name,
62
+ :description,
63
+ rexport_fields: {},
64
+ export_filter_attributes: {}
65
+ ]
67
66
  end
68
-
67
+
69
68
  def export_content_type
70
69
  request.user_agent =~ /windows/i ? 'application/vnd.ms-excel' : 'text/csv'
71
70
  end
72
-
71
+
73
72
  def filename
74
- "#{@export.model_class_name}_#{@export.name.gsub(/ /, '_')}_#{Time.now.strftime('%Y%m%d')}.csv"
73
+ "#{export.model_class_name}_#{export.name.gsub(/ /, '_')}_#{Time.now.strftime('%Y%m%d')}.csv"
75
74
  end
76
75
  end
77
76
  end
@@ -0,0 +1,118 @@
1
+ module Rexport #:nodoc:
2
+ class RexportModel
3
+ attr_accessor :klass, :path
4
+
5
+ def initialize(klass, path: nil)
6
+ self.klass = klass
7
+ self.path = path.to_s unless path.blank?
8
+ initialize_rexport_fields
9
+ end
10
+
11
+ def rexport_fields
12
+ @rexport_fields ||= HashWithIndifferentAccess.new
13
+ end
14
+
15
+ def rexport_fields_array
16
+ rexport_fields.values.sort
17
+ end
18
+
19
+ def field_path(field_name)
20
+ [path, field_name].compact * '.'
21
+ end
22
+
23
+ def collection_from_association(association)
24
+ if klass.respond_to?("find_#{association}_for_rexport")
25
+ klass.public_send("find_#{association}_for_rexport")
26
+ else
27
+ klass.reflect_on_association(association.to_sym).klass.all
28
+ end
29
+ end
30
+
31
+ def filter_column(field)
32
+ return field.method unless field.method.include?('.')
33
+ association = field.method.split('.').first
34
+ klass.reflect_on_association(association.to_sym).foreign_key
35
+ end
36
+
37
+ def name
38
+ klass.name
39
+ end
40
+
41
+ # Adds a data item to rexport_fields
42
+ def add_rexport_field(name, options = {})
43
+ rexport_fields[name.to_s] = DataField.new(name, options)
44
+ end
45
+
46
+ # Removes files from rexport_fields
47
+ # useful to remove content columns you don't want included in exports
48
+ def remove_rexport_fields(*fields)
49
+ fields.flatten.each { |field| rexport_fields.delete(field.to_s) }
50
+ end
51
+
52
+ # Adds associated methods to rexport_fields
53
+ # :associations - an association or arrary of associations
54
+ # :methods - a method or array of methods
55
+ # :filter - if true will send type: :association to add_report_field
56
+ def add_association_methods(options = {})
57
+ options.stringify_keys!
58
+ options.assert_valid_keys(%w(associations methods filter))
59
+
60
+ methods = options.reverse_merge('methods' => 'name')['methods']
61
+ methods = [methods] if methods.kind_of?(String)
62
+
63
+ associations = options['associations']
64
+ associations = [associations] if associations.kind_of?(String)
65
+
66
+ type = options['filter'] ? :association : nil
67
+
68
+ associations.each do |association|
69
+ methods.each do |method|
70
+ add_rexport_field("#{association}_#{method}", method: "#{association}.#{method}", type: type)
71
+ end
72
+ end
73
+ end
74
+
75
+ # Returns an array of export methods corresponding with field_names
76
+ def get_rexport_methods(*field_names)
77
+ field_names.flatten.map do |f|
78
+ begin
79
+ components = f.to_s.split('.')
80
+ field_name = components.pop
81
+ components.push(get_rexport_model(components).get_rexport_method(field_name)) * '.'
82
+ rescue NoMethodError
83
+ 'undefined_rexport_field'
84
+ end
85
+ end
86
+ end
87
+
88
+ protected
89
+
90
+ # Returns a rexport_model for the associated class by following the chain of associations
91
+ def get_rexport_model(associations)
92
+ associations.empty? ? self : rexport_models[associations.dup]
93
+ end
94
+
95
+ # Memoize rexport_models to avoid initializing rexport_fields multiple times
96
+ def rexport_models
97
+ @rexport_models ||= Hash.new do |hash, key|
98
+ hash[key] = self.class.new(klass.get_klass_from_associations(key))
99
+ end
100
+ end
101
+
102
+ # Returns the export method for a given field_name
103
+ def get_rexport_method(field_name)
104
+ if rexport_fields[field_name]
105
+ rexport_fields[field_name].method
106
+ else
107
+ raise NoMethodError
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ def initialize_rexport_fields
114
+ klass.content_columns.each { |field| add_rexport_field(field.name, type: field.type) }
115
+ klass.initialize_local_rexport_fields(self) if klass.respond_to?(:initialize_local_rexport_fields)
116
+ end
117
+ end
118
+ end
@@ -2,44 +2,41 @@ module Rexport #:nodoc:
2
2
  # A basic tree for building up ActiveRecord find :include parameters
3
3
  class TreeNode
4
4
  attr_accessor :name, :children
5
-
5
+
6
6
  # Initialize a tree node setting name and adding a child if one was passed
7
7
  def initialize(name, *names)
8
8
  self.name = name
9
9
  self.children = []
10
10
  add_child(names)
11
11
  end
12
-
12
+
13
13
  # Add one or more children to the tree
14
14
  def add_child(*names)
15
15
  names.flatten!
16
16
  return unless name = names.shift
17
- if node = children.find {|c| c.name == name}
18
- node.add_child(names)
19
- else
20
- children << TreeNode.new(name, names)
21
- end
17
+ node = children.find { |c| c.name == name }
18
+ node ? node.add_child(names) : (children << TreeNode.new(name, names))
22
19
  end
23
-
20
+
24
21
  # Return an array representation of the tree
25
22
  def to_a
26
- [name, children.map {|c| c.to_a}]
23
+ [name, children.map(&:to_a)]
27
24
  end
28
-
25
+
29
26
  # Return a :include comptatible statement from the tree
30
27
  def to_include
31
- children.map {|c| c.build_include}
28
+ children.map(&:build_include)
32
29
  end
33
-
30
+
34
31
  # Return the include parameters for a child
35
32
  def build_include
36
- leaf_node? ? name.to_sym : { name.to_sym => children.map {|c| c.build_include} }
33
+ leaf_node? ? name.to_sym : { name.to_sym => children.map(&:build_include) }
37
34
  end
38
-
35
+
39
36
  private
40
-
37
+
41
38
  def leaf_node?
42
39
  children.blank?
43
40
  end
44
41
  end
45
- end
42
+ end
@@ -1,3 +1,3 @@
1
1
  module Rexport
2
- VERSION = '0.5.4'
2
+ VERSION = '1.2.0'
3
3
  end
data/test/factories.rb CHANGED
@@ -1,86 +1,91 @@
1
1
  module Rexport
2
2
  module Factories
3
-
4
- FactoryGirl.define do
5
-
3
+
4
+ FactoryBot.define do
5
+
6
6
  factory :status do
7
- name 'active'
7
+ name { 'active' }
8
8
  end
9
9
 
10
10
  factory :family do
11
- name 'The Sample Family'
11
+ name { 'The Sample Family' }
12
12
  end
13
-
13
+
14
14
  factory :student do
15
- family {|f| f.association(:family)}
16
- name 'Sammy Sample'
17
- date_of_birth Date.parse('2008-12-08')
15
+ family { |family| family.association(:family) }
16
+ name { 'Sammy Sample' }
17
+ date_of_birth { Date.parse('2008-12-08') }
18
18
  end
19
-
19
+
20
20
  factory :enrollment do
21
- status {|status| status.association(:status)}
22
- student {|s| s.association(:student)}
23
- grade 1
21
+ status { |status| status.association(:status) }
22
+ student { |student| student.association(:student) }
23
+ grade { 1 }
24
+ updated_at { Time.now }
24
25
  end
25
-
26
- factory :second_grade_enrollment, :class => 'Enrollment' do
27
- status {|status| status.association(:status)}
28
- grade 2
26
+
27
+ factory :second_grade_enrollment, class: 'Enrollment' do
28
+ status { |status| status.association(:status) }
29
+ grade { 2 }
29
30
  end
30
-
31
+
31
32
  factory :export do
32
- name 'Enrollment Export'
33
- model_class_name 'Enrollment'
33
+ name { 'Enrollment Export' }
34
+ model_class_name { 'Enrollment' }
34
35
  export_items do |items|
35
36
  %w(family_name_export_item grade_export_item status_name_export_item bogus_export_item).map do |item|
36
37
  items.association(item)
37
38
  end
38
39
  end
39
40
  end
40
-
41
- factory :filtered_export, :class => 'Export' do
42
- name 'Filtered Enrollment Export'
43
- model_class_name 'Enrollment'
41
+
42
+ factory :filtered_export, class: 'Export' do
43
+ name { 'Filtered Enrollment Export' }
44
+ model_class_name { 'Enrollment' }
45
+ export_items do |items|
46
+ %w(grade_export_item status_name_export_item).map do |item|
47
+ items.association(item)
48
+ end
49
+ end
44
50
  export_filters do |filters|
45
51
  %w(grade_filter status_filter).map do |filter|
46
52
  filters.association(filter)
47
53
  end
48
54
  end
49
55
  end
50
-
51
- factory :family_name_export_item, :class => 'ExportItem' do
52
- position 1
53
- name 'Family Name'
54
- rexport_field 'student.family.name'
56
+
57
+ factory :family_name_export_item, class: 'ExportItem' do
58
+ position { 1 }
59
+ name { 'Family Name' }
60
+ rexport_field { 'student.family.name' }
55
61
  end
56
-
57
- factory :grade_export_item, :class => 'ExportItem' do
58
- position 2
59
- name 'Grade'
60
- rexport_field 'grade'
62
+
63
+ factory :grade_export_item, class: 'ExportItem' do
64
+ position { 2 }
65
+ rexport_field { 'grade' }
61
66
  end
62
-
63
- factory :status_name_export_item, :class => 'ExportItem' do
64
- position 3
65
- name 'Status'
66
- rexport_field 'status_name'
67
+
68
+ factory :status_name_export_item, class: 'ExportItem' do
69
+ position { 3 }
70
+ name { 'Status' }
71
+ rexport_field { 'status_name' }
67
72
  end
68
-
69
- factory :bogus_export_item, :class => 'ExportItem' do
70
- position 4
71
- name 'Bogus Item'
72
- rexport_field 'bogus_field'
73
+
74
+ factory :bogus_export_item, class: 'ExportItem' do
75
+ position { 4 }
76
+ name { 'Bogus Item' }
77
+ rexport_field { 'bogus_field' }
73
78
  end
74
-
75
- factory :grade_filter, :class => 'ExportFilter' do
76
- filter_field 'grade'
77
- value '1'
79
+
80
+ factory :grade_filter, class: 'ExportFilter' do
81
+ filter_field { 'grade' }
82
+ value { '1' }
78
83
  end
79
-
80
- factory :status_filter, :class => 'ExportFilter' do
81
- filter_field 'status.name'
82
- value 'active'
84
+
85
+ factory :status_filter, class: 'ExportFilter' do
86
+ filter_field { 'status.name' }
87
+ value { 'active' }
83
88
  end
84
89
  end
85
90
  end
86
- end
91
+ end
data/test/test_helper.rb CHANGED
@@ -1,29 +1,26 @@
1
- require 'minitest/autorun'
2
- require 'rubygems'
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/test/'
4
+ end
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
7
+ require "rails"
3
8
  require 'active_record'
4
- require 'active_support/test_case'
5
- require 'logger'
6
- require 'factory_girl'
7
- require 'mocha/mini_test'
8
- require File.dirname(__FILE__) + '/factories'
9
- require File.dirname(__FILE__) + '/../lib/rexport/data_fields'
10
- require File.dirname(__FILE__) + '/../lib/rexport/export_methods'
11
- require File.dirname(__FILE__) + '/../lib/rexport/export_item_methods'
12
- require File.dirname(__FILE__) + '/../lib/rexport/export_filter_methods'
13
- require File.dirname(__FILE__) + '/../lib/rexport/tree_node'
9
+ require "rexport"
14
10
 
15
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
11
+ require 'minitest/autorun'
12
+ require 'factory_bot'
13
+ require 'mocha/minitest'
14
+ require File.dirname(__FILE__) + '/factories'
16
15
 
17
- RAILS_DEFAULT_LOGGER = Logger.new(File.dirname(__FILE__) + '/log/test.log')
18
- RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
19
- ActiveRecord::Base.logger = RAILS_DEFAULT_LOGGER
16
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
20
17
 
21
18
  class ActiveSupport::TestCase
22
- include FactoryGirl::Syntax::Methods
19
+ include FactoryBot::Syntax::Methods
23
20
  include Rexport::Factories
24
21
 
25
22
  def setup
26
- setup_db
23
+ suppress_output { setup_db }
27
24
  Enrollment.instance_variable_set('@rexport_fields', nil)
28
25
  Student.instance_variable_set('@rexport_fields', nil)
29
26
  end
@@ -32,17 +29,10 @@ class ActiveSupport::TestCase
32
29
  teardown_db
33
30
  end
34
31
 
35
- # Placeholder so test/unit ignores test cases without any tests.
36
- def default_test
37
- end
38
-
39
32
  private
40
33
 
41
34
  def setup_db
42
- old_stdout = $stdout
43
- $stdout = StringIO.new
44
-
45
- ActiveRecord::Schema.define(:version => 1) do
35
+ ActiveRecord::Schema.define(version: 1) do
46
36
  create_table :enrollments do |t|
47
37
  t.integer :student_id, :status_id, :grade
48
38
  t.boolean :active
@@ -85,8 +75,6 @@ class ActiveSupport::TestCase
85
75
  create_table :self_referential_checks do |t|
86
76
  end
87
77
  end
88
-
89
- $stdout = old_stdout
90
78
  end
91
79
 
92
80
  def teardown_db
@@ -94,6 +82,14 @@ class ActiveSupport::TestCase
94
82
  ActiveRecord::Base.connection.drop_table(table)
95
83
  end
96
84
  end
85
+
86
+ def suppress_output
87
+ original_stdout = $stdout.clone
88
+ $stdout.reopen File.new('/dev/null', 'w')
89
+ yield
90
+ ensure
91
+ $stdout.reopen original_stdout
92
+ end
97
93
  end
98
94
 
99
95
  class ActiveRecord::Base
@@ -107,7 +103,7 @@ class Enrollment < ActiveRecord::Base
107
103
  include Rexport::DataFields
108
104
  belongs_to :student
109
105
  belongs_to :status
110
- belongs_to :ilp_status, :class_name => 'Status', :foreign_key => 'ilp_status_id'
106
+ belongs_to :ilp_status, class_name: 'Status', foreign_key: 'ilp_status_id'
111
107
  belongs_to :self_referential_check
112
108
 
113
109
  def foo
@@ -116,10 +112,10 @@ class Enrollment < ActiveRecord::Base
116
112
 
117
113
  private
118
114
 
119
- def Enrollment.initialize_local_rexport_fields
120
- add_rexport_field(:foo_method, :method => :foo)
121
- add_rexport_field(:bad_method, :method => 'bad_method')
122
- add_association_methods(:associations => %w(status ilp_status))
115
+ def self.initialize_local_rexport_fields(rexport_model)
116
+ rexport_model.add_rexport_field(:foo_method, method: :foo)
117
+ rexport_model.add_rexport_field(:bad_method, method: 'bad_method')
118
+ rexport_model.add_association_methods(associations: %w(status ilp_status))
123
119
  end
124
120
  end
125
121
 
@@ -127,6 +123,10 @@ class Student < ActiveRecord::Base
127
123
  include Rexport::DataFields
128
124
  belongs_to :family
129
125
  has_many :enrollments
126
+
127
+ def self.find_family_for_rexport
128
+ Family.order(:name)
129
+ end
130
130
  end
131
131
 
132
132
  class Family < ActiveRecord::Base
@@ -139,8 +139,8 @@ class Family < ActiveRecord::Base
139
139
 
140
140
  private
141
141
 
142
- def Family.initialize_local_rexport_fields
143
- add_rexport_field(:foo_method, :method => :foo)
142
+ def self.initialize_local_rexport_fields(rexport_model)
143
+ rexport_model.add_rexport_field(:foo_method, method: :foo)
144
144
  end
145
145
  end
146
146
 
@@ -162,9 +162,6 @@ class ExportFilter < ActiveRecord::Base
162
162
  end
163
163
 
164
164
  class SelfReferentialCheck < ActiveRecord::Base
165
+ include Rexport::DataFields
165
166
  belongs_to :enrollment
166
-
167
- def SelfReferentialCheck.rexport_fields
168
- 'trick get_rexport_models into believing we are exportable'
169
- end
170
167
  end