active-list 4.2.4 → 5.0.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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/VERSION +1 -1
  3. data/{lib → app}/assets/images/active-list.png +0 -0
  4. data/app/assets/images/active-list.svg +415 -0
  5. data/{lib/assets/javascripts/active-list.jquery.js → app/assets/javascripts/active_list.jquery.js} +2 -2
  6. data/app/assets/stylesheets/active_list.css.scss +7 -0
  7. data/{lib/active-list/compass/stylesheets/active-list/_background.scss → app/assets/stylesheets/active_list/background.scss} +10 -8
  8. data/{lib/active-list/compass/stylesheets/active-list/_minimal.scss → app/assets/stylesheets/active_list/minimal.scss} +16 -16
  9. data/{lib/active-list/compass/stylesheets/active-list/_theme.scss → app/assets/stylesheets/active_list/theme.scss} +37 -37
  10. data/lib/active-list.rb +1 -37
  11. data/lib/active_list.rb +43 -0
  12. data/lib/active_list/action_pack.rb +46 -0
  13. data/lib/active_list/definition.rb +17 -0
  14. data/lib/active_list/definition/abstract_column.rb +54 -0
  15. data/lib/active_list/definition/action_column.rb +76 -0
  16. data/lib/active_list/definition/association_column.rb +80 -0
  17. data/lib/active_list/definition/attribute_column.rb +58 -0
  18. data/lib/active_list/definition/check_box_column.rb +17 -0
  19. data/lib/active_list/definition/data_column.rb +88 -0
  20. data/lib/active_list/definition/empty_column.rb +10 -0
  21. data/lib/active_list/definition/field_column.rb +20 -0
  22. data/lib/active_list/definition/status_column.rb +10 -0
  23. data/lib/active_list/definition/table.rb +159 -0
  24. data/lib/active_list/definition/text_field_column.rb +10 -0
  25. data/lib/active_list/exporters.rb +28 -0
  26. data/lib/active_list/exporters/abstract_exporter.rb +55 -0
  27. data/lib/active_list/exporters/csv_exporter.rb +32 -0
  28. data/lib/active_list/exporters/excel_csv_exporter.rb +38 -0
  29. data/lib/active_list/exporters/open_document_spreadsheet_exporter.rb +82 -0
  30. data/lib/active_list/generator.rb +122 -0
  31. data/lib/active_list/generator/finder.rb +150 -0
  32. data/lib/active_list/helpers.rb +33 -0
  33. data/lib/{active-list → active_list}/rails/engine.rb +3 -2
  34. data/lib/active_list/renderers.rb +25 -0
  35. data/lib/active_list/renderers/abstract_renderer.rb +29 -0
  36. data/lib/active_list/renderers/simple_renderer.rb +356 -0
  37. metadata +74 -55
  38. data/lib/active-list/action_pack.rb +0 -48
  39. data/lib/active-list/columns/action_column.rb +0 -83
  40. data/lib/active-list/columns/data_column.rb +0 -151
  41. data/lib/active-list/columns/field_column.rb +0 -29
  42. data/lib/active-list/compass/stylesheets/_active-list.scss +0 -7
  43. data/lib/active-list/definition.rb +0 -103
  44. data/lib/active-list/exporters.rb +0 -71
  45. data/lib/active-list/exporters/csv_exporter.rb +0 -30
  46. data/lib/active-list/exporters/excel_csv_exporter.rb +0 -36
  47. data/lib/active-list/exporters/open_document_spreadsheet_exporter.rb +0 -81
  48. data/lib/active-list/finder.rb +0 -134
  49. data/lib/active-list/generator.rb +0 -88
  50. data/lib/active-list/renderers.rb +0 -28
  51. data/lib/active-list/renderers/simple_renderer.rb +0 -335
  52. data/lib/assets/stylesheets/active-list.css.scss +0 -7
@@ -0,0 +1,54 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class AbstractColumn
6
+ attr_reader :table, :name, :id, :options
7
+
8
+ def initialize(table, name, options = {})
9
+ @table = table
10
+ @name = name.to_sym
11
+ @options = options
12
+ @hidden = !!@options.delete(:hidden)
13
+ @id = ActiveList.new_uid
14
+ end
15
+
16
+ def header_code
17
+ raise NotImplementedError, "#{self.class.name}#header_code is not implemented."
18
+ end
19
+
20
+ def hidden?
21
+ @hidden
22
+ end
23
+
24
+ def sortable?
25
+ false
26
+ end
27
+
28
+ def exportable?
29
+ false
30
+ end
31
+
32
+ # Unique identifier of the column in the application
33
+ def unique_id
34
+ "#{@table.name}-#{@name}"
35
+ end
36
+
37
+ # Uncommon but simple identifier for CSS class uses
38
+ def short_id
39
+ @id
40
+ end
41
+
42
+ alias :sort_id :name
43
+
44
+ def check_options!(options, *keys)
45
+ for key in options.keys
46
+ raise ArgumentError, "Key :#{key} is unexpected. (Expecting: #{keys.to_sentence})"
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,76 @@
1
+ # coding: utf-8
2
+ module ActiveList
3
+
4
+ module Definition
5
+
6
+ class ActionColumn < AbstractColumn
7
+ include ActiveList::Helpers
8
+
9
+ def header_code
10
+ "''".c
11
+ end
12
+
13
+ def operation(record = 'record_of_the_death')
14
+ @options[:method] = :delete if @name.to_s == "destroy" and !@options.has_key?(:method)
15
+ @options[:confirm] ||= :are_you_sure_you_want_to_delete if @name.to_s == "destroy" and !@options.has_key?(:confirm)
16
+ @options[:if] ||= :destroyable? if @name.to_s == "destroy"
17
+ @options[:if] ||= :editable? if @name.to_s == "edit"
18
+ @options[:confirm] = :are_you_sure if @options[:confirm].is_a?(TrueClass)
19
+ link_options = ""
20
+ if @options[:confirm]
21
+ link_options << ", 'data-confirm' => #{(@options[:confirm]).inspect}.t(scope: 'labels')"
22
+ end
23
+ if @options['data-method'] or @options[:method]
24
+ link_options << ", :method => h('#{(@options['data-method']||@options[:method])}')"
25
+ end
26
+ action = @name
27
+ format = @options[:format] ? ", :format => '#{@options[:format]}'" : ""
28
+ if @options[:remote]
29
+ raise StandardError, "Sure to use :remote ?"
30
+ # remote_options = @options.dup
31
+ # remote_options['data-confirm'] = "#{@options[:confirm].inspect}.tl".c unless @options[:confirm].nil?
32
+ # remote_options.delete :remote
33
+ # remote_options.delete :image
34
+ # remote_options = remote_options.inspect.to_s
35
+ # remote_options = remote_options[1..-2]
36
+ # code = "link_to_remote(#{image}"
37
+ # code += ", {url: {action: "+@name.to_s+", id: "+record+".id"+format+"}"
38
+ # code += ", "+remote_options+"}"
39
+ # code += ", {title: #{action.inspect}.tl}"
40
+ # code += ")"
41
+ elsif @options[:actions]
42
+ unless @options[:actions].is_a? Hash
43
+ raise StandardError, "options[:actions] have to be a Hash."
44
+ end
45
+ cases = []
46
+ for expected, url in @options[:actions]
47
+ cases << record+"."+@name.to_s+" == " + expected.inspect + "\nlink_to(content_tag(:i) + h(#{url[:action].inspect}.t(scope: 'labels'))"+
48
+ ", {"+(url[:controller] ? 'controller: :'+url[:controller].to_s+', ' : '')+"action: '"+url[:action].to_s+"', id: "+record+".id"+format+"}"+
49
+ ", {:class => '#{@name}'"+link_options+"}"+
50
+ ")\n"
51
+ end
52
+
53
+ code = "if "+cases.join("elsif ")+"end"
54
+ else
55
+ url = @options[:url] ||= {}
56
+ url[:controller] ||= (@options[:controller] || "RECORD.class.name.tableize".c) # self.table.model.name.underscore.pluralize.to_s
57
+ url[:action] ||= @name.to_s
58
+ url[:id] ||= "RECORD.id".c
59
+ url.delete_if{|k, v| v.nil?}
60
+ url = "{" + url.collect{|k, v| "#{k}: " + urlify(v, record)}.join(", ")+format+"}"
61
+ code = "{class: '#{@name}'"+link_options+"}"
62
+ code = "link_to(content_tag(:i) + h('#{action}'.t(scope: 'labels')), "+url+", "+code+")"
63
+ end
64
+ if @options[:if]
65
+ code = "if " + recordify!(@options[:if], record) + "\n" + code.dig + "end"
66
+ end
67
+ if @options[:unless]
68
+ code = "unless " + recordify!(@options[:unless], record) + "\n" + code.dig + "end"
69
+ end
70
+ code.c
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,80 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class AssociationColumn < DataColumn
6
+
7
+ attr_reader :label_method, :reflection
8
+
9
+ def initialize(table, name, options = {})
10
+ super(table, name, options)
11
+ unless @options[:through]
12
+ raise ArgumentError, "Option :through must be given"
13
+ end
14
+ reflection_name = @options.delete(:through).to_sym
15
+ if @reflection = @table.model.reflect_on_association(reflection_name)
16
+ if @reflection.macro == :belongs_to
17
+ # Do some stuff
18
+ elsif @reflection.macro == :has_one
19
+ # Do some stuff
20
+ else
21
+ raise ArgumentError, "Only belongs_to are usable. Can't handle: #{reflection.macro} :#{reflection.name}."
22
+ end
23
+ else
24
+ raise UnknownReflection, "Reflection #{reflection_name} cannot be found for #{table.model.name}."
25
+ end
26
+ columns_def = @reflection.class_name.constantize.columns_hash.keys.map(&:to_sym)
27
+ unless @label_method = @options.delete(:label_method)
28
+ columns = columns_def + @reflection.class_name.constantize.instance_methods.map(&:to_sym)
29
+ unless @label_method = LABELS_COLUMNS.detect{|m| columns.include?(m)}
30
+ raise ArgumentError, ":label_method option must be given for association #{name}. (#{columns.inspect})"
31
+ end
32
+ end
33
+ unless @sort_column = @options.delete(:sort)
34
+ if columns_def.include?(@label_method)
35
+ @sort_column = @label_method
36
+ else
37
+ unless @sort_column = LABELS_COLUMNS.detect{|m| columns_def.include?(m)}
38
+ @sort_column = :id
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ # Code for rows
46
+ def datum_code(record = 'record_of_the_death', child = false)
47
+ code = ""
48
+ if child
49
+ code = "nil"
50
+ # if @options[:children].is_a?(FalseClass)
51
+ # code = "nil"
52
+ # else
53
+ # code = "#{record}.#{table.options[:children]}.#{@reflection.name}.#{@options[:children] || @label_method}"
54
+ # end
55
+ else
56
+ code = "(#{record}.#{@reflection.name} ? #{record}.#{@reflection.name}.#{@label_method} : nil)"
57
+ end
58
+ return code.c
59
+ end
60
+
61
+ def class_name
62
+ return @reflection.class_name
63
+ end
64
+
65
+ def record_expr(record = 'record_of_the_death')
66
+ return "#{record}.#{@reflection.name}"
67
+ end
68
+
69
+ def sort_expression
70
+ if table.reflections.select{|r| r.table_name == @reflection.table_name}.size > 1
71
+ "#{@reflection.name.to_s.pluralize}_#{@reflection.class_name.constantize.table_name}.#{@sort_column}"
72
+ else
73
+ "#{@reflection.class_name.constantize.table_name}.#{@sort_column}"
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,58 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class AttributeColumn < DataColumn
6
+
7
+ attr_reader :column, :label_method, :sort_column
8
+
9
+ def initialize(table, name, options = {})
10
+ super(table, name, options)
11
+ @label_method = (options[:label_method] || @name).to_sym
12
+ unless @sort_column = options[:sort]
13
+ if @table.model.columns_hash[@label_method]
14
+ @sort_column = @label_method
15
+ elsif @table.model.columns_hash[@name]
16
+ @sort_column = @name
17
+ else
18
+ @sort_column = :id
19
+ end
20
+ end
21
+ @column = @table.model.columns_hash[@label_method.to_s]
22
+ end
23
+
24
+ # Code for rows
25
+ def datum_code(record = 'record_of_the_death', child = false)
26
+ code = ""
27
+ if child
28
+ if @options[:children].is_a?(FalseClass)
29
+ code = "nil"
30
+ else
31
+ code = "#{record}.#{table.options[:children]}.#{@options[:children] || @label_method}"
32
+ end
33
+ else
34
+ code = "#{record}.#{@label_method}"
35
+ end
36
+ return code.c
37
+ end
38
+
39
+ # Returns the class name of the used model
40
+ def class_name
41
+ return self.table.model.name
42
+ end
43
+
44
+ def enumerize?
45
+ self.table.model.send(@label_method).send(:values)
46
+ return true
47
+ rescue
48
+ return false
49
+ end
50
+
51
+ def sort_expression
52
+ "#{@table.model.table_name}.#{@sort_column}"
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class CheckBoxColumn < FieldColumn
6
+ attr_reader :form_value
7
+
8
+ def initialize(table, name, options = {})
9
+ super(table, name, options)
10
+ @form_value = options.delete(:form_value)
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,88 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class DataColumn < AbstractColumn
6
+
7
+ LABELS_COLUMNS = [:full_name, :label, :name, :number, :coordinate]
8
+
9
+ def header_code
10
+ if @options[:label]
11
+ "#{@options[:label].to_s.strip.inspect}.t(scope: 'labels')".c
12
+ else
13
+ "#{@table.model.name}.human_attribute_name(#{@name.inspect})".c
14
+ end
15
+ end
16
+
17
+ # Code for exportation
18
+ def exporting_datum_code(record='record_of_the_death', noview=false)
19
+ datum = self.datum_code(record)
20
+ if self.datatype == :boolean
21
+ datum = "(#{datum} ? ::I18n.translate('list.export.true_value') : ::I18n.translate('list.export.false_value'))"
22
+ elsif self.datatype == :date
23
+ datum = "(#{datum}.nil? ? '' : #{datum}.l)"
24
+ elsif self.datatype == :decimal and not noview
25
+ currency = nil
26
+ if currency = self.options[:currency]
27
+ currency = currency[:body] if currency.is_a?(Hash)
28
+ currency = :currency if currency.is_a?(TrueClass)
29
+ currency = "#{record}.#{currency}".c if currency.is_a?(Symbol)
30
+ end
31
+ datum = "(#{datum}.nil? ? '' : #{datum}.l(#{'currency: ' + currency.inspect if currency}))"
32
+ elsif @name.to_s.match(/(^|\_)currency$/) and self.datatype == :string
33
+ datum = "(Nomen::Currencies[#{datum}] ? Nomen::Currencies[#{datum}].human_name : '')"
34
+ elsif @name.to_s.match(/(^|\_)country$/) and self.datatype == :string
35
+ datum = "(Nomen::Countries[#{datum}] ? Nomen::Countries[#{datum}].human_name : '')"
36
+ elsif @name.to_s.match(/(^|\_)language$/) and self.datatype == :string
37
+ datum = "(Nomen::Languages[#{datum}] ? Nomen::Languages[#{datum}].human_name : '')"
38
+ elsif self.enumerize?
39
+ datum = "(#{datum}.nil? ? '' : #{datum}.text)"
40
+ end
41
+ return datum
42
+ end
43
+
44
+ # Returns the data type of the column if the column is in the database
45
+ def datatype
46
+ @options[:datatype] || (@column ? @column.type : nil)
47
+ end
48
+
49
+
50
+ def enumerize?
51
+ return false
52
+ end
53
+
54
+ def numeric?
55
+ [:decimal, :integer, :float, :numeric].include? self.datatype
56
+ end
57
+
58
+ # Returns the size/length of the column if the column is in the database
59
+ def limit
60
+ @column[:limit] if @column
61
+ end
62
+
63
+ # Defines if column is exportable
64
+ def exportable?
65
+ true
66
+ end
67
+
68
+ # Check if a column is sortable
69
+ def sortable?
70
+ return true
71
+ # not self.action? and
72
+ not self.options[:through] and not @column.nil?
73
+ end
74
+
75
+ # Generate code in order to get the (foreign) record of the column
76
+ def record_expr(record = 'record_of_the_death')
77
+ return record
78
+ end
79
+
80
+ def sort_expression
81
+ raise NotImplementedError, "sort_expression must be implemented"
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
88
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class EmptyColumn < AbstractColumn
6
+ end
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class FieldColumn < AbstractColumn
6
+ attr_reader :form_name
7
+
8
+ def initialize(table, name, options = {})
9
+ super(table, name, options)
10
+ @form_name = options.delete(:form_name)
11
+ end
12
+
13
+ def header_code
14
+ "#{@table.model.name}.human_attribute_name('#{@name}')"
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class StatusColumn < AttributeColumn
6
+ end
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,159 @@
1
+ module ActiveList
2
+
3
+ module Definition
4
+
5
+ class Table
6
+ attr_reader :name, :model, :options, :id, :columns, :parameters
7
+
8
+ def initialize(name, model = nil, options = {})
9
+ @name = name
10
+ @model = model || name.to_s.classify.constantize
11
+ @options = options
12
+ @paginate = !(@options[:pagination]==:none || @options[:paginate].is_a?(FalseClass))
13
+ @options[:renderer] ||= :simple_renderer
14
+ @options[:per_page] = 20 if @options[:per_page].to_i <= 0
15
+ @options[:page] = 1 if @options[:page].to_i <= 0
16
+ @columns = []
17
+ @id = ActiveList.new_uid
18
+ end
19
+
20
+ def new_column_id
21
+ @current_column_id ||= 0
22
+ id = @current_column_id.to_s(36).to_sym
23
+ @current_column_id += 1
24
+ return id
25
+ end
26
+
27
+ def model_columns
28
+ @model.columns_hash.values
29
+ end
30
+
31
+ def sortable_columns
32
+ @columns.select{|c| c.sortable?}
33
+ end
34
+
35
+ def exportable_columns
36
+ @columns.select{|c| c.exportable?}
37
+ end
38
+
39
+ def children
40
+ @columns.map(&:child)
41
+ end
42
+
43
+ def paginate?
44
+ @paginate
45
+ end
46
+
47
+ # Retrieves all columns in database
48
+ def table_columns
49
+ cols = self.model_columns.map(&:name)
50
+ @columns.select{|c| c.is_a? DataColumn and cols.include? c.name.to_s}
51
+ end
52
+
53
+ def data_columns
54
+ @columns.select{|c| c.is_a? DataColumn}
55
+ end
56
+
57
+ def hidden_columns
58
+ self.data_columns.select(&:hidden?)
59
+ end
60
+
61
+ # Compute includes Hash
62
+ def reflections
63
+ hash = []
64
+ for column in self.columns
65
+ if column.respond_to?(:reflection)
66
+ unless hash.detect{|r| r.name == column.reflection.name }
67
+ hash << column.reflection
68
+ end
69
+ end
70
+ end
71
+ return hash
72
+ end
73
+
74
+
75
+ # Add a new method in Table which permit to define text_field columns
76
+ def text_field(name, options = {})
77
+ add :text_field, name, options
78
+ end
79
+
80
+ # Add a new method in Table which permit to define check_box columns
81
+ def check_box(name, options = {})
82
+ add :check_box, name, options
83
+ end
84
+
85
+ # Add a new method in Table which permit to define action columns
86
+ def action(name, options = {})
87
+ add :action, name, options
88
+ end
89
+
90
+ # # Add a new method in Table which permit to define data columns
91
+ # def attribute(name, options = {})
92
+ # add :attribute, name, options
93
+ # end
94
+
95
+ # # Add a column referencing an association
96
+ # def association(name, options = {})
97
+ # options[:through] ||= name
98
+ # add :association, name, options
99
+ # end
100
+
101
+ # Add a new method in Table which permit to define data columns
102
+ def column(name, options = {})
103
+ if @model.reflect_on_association(name)
104
+ options[:through] ||= name
105
+ add :association, name, options
106
+ elsif @model.reflect_on_association(options[:through])
107
+ options[:label_method] ||= name
108
+ add :association, name, options
109
+ else
110
+ add :attribute, name, options
111
+ end
112
+ end
113
+
114
+ def status(*args)
115
+ options = args.extract_options!
116
+ name = args.shift || :status
117
+ add :status, name, options
118
+ end
119
+
120
+
121
+ def load_default_columns
122
+ for column in self.model_columns
123
+ reflections = @model.reflections.values.select{|r| r.macro == :belongs_to and r.foreign_key.to_s == column.name.to_s}
124
+ if reflections.size == 1
125
+ reflection = reflections.first
126
+ columns = reflection.class_name.constantize.columns.collect{ |c| c.name.to_s }
127
+ self.column([:label, :name, :code, :number].detect{ |l| columns.include?(l.to_s) }, :through => reflection.name, :url => true)
128
+ else
129
+ self.column(column.name)
130
+ end
131
+ end
132
+ return true
133
+ end
134
+
135
+ private
136
+
137
+ # Checks and add column
138
+ def add(type, name, options = {})
139
+ klass = "ActiveList::Definition::#{type.to_s.camelcase}Column".constantize rescue nil
140
+ if klass and klass < AbstractColumn
141
+ unless name.is_a?(Symbol)
142
+ raise ArgumentError, "Name of a column must be a Symbol (got #{name.inspect})."
143
+ end
144
+ if @columns.detect{|c| c.name == name}
145
+ raise ArgumentError, "Column name must be unique. #{name.inspect} is already used in #{self.name}"
146
+ end
147
+ @columns << klass.new(self, name, options)
148
+ else
149
+ raise ArgumentError, "Invalid column type: #{type.inspect}"
150
+ end
151
+ end
152
+
153
+
154
+ end
155
+
156
+
157
+ end
158
+
159
+ end