active_list 7.0.0 → 8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: '022092abfbb1ef56a9e935ba1f815b5f13dd7fc4'
4
- data.tar.gz: c15a78e14c48e10bfe281e2f735e5b3bc9084ea7
2
+ SHA256:
3
+ metadata.gz: 27436c5734c9477fdef8d87ba8d8baf417a3c6a63e74dcbe6461f02236139452
4
+ data.tar.gz: 9da5f80f7bd3617a7bb71dc0266c23c943180523a7f64ffa1d522afdc08f7b5e
5
5
  SHA512:
6
- metadata.gz: 51bed46ba112355efe2aadbeae3e652296fbe9071d63d915a73d4189b87c21b933c44b3af2eefc14032827bcf13e5e370f6310f6f30dc66c30ddbaf658330013
7
- data.tar.gz: edc59cfcb98fa9f621d67f1f6b593657ca2e1ede60c22631884c0ec749ed455e5ff0e8724c6559e2a2e8a915288d22311d8d72a6247aaea69687b7df29eb5fd5
6
+ metadata.gz: 6b1e434947929a963d81ad16c5fc34cae590f1010139da5862ad43aebc18f32ea946b378636c516939d7b2d0c6fa1074cf4e3a09364ebbabdbc6eef3cd85010e
7
+ data.tar.gz: e6344a3442a0c812667cd07ec35504e4545e526d4acb76bc27f02d4cbccf8205e52efccec42f8719a8f09bf25d0c3f8965ccc74b698e2a015a79f37904ccf2fc
@@ -158,6 +158,11 @@ ActiveList = {}
158
158
 
159
159
  # Select row
160
160
  $(document).on "click", "*[data-list-source] td>input[data-list-selector]", (event) ->
161
+ mainCheckbox = $("input[data-list-selector]").first()
162
+ someChecked = $("*[data-list-source] td>input[data-list-selector]:checked").length > 0
163
+ someUnchecked = $("*[data-list-source] td>input[data-list-selector]:not(:checked)").length > 0
164
+ mainCheckbox.prop("indeterminate", someChecked && someUnchecked);
165
+ mainCheckbox.prop("checked", someChecked || !someUnchecked);
161
166
  AL.select $(this)
162
167
  true
163
168
 
@@ -1,13 +1,14 @@
1
1
  module ActiveList
2
2
  module Definition
3
3
  class AbstractColumn
4
- attr_reader :table, :name, :id, :options
4
+ attr_reader :table, :name, :id, :options, :condition
5
5
 
6
6
  def initialize(table, name, options = {})
7
7
  @table = table
8
8
  @name = name.to_sym
9
9
  @options = options
10
10
  @hidden = !!@options.delete(:hidden)
11
+ @condition = @options.delete(:condition)
11
12
  @id = 'c' + @table.new_column_id # ActiveList.new_uid
12
13
  end
13
14
 
@@ -94,8 +94,8 @@ module ActiveList
94
94
  url[:redirect] ||= 'params[:redirect]'.c
95
95
  url.delete_if { |_k, v| v.nil? }
96
96
  cases << "#{record}.#{@name} == #{expected.inspect}\nlink_to(content_tag(:i) + h(#{url[:action].inspect}.t(scope: 'rest.actions'))"\
97
- ', {' + url.collect { |k, v| "#{k}: " + urlify(v, record) }.join(', ') + format + '}' \
98
- ", {class: '#{@name}'" + link_options + '}'\
97
+ ', {' + url.collect { |k, v| "#{k}: " + urlify(k, v, record) }.join(', ') + format + '}' \
98
+ ", {class: '#{@options[:icon_name] || @name}'" + link_options + '}'\
99
99
  ")\n"
100
100
  end
101
101
 
@@ -108,8 +108,8 @@ module ActiveList
108
108
  url[:id] = 'RECORD.id'.c if url[:id] == ID_PLACEHOLDER
109
109
  url[:redirect] ||= 'params[:redirect]'.c
110
110
  url.delete_if { |_k, v| v.nil? }
111
- url = '{' + url.collect { |k, v| "#{k}: " + urlify(v, record) }.join(', ') + format + '}'
112
- code = "{class: '#{@name}'" + link_options + '}'
111
+ url = '{' + url.collect { |k, v| "#{k}: " + urlify(k, v, record) }.join(', ') + format + '}'
112
+ code = "{class: '#{@options[:icon_name] || @name}'" + link_options + '}'
113
113
  code = "link_to(content_tag(:i) + h(' ' + :#{action}.t(scope: 'rest.actions')), " + url + ', ' + code + ')'
114
114
  end
115
115
  if @options[:if]
@@ -11,13 +11,7 @@ module ActiveList
11
11
  else
12
12
  @label_method.to_s.gsub('human_', '').to_sym
13
13
  end
14
- unless @sort_column = options[:sort]
15
- @sort_column = if @table.model.columns_hash[@label_method.to_s]
16
- @label_method
17
- elsif @table.model.columns_hash[@name.to_s]
18
- @name
19
- end
20
- end
14
+ @sort_column = get_sort_column
21
15
  @computation_method = options[:on_select]
22
16
  @column = @table.model.columns_hash[@label_method.to_s]
23
17
  end
@@ -51,9 +45,27 @@ module ActiveList
51
45
  code.c
52
46
  end
53
47
 
48
+ def get_sort_column
49
+ selects = @table.options[:select] || {}
50
+ selects_label = selects.find { |sql_name, name| name.to_s == @label_method.to_s }&.last
51
+ selects_name = selects.find { |sql_name, name| name.to_s == @name.to_s }&.last
52
+ if (selects_label || selects_name) && options[:sort].blank?
53
+ sort_column = (selects_label || selects_name)
54
+ else
55
+ sort_column = options[:sort]
56
+ sort_column ||= if @table.model.columns_hash[@label_method.to_s]
57
+ @label_method
58
+ elsif @table.model.columns_hash[@name.to_s]
59
+ @name
60
+ end
61
+ sort_column &&= "#{@table.model.table_name}.#{sort_column}"
62
+ end
63
+ sort_column
64
+ end
65
+
54
66
  # Returns the class name of the used model
55
67
  def class_name
56
- table.model.name
68
+ "RECORD.class.name.tableize".c
57
69
  end
58
70
 
59
71
  def sortable?
@@ -70,7 +82,7 @@ module ActiveList
70
82
  end
71
83
 
72
84
  def sort_expression
73
- "#{@table.model.table_name}.#{@sort_column}"
85
+ @sort_column
74
86
  end
75
87
  end
76
88
  end
@@ -22,11 +22,11 @@ module ActiveList
22
22
  currency = currency_for(record)
23
23
  datum = "(#{datum}.nil? ? '' : #{datum}.l(#{'currency: ' + currency.inspect if currency}))"
24
24
  elsif @name.to_s.match(/(^|\_)currency$/) && datatype == :string
25
- datum = "(Nomen::Currency[#{datum}] ? Nomen::Currency[#{datum}].human_name : '')"
25
+ datum = "(Onoma::Currency[#{datum}] ? Onoma::Currency[#{datum}].human_name : '')"
26
26
  elsif @name.to_s.match(/(^|\_)country$/) && datatype == :string
27
- datum = "(Nomen::Country[#{datum}] ? Nomen::Country[#{datum}].human_name : '')"
27
+ datum = "(Onoma::Country[#{datum}] ? Onoma::Country[#{datum}].human_name : '')"
28
28
  elsif @name.to_s.match(/(^|\_)language$/) && datatype == :string
29
- datum = "(Nomen::Language[#{datum}] ? Nomen::Language[#{datum}].human_name : '')"
29
+ datum = "(Onoma::Language[#{datum}] ? Onoma::Language[#{datum}].human_name : '')"
30
30
  elsif enumerize?
31
31
  datum = "(#{datum}.nil? ? '' : #{datum}.text)"
32
32
  end
@@ -1,6 +1,10 @@
1
1
  module ActiveList
2
2
  module Definition
3
3
  class EmptyColumn < AbstractColumn
4
+
5
+ def header_code
6
+ "#{name.to_s.strip.inspect}.t(scope: 'labels')".c
7
+ end
4
8
  end
5
9
  end
6
10
  end
@@ -1,6 +1,19 @@
1
1
  module ActiveList
2
2
  module Definition
3
3
  class StatusColumn < AttributeColumn
4
+ def initialize(table, name, options = {})
5
+ super
6
+
7
+ @tooltip_method = options.fetch(:tooltip_method, nil)
8
+ end
9
+
10
+ def tooltip_title_code(record, child)
11
+ c = if @tooltip_method.nil?
12
+ "#{record}.try(:human_status) || #{record}&.try(:human_state_name) || #{datum_value(record, child)}.to_s.capitalize"
13
+ else
14
+ "#{record}.#{@tooltip_method}"
15
+ end
16
+ end
4
17
  end
5
18
  end
6
19
  end
@@ -103,6 +103,10 @@ module ActiveList
103
103
  add :action, name, options
104
104
  end
105
105
 
106
+ # Add a new method in Table which permit to define empty columns
107
+ def empty(name, options = {})
108
+ add :empty, name, options
109
+ end
106
110
  # # Add a new method in Table which permit to define data columns
107
111
  # def attribute(name, options = {})
108
112
  # add :attribute, name, options
@@ -12,12 +12,39 @@ module ActiveList
12
12
  'txt'
13
13
  end
14
14
 
15
+ def file_name_code
16
+ "file_name = #{table.model.name}.model_name.human\n".c
17
+ end
18
+
15
19
  def mime_type
16
20
  Mime::TEXT
17
21
  end
18
22
 
23
+ def generate_file_code(format)
24
+ code = file_name_code
25
+ if generator.export_class
26
+ code << generator.exportable_query_code
27
+ code << "#{generator.export_class}.perform_later(user: current_user,\n"
28
+ code << " query: query,\n"
29
+ code << " content: #{columns_to_hash},\n"
30
+ code << " file_name: file_name,\n"
31
+ code << " format: '#{format}',\n"
32
+ code << " file_extension: '#{file_extension}')\n"
33
+ code << "notify_success(:document_in_preparation)\n"
34
+ code << "redirect_to(:back)\n"
35
+ else
36
+ code << generate_data_code
37
+ code << send_data_code
38
+ end
39
+ code.c
40
+ end
41
+
19
42
  def send_data_code
20
- raise NotImplementedError, "#{self.class.name}#format_data_code is not implemented."
43
+ raise NotImplementedError.new("#{self.class.name}#send_data_code must be implemented in sub-classes.")
44
+ end
45
+
46
+ def generate_data_code
47
+ raise NotImplementedError.new("#{self.class.name}#generate_data_code must be implemented in sub-classes.")
21
48
  end
22
49
 
23
50
  def columns_headers(options = {})
@@ -46,6 +73,12 @@ module ActiveList
46
73
  end
47
74
  array
48
75
  end
76
+
77
+ def columns_to_hash
78
+ table.exportable_columns.map do |column|
79
+ [column.header_code, column.exporting_datum_code('record').to_s]
80
+ end.to_h
81
+ end
49
82
  end
50
83
  end
51
84
  end
@@ -1,6 +1,7 @@
1
1
  module ActiveList
2
2
  module Exporters
3
3
  class CsvExporter < AbstractExporter
4
+
4
5
  def file_extension
5
6
  'csv'
6
7
  end
@@ -9,7 +10,7 @@ module ActiveList
9
10
  Mime[:csv]
10
11
  end
11
12
 
12
- def send_data_code
13
+ def generate_data_code
13
14
  record = 'r'
14
15
  code = generator.select_data_code(paginate: false)
15
16
  code << "data = ::CSV.generate do |csv|\n"
@@ -18,9 +19,12 @@ module ActiveList
18
19
  code << " csv << [#{columns_to_array(:body, record: record).join(', ')}]\n"
19
20
  code << " end\n"
20
21
  code << "end\n"
21
- code << "send_data(data, type: #{mime_type.to_s.inspect}, disposition: 'inline', filename: #{table.model.name}.model_name.human.gsub(/[^a-z0-9]/i, '_') + '.#{file_extension}')\n"
22
22
  code.c
23
23
  end
24
+
25
+ def send_data_code
26
+ "send_data(data, type: #{mime_type.to_s.inspect}, disposition: 'inline', filename: file_name.parameterize + '.#{file_extension}')\n".c
27
+ end
24
28
  end
25
29
  end
26
30
  end
@@ -4,6 +4,7 @@ Mime::Type.register('text/csv', :xcsv) unless defined? Mime::XCSV
4
4
  module ActiveList
5
5
  module Exporters
6
6
  class ExcelCsvExporter < CsvExporter
7
+
7
8
  def file_extension
8
9
  'csv'
9
10
  end
@@ -12,7 +13,7 @@ module ActiveList
12
13
  Mime[:xcsv]
13
14
  end
14
15
 
15
- def send_data_code
16
+ def generate_data_code
16
17
  record = 'r'
17
18
  code = generator.select_data_code(paginate: false)
18
19
  encoding = 'CP1252'
@@ -22,7 +23,6 @@ module ActiveList
22
23
  code << " csv << [#{columns_to_array(:body, record: record, encoding: encoding).join(', ')}]\n"
23
24
  code << " end\n"
24
25
  code << "end\n"
25
- code << "send_data(data, type: #{mime_type.to_s.inspect}, disposition: 'inline', filename: #{table.model.name}.model_name.human.gsub(/[^a-z0-9]/i,'_')+'.#{file_extension}')\n"
26
26
  code.c
27
27
  end
28
28
  end
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'zip'
3
+ require 'rodf'
4
4
 
5
5
  # Register ODS format unless is already set
6
6
  Mime::Type.register('application/vnd.oasis.opendocument.spreadsheet', :ods) unless defined? Mime::ODS
@@ -8,11 +8,6 @@ Mime::Type.register('application/vnd.oasis.opendocument.spreadsheet', :ods) unle
8
8
  module ActiveList
9
9
  module Exporters
10
10
  class OpenDocumentSpreadsheetExporter < AbstractExporter
11
- DATE_ELEMENTS = {
12
- 'm' => '<number:month number:style="long"/>',
13
- 'd' => '<number:day number:style="long"/>',
14
- 'Y' => '<number:year/>'
15
- }.freeze
16
11
 
17
12
  def file_extension
18
13
  'ods'
@@ -22,55 +17,42 @@ module ActiveList
22
17
  Mime[:ods]
23
18
  end
24
19
 
25
- def send_data_code
26
- xml_escape = "to_s.gsub('&', '&amp;').gsub('\\'', '&apos;').gsub('<', '&lt;').gsub('>', '&gt;')"
27
- xml_escape << ".force_encoding('US-ASCII')" if xml_escape.respond_to?(:force_encoding)
20
+ def generate_data_code
28
21
  record = 'r'
29
- code = generator.select_data_code(paginate: false)
30
- code << "name = #{table.model.name}.model_name.human.gsub(/[^a-z0-9]/i, '_')\n"
31
- code << "file = ActiveList.temporary_directory.join(name + rand(999_999_999).to_s(36) + '.#{file_extension}')\n"
32
- code << "FileUtils.mkdir_p(file.dirname)\n"
33
- code << "Zip::OutputStream.open(file) do |zile|\n"
34
- # MimeType in first place
35
- code << " zile.put_next_entry('mimetype', nil, nil, Zip::Entry::STORED)\n"
36
- code << " zile << '#{mime_type}'\n"
37
-
38
- # Manifest
39
- code << " zile.put_next_entry('META-INF/manifest.xml')\n"
40
- code << " zile << ('<?xml version=\"1.0\" encoding=\"UTF-8\"?><manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\"><manifest:file-entry manifest:media-type=\"#{mime_type}\" manifest:full-path=\"/\"/><manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/></manifest:manifest>')\n"
41
- code << " zile.put_next_entry('content.xml')\n"
42
22
 
43
- code << " zile << ('<?xml version=\"1.0\" encoding=\"UTF-8\"?><office:document-content xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\" xmlns:math=\"http://www.w3.org/1998/Math/MathML\" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\" xmlns:ooo=\"http://openoffice.org/2004/office\" xmlns:ooow=\"http://openoffice.org/2004/writer\" xmlns:oooc=\"http://openoffice.org/2004/calc\" xmlns:dom=\"http://www.w3.org/2001/xml-events\" xmlns:xforms=\"http://www.w3.org/2002/xforms\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:field=\"urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:field:1.0\" office:version=\"1.1\"><office:scripts/>')\n"
44
- # Styles
45
- code << " zile << ('<office:automatic-styles>"\
46
- '<style:style style:name="co1" style:family="table-column"><style:table-column-properties fo:break-before="auto" style:use-optimal-column-width="true"/></style:style>'\
47
- '<style:style style:name="header" style:family="table-cell"><style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/></style:style>'\
48
- '<number:date-style style:name="K4D" number:automatic-order="true"><number:text>' + ::I18n.translate('date.formats.default').gsub(/\%./) { |x| '</number:text>' + DATE_ELEMENTS[x[1..1]] + '<number:text>' } + '</number:text></number:date-style><style:style style:name="ce1" style:family="table-cell" style:data-style-name="K4D"/>'\
49
- "</office:automatic-styles>')\n"
23
+ code = generator.select_data_code(paginate: false)
24
+ code << <<~RUBY
25
+ records = #{generator.records_variable_name}
26
+ data = RODF::Spreadsheet.new
27
+
28
+ data.instance_eval do
29
+ office_style :head, family: :cell do
30
+ property :text, 'font-weight': :bold
31
+ property :paragraph, 'text-align': :center
32
+ end
33
+
34
+ table #{table.model.name}.model_name.human do
35
+ row do
36
+ #{columns_to_array(:header)}.each do |header|
37
+ cell header, style: :head
38
+ end
39
+ end
40
+
41
+ for #{record} in records
42
+ row do
43
+ #{columns_to_array(:body, record: record)}.each do |value|
44
+ cell value
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ RUBY
51
+ code.c
52
+ end
50
53
 
51
- # Tables
52
- code << " zile << ('<office:body><office:spreadsheet><table:table table:name=\"'+#{table.model.name}.model_name.human.#{xml_escape}+'\">')\n"
53
- code << " zile << ('<table:table-column table:number-columns-repeated=\"#{table.exportable_columns.size}\"/>')\n"
54
- code << " zile << ('<table:table-header-rows><table:table-row>" + columns_headers.collect { |h| "<table:table-cell table:style-name=\"header\" office:value-type=\"string\"><text:p>'+(#{h}).#{xml_escape}+'</text:p></table:table-cell>" }.join + "</table:table-row></table:table-header-rows>')\n"
55
- code << " for #{record} in #{generator.records_variable_name}\n"
56
- code << " zile << ('<table:table-row>" + table.exportable_columns.collect do |column|
57
- '<table:table-cell' + (if column.numeric? || column.datatype == :decimal
58
- " office:value-type=\"float\" office:value=\"'+(#{column.datum_code(record)}).#{xml_escape}+'\""
59
- elsif column.datatype == :boolean
60
- " office:value-type=\"boolean\" office:boolean-value=\"'+(#{column.datum_code(record)}).#{xml_escape}+'\""
61
- elsif column.datatype == :date
62
- " office:value-type=\"date\" table:style-name=\"ce1\" office:date-value=\"'+(#{column.datum_code(record)}).#{xml_escape}+'\""
63
- else
64
- ' office:value-type="string"'
65
- end) + "><text:p>'+(" + column.exporting_datum_code(record, true) + ").#{xml_escape}+'</text:p></table:table-cell>"
66
- end.join + "</table:table-row>')\n"
67
- code << " end\n"
68
- code << " zile << ('</table:table></office:spreadsheet></office:body></office:document-content>')\n"
69
- code << "end\n"
70
- code << "send_file(file, stream: false, type: #{mime_type.to_s.inspect}, disposition: 'inline', filename: name+'.#{file_extension}')\n"
71
- # code << "File.delete(file)\n" # Removes tmp files before they explode the disk
72
- # raise code
73
- code
54
+ def send_data_code
55
+ "send_data(data.bytes, type: #{mime_type.to_s.inspect}, disposition: 'inline', filename: file_name.parameterize + '.#{file_extension}')\n".c
74
56
  end
75
57
  end
76
58
  end
@@ -1,17 +1,18 @@
1
1
  module ActiveList
2
2
  class Generator
3
- attr_accessor :table, :controller, :controller_method_name, :view_method_name, :records_variable_name
3
+ attr_accessor :table, :controller, :controller_method_name, :view_method_name, :records_variable_name, :export_class
4
4
 
5
5
  def initialize(*args, &_block)
6
6
  options = args.extract_options!
7
7
  @controller = options[:controller]
8
8
  name = args.shift || @controller.controller_name.to_sym
9
9
  model = (options[:model] || name).to_s.classify.constantize
10
- @collection = !!(model.name == @controller.controller_name.to_s.classify)
10
+ @collection = options[:collection] || !!(model.name == @controller.controller_name.to_s.classify)
11
11
  @controller_method_name = "list#{'_' + name.to_s if name != @controller.controller_name.to_sym}"
12
12
  @view_method_name = "_#{@controller.controller_name}_list_#{name}_tag"
13
13
  @records_variable_name = "@#{name}"
14
14
  @table = ActiveList::Definition::Table.new(name, model, options)
15
+ @export_class = options[:export_class]
15
16
  if block_given?
16
17
  yield @table
17
18
  else
@@ -47,7 +48,7 @@ module ActiveList
47
48
  code << " end\n"
48
49
  for format, exporter in ActiveList::Exporters.hash
49
50
  code << " format.#{format} do\n"
50
- code << exporter.new(self).send_data_code.dig(3)
51
+ code << exporter.new(self).generate_file_code(format).dig(3)
51
52
  code << " end\n"
52
53
  end
53
54
  code << " end\n"
@@ -10,8 +10,8 @@ module ActiveList
10
10
  @table.options[:order] = (columns.any? ? columns.first.name.to_sym : { id: :desc })
11
11
  end
12
12
 
13
- class_name = @table.model.name
14
- class_name = "(controller_name != '#{class_name.tableize}' ? controller_name.to_s.classify.constantize : #{class_name})" if collection?
13
+ class_name = "options[\"constant_name\"]&.constantize || #{@table.model.name}"
14
+ class_name = "(controller_name != '#{class_name.tableize}' && !options[\"constant_name\"] ? controller_name.to_s.classify.constantize : #{class_name})" if collection?
15
15
 
16
16
  # Find data
17
17
  query_code = class_name.to_s
@@ -34,7 +34,6 @@ module ActiveList
34
34
  else
35
35
  "#{var_name(:count)} = #{query_code}.count\n"
36
36
  end
37
-
38
37
  query_code << ".group(#{@table.options[:group].inspect})" unless @table.options[:group].blank?
39
38
  query_code << ".reorder(#{var_name(:order)})"
40
39
 
@@ -65,6 +64,64 @@ module ActiveList
65
64
  code
66
65
  end
67
66
 
67
+ def exportable_query_code(options = {})
68
+ unless @table.options.keys.include?(:order)
69
+ columns = @table.table_columns
70
+ @table.options[:order] = (columns.any? ? columns.first.name.to_sym : { id: :desc })
71
+ end
72
+
73
+ class_name = "options[\"constant_name\"]&.constantize || #{@table.model.name}"
74
+ class_name = "(controller_name != '#{class_name.tableize}' && !options[\"constant_name\"] ? controller_name.to_s.classify.constantize : #{class_name})" if collection?
75
+
76
+ code = ''
77
+
78
+ code << "query = #{class_name}.to_s\n"
79
+ code << "query << #{scope_code.inspect}\n" if scope_code
80
+
81
+ if select_code
82
+ code << "select = #{select_code}\n"
83
+ code << "query << \".select(\"\n"
84
+ code << "query << select.inspect\n"
85
+ code << "query << \")\"\n"
86
+ end
87
+
88
+ if from_code
89
+ code << "from = #{from_code}\n"
90
+ code << "query << \".from(\"\n"
91
+ code << "query << from.inspect\n"
92
+ code << "query << \")\"\n"
93
+ end
94
+
95
+ unless @table.options[:conditions].blank?
96
+ code << "condition = #{conditions_code}\n"
97
+ code << "query << \".where(\"\n"
98
+ code << "query << condition.inspect\n"
99
+ code << "query << \")\"\n"
100
+ end
101
+
102
+ code << "query << \".joins(#{@table.options[:joins].inspect})\"\n" unless @table.options[:joins].blank?
103
+
104
+ unless includes_reflections.empty?
105
+ expr = includes_reflections.inspect[1..-2]
106
+ code << "query << \".includes(#{expr})\"\n"
107
+ code << "query << \".references(#{expr})\"\n"
108
+ end
109
+
110
+ unless @table.options[:group].blank?
111
+ code << "group = #{@table.options[:group].inspect}\n"
112
+ code << "query << \".group(\"\n"
113
+ code << "query << group.inspect\n"
114
+ code << "query << \")\"\n"
115
+ end
116
+
117
+ code << "order = #{var_name(:order)}\n"
118
+ code << "query << \".reorder(\"\n"
119
+ code << "query << order.inspect\n"
120
+ code << "query << \")\"\n"
121
+
122
+ code.c
123
+ end
124
+
68
125
  protected
69
126
 
70
127
  # Compute includes Hash
@@ -133,7 +190,7 @@ module ActiveList
133
190
  code << 'DISTINCT ' if @table.options[:distinct]
134
191
  if @table.options[:select]
135
192
  # code << @table.options[:select].collect { |k, v| ", #{k[0].to_s + '.' + k[1].to_s} AS #{v}" }.join
136
- code << @table.options[:select].collect do |k, v|
193
+ code << @table.options[:select].collect do |k, v|
137
194
  c = if k.is_a? Array
138
195
  k[0].to_s + '.' + k[1].to_s
139
196
  else
@@ -145,7 +202,7 @@ module ActiveList
145
202
  else
146
203
  code << "#{@table.model.table_name}.*"
147
204
  end
148
- ("'" + code + "'").c
205
+ ('"' + code + '"').c
149
206
  end
150
207
 
151
208
  def sanitize_condition(value)
@@ -20,11 +20,12 @@ module ActiveList
20
20
  end
21
21
  end
22
22
 
23
- def urlify(value, record_name)
24
- if value.is_a?(CodeString)
25
- '(' + value.gsub(/RECORD/, record_name) + ')'
23
+ def urlify(key, value, record_name, namespace = nil)
24
+ return value.inspect unless value.is_a?(CodeString)
25
+ if key == :controller && namespace
26
+ '(' + "'/#{namespace}/' + " + value.gsub(/RECORD/, record_name) + ')'
26
27
  else
27
- value.inspect
28
+ '(' + value.gsub(/RECORD/, record_name) + ')'
28
29
  end
29
30
  end
30
31
  end
@@ -41,7 +41,7 @@ module ActiveList
41
41
  header = header_code
42
42
  extras = extras_codes
43
43
 
44
- code = generator.select_data_code
44
+ code = "#{generator.select_data_code}(options)\n"
45
45
  # Hack for Rails 5
46
46
  code << "__params = params.permit!\n"
47
47
  code << "#{var_name(:tbody)} = '<tbody data-total=\"' + #{var_name(:count)}.to_s + '\""
@@ -53,7 +53,12 @@ module ActiveList
53
53
  code << "if #{var_name(:count)} > 0\n"
54
54
  code << " #{generator.records_variable_name}.each do |#{record}|\n"
55
55
  code << " #{var_name(:attrs)} = {id: 'r' + #{record}.id.to_s}\n"
56
- code << " #{var_name(:attrs)}['data-' + options[:data].gsub('_', '-')] = #{record}.send(options[:data]) if options[:data]\n"
56
+ code << " if #{var_name(:params)}[:data]\n"
57
+ code << " #{var_name(:params)}[:data] = [#{var_name(:params)}[:data]] unless #{var_name(:params)}[:data].is_a?(Array)\n"
58
+ code << " #{var_name(:params)}[:data].each do |attr|\n"
59
+ code << " #{var_name(:attrs)}['data-' + attr.gsub('_', '-')] = #{record}.send(attr)\n"
60
+ code << " end\n"
61
+ code << " end\n"
57
62
  if table.options[:line_class]
58
63
  code << " #{var_name(:attrs)}[:class] = (#{recordify!(table.options[:line_class], record)}).to_s\n"
59
64
  code << " #{var_name(:attrs)}[:class] << ' focus' if __params['#{table.name}-id'].to_i == #{record}.id\n"
@@ -165,17 +170,20 @@ module ActiveList
165
170
  children_mode = !!(nature == :children)
166
171
  for column in table.columns
167
172
  value_code = ''
173
+ title_value_code = ''
168
174
  if column.is_a? ActiveList::Definition::EmptyColumn
169
175
  value_code = 'nil'
170
176
  elsif column.is_a? ActiveList::Definition::StatusColumn
171
-
177
+ title_value_code = nil
172
178
  value_code = column.datum_code(record, children_mode)
179
+ title_code = column.tooltip_title_code(record, children_mode)
173
180
  levels = %w[go caution stop]
174
181
  lights = levels.collect do |light|
175
182
  "content_tag(:span, '', :class => #{light.inspect})"
176
183
  end.join(' + ')
177
184
  # Expected value are :valid, :warning, :error
178
- value_code = "content_tag(:span, #{lights}, :class => 'lights lights-' + (#{levels.inspect}.include?(#{value_code}.to_s) ? #{value_code}.to_s : 'undefined'))"
185
+
186
+ value_code = "content_tag(:span, #{lights}, :class => 'lights lights-' + (#{levels.inspect}.include?(#{value_code}.to_s) ? #{value_code}.to_s : 'undefined'), data: { toggle: :tooltip, placement: :top }, title: #{title_code})"
179
187
 
180
188
  elsif column.is_a? ActiveList::Definition::DataColumn
181
189
  if column.options[:children].is_a?(FalseClass) && children_mode
@@ -183,6 +191,7 @@ module ActiveList
183
191
  else
184
192
  value_code = column.datum_code(record, children_mode)
185
193
  if column.datatype == :boolean
194
+ title_value_code = value_code
186
195
  value_code = "content_tag(:div, '', :class => 'checkbox-'+(" + value_code.to_s + " ? 'true' : 'false'))"
187
196
  elsif %i[date datetime timestamp measure].include? column.datatype
188
197
  value_code = "(#{value_code}.nil? ? '' : #{value_code}.l)"
@@ -204,29 +213,37 @@ module ActiveList
204
213
  column.options[:url] = {} unless column.options[:url].is_a?(Hash)
205
214
  column.options[:url][:id] ||= (column.record_expr(record) + '.id').c
206
215
  column.options[:url][:action] ||= :show
207
- column.options[:url][:controller] ||= column.class_name.tableize.to_sym # (self.generator.collection? ? "RECORD.class.name.tableize".c : column.class_name.tableize.to_sym)
208
- # column.options[:url][:controller] ||= "#{value_code}.class.name.tableize".c
209
- url = column.options[:url].collect { |k, v| "#{k}: " + urlify(v, record) }.join(', ')
216
+ default_controller = column.class_name.is_a?(CodeString) ? column.class_name : column.class_name.tableize.to_sym
217
+ column.options[:url][:controller] ||= default_controller
218
+ namespace = column.options[:url].delete(:namespace)
219
+ url = column.options[:url].collect { |k, v| "#{k}: " + urlify(k, v, record, namespace) }.join(', ')
220
+
221
+ title_value_code = value_code
210
222
  value_code = "(#{value_code}.blank? ? '' : link_to(#{value_code}.to_s, #{url}))"
211
223
  elsif column.options[:mode] || column.label_method == :email
224
+ title_value_code = value_code
212
225
  value_code = "(#{value_code}.blank? ? '' : mail_to(#{value_code}))"
213
226
  elsif column.options[:mode] || column.label_method == :website
227
+ title_value_code = value_code
214
228
  value_code = "(#{value_code}.blank? ? '' : link_to(" + value_code + ', ' + value_code + '))'
215
229
  elsif column.label_method == :color
230
+ title_value_code = value_code
216
231
  value_code = "content_tag(:div, #{column.datum_code(record)}, style: 'background: #'+" + column.datum_code(record) + ')'
217
232
  elsif column.label_method.to_s.match(/(^|\_)currency$/) && column.datatype == :string
218
- value_code = "(Nomen::Currency[#{value_code}] ? Nomen::Currency[#{value_code}].human_name : #{value_code})"
233
+ value_code = "(Onoma::Currency[#{value_code}] ? Onoma::Currency[#{value_code}].human_name : #{value_code})"
219
234
  elsif column.label_method.to_s.match(/(^|\_)language$/) && column.datatype == :string
220
- value_code = "(Nomen::Language[#{value_code}] ? Nomen::Language[#{value_code}].human_name : #{value_code})"
235
+ value_code = "(Onoma::Language[#{value_code}] ? Onoma::Language[#{value_code}].human_name : #{value_code})"
221
236
  elsif column.label_method.to_s.match(/(^|\_)country$/) && column.datatype == :string
222
- value_code = "(Nomen::Country[#{value_code}] ? (image_tag('countries/' + #{value_code}.to_s + '.png') + ' ' + Nomen::Country[#{value_code}].human_name).html_safe : #{value_code})"
237
+ value_code = "(Onoma::Country[#{value_code}] ? (image_tag('countries/' + #{value_code}.to_s + '.png') + ' ' + Onoma::Country[#{value_code}].human_name).html_safe : #{value_code})"
223
238
  else # if column.datatype == :string
224
239
  value_code = "h(#{value_code}.to_s)"
240
+ title_value_code = nil
225
241
  end
226
242
 
227
243
  value_code = "if #{record}\n#{value_code.dig}end" if column.is_a?(ActiveList::Definition::AssociationColumn)
228
244
  end
229
245
  elsif column.is_a?(ActiveList::Definition::CheckBoxColumn)
246
+ title_value_code = nil
230
247
  if nature == :body
231
248
  form_name = column.form_name || "'#{table.name}[' + #{record}.id.to_s + '][#{column.name}]'".c
232
249
  value = 'nil'
@@ -241,15 +258,26 @@ module ActiveList
241
258
  value_code << 'nil'
242
259
  end
243
260
  elsif column.is_a?(ActiveList::Definition::TextFieldColumn)
261
+ title_value_code = nil
244
262
  form_name = column.form_name || "'#{table.name}[' + #{record}.id.to_s + '][#{column.name}]'".c
245
263
  value_code = (nature == :body ? "text_field_tag(#{form_name.inspect}, #{recordify!(column.options[:value] || column.name, record)}#{column.options[:size] ? ', size: ' + column.options[:size].to_s : ''})" : 'nil') # , id: '#{table.name}_'+#{record}.id.to_s + '_#{column.name}'
246
264
  elsif column.is_a?(ActiveList::Definition::ActionColumn)
265
+ title_value_code = nil
247
266
  next unless column.use_single?
248
267
  value_code = (nature == :body ? column.operation(record) : 'nil')
249
268
  else
250
269
  value_code = "'&#160;&#8709;&#160;'.html_safe"
251
270
  end
252
- code << "content_tag(:td, :class => \"#{column_classes(column)}\","
271
+
272
+ title_attr_code = if title_value_code.nil?
273
+ ''
274
+ elsif title_value_code.blank?
275
+ ":title => (#{value_code}),"
276
+ else
277
+ ":title => (#{title_value_code}),"
278
+ end
279
+
280
+ code << "content_tag(:td, #{title_attr_code} :class => \"#{column_classes(column)}\","
253
281
  code << " data: { 'list-column-header' => '#{column.short_id}'"
254
282
  code << ", 'list-cell-value' => \"\#{#{column.datum_value(record, children_mode)}.to_f}\"" if column.computable?
255
283
  code << " } ) do\n"
@@ -314,7 +342,7 @@ module ActiveList
314
342
  end
315
343
  table.columns.each do |column|
316
344
  next if column.is_a?(ActiveList::Definition::ActionColumn) && !column.use_single?
317
- code << "<th data-list-column=\"#{column.sort_id}\""
345
+ code << "<th data-list-column=\"#{column.options[:icon_name] || column.sort_id}\""
318
346
  code << " data-list-column-cells=\"#{column.short_id}\""
319
347
  code << " data-list-column-sort=\"'+(#{var_name(:params)}[:sort] != '#{column.sort_id}' ? 'asc' : #{var_name(:params)}[:dir] == 'asc' ? 'desc' : 'asc')+'\"" if column.sortable?
320
348
  code << " data-list-column-computation=\"#{column.computation_method}\"" if column.computable?
@@ -322,8 +350,8 @@ module ActiveList
322
350
  unit = "''"
323
351
  precision = "''"
324
352
  if column.options[:currency]
325
- unit = "Nomen::Currency.find(#{column.currency_for(generator.records_variable_name + '.first').inspect} || 'EUR').symbol.to_s"
326
- precision = "Nomen::Currency.find(#{column.currency_for(generator.records_variable_name + '.first').inspect} || 'EUR').precision.to_s"
353
+ unit = "Onoma::Currency.find(#{column.currency_for(generator.records_variable_name + '.first').inspect} || 'EUR').symbol.to_s"
354
+ precision = "Onoma::Currency.find(#{column.currency_for(generator.records_variable_name + '.first').inspect} || 'EUR').precision.to_s"
327
355
  elsif column.computable?
328
356
  unit = "#{generator.records_variable_name}.first.#{column.value_method}.symbol"
329
357
  precision = "'2'"
@@ -424,7 +452,11 @@ module ActiveList
424
452
  classes = []
425
453
  conds = []
426
454
  conds << [:sor, "#{var_name(:params)}[:sort] == '#{column.sort_id}'".c] if column.sortable?
427
- conds << [:hidden, "#{var_name(:params)}[:hidden_columns].include?(:#{column.name})".c] if column.is_a? ActiveList::Definition::DataColumn
455
+ if column.is_a? ActiveList::Definition::DataColumn
456
+ conds << [:hidden, "#{var_name(:params)}[:hidden_columns].include?(:#{column.name})".c]
457
+ elsif column.condition
458
+ conds << [:hidden, "h(#{column.condition}) == 'false'".c]
459
+ end
428
460
  classes << column.options[:class].to_s.strip unless column.options[:class].blank?
429
461
  classes << column.short_id unless without_id
430
462
  if column.is_a? ActiveList::Definition::ActionColumn
@@ -1,3 +1,3 @@
1
1
  module ActiveList
2
- VERSION = '7.0.0'.freeze
2
+ VERSION = '8.1.0'.freeze
3
3
  end
@@ -1,4 +1,5 @@
1
1
  source 'https://rubygems.org'
2
+ git_source(:gitlab) { |repo_name| "https://gitlab.com/#{repo_name}.git" }
2
3
 
3
4
  gem 'rails', '~> 5.0'
4
5
 
@@ -8,6 +9,10 @@ gem 'rails', '~> 5.0'
8
9
  gem 'sqlite3', '~> 1.3.6'
9
10
  gem 'active_list', path: '../..'
10
11
 
12
+ gem 'sprockets', '< 4'
13
+
14
+ gem 'onoma', gitlab: 'ekylibre/onoma'
15
+
11
16
  # Gems used only for assets and not required
12
17
  # in production environments by default.
13
18
  group :assets do
@@ -39,6 +39,7 @@ module Dummy
39
39
  # Configure sensitive parameters which will be filtered from the log file.
40
40
  config.filter_parameters += [:password]
41
41
 
42
+ config.active_record.sqlite3.represent_boolean_as_integer = true
42
43
  # Use SQL instead of Active Record's schema dumper when creating the database.
43
44
  # This is necessary if your schema can't be completely dumped by the schema dumper,
44
45
  # like if you have constraints or database-specific column types
@@ -5,4 +5,3 @@
5
5
  # Make sure the secret is at least 30 characters and all random,
6
6
  # no regular words or you'll be exposed to dictionary attacks.
7
7
  Dummy::Application.config.secret_key_base = '7a06e73398090c095bed02f2dff3808b043e50cb928f7b7bfd02faadd988a0d9cf31dfc74d1bf661ac94c8d0d73c8ceb4d98210d09f251f0c6d70f09923db784'
8
- Dummy::Application.config.secret_token = '7a06e73398090c095bed02f2dff3808b043e50cb928f7b7bfd02faadd988a0d9cf31dfc74d1bf661ac94c8d0d73c8ceb4d98210d09f251f0c6d70f09923db784'
@@ -41,7 +41,7 @@ end
41
41
 
42
42
  module ActionView
43
43
  class Base
44
- module Nomen
44
+ module Onoma
45
45
  class Currencies
46
46
  def self.[](_)
47
47
  klass = Struct.const_defined?(:Currency) ? Struct::Currency : Struct.new('Currency', :precision, :symbol)
metadata CHANGED
@@ -1,91 +1,119 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_list
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.0
4
+ version: 8.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brice Texier
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-01 00:00:00.000000000 Z
11
+ date: 2020-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: arel
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.2'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '6'
19
+ version: 5.0.0
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
- version: '3.2'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '6'
26
+ version: 5.0.0
33
27
  - !ruby/object:Gem::Dependency
34
- name: arel
28
+ name: code_string
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
31
  - - ">="
38
32
  - !ruby/object:Gem::Version
39
- version: 5.0.0
33
+ version: 0.0.1
40
34
  type: :runtime
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
38
  - - ">="
45
39
  - !ruby/object:Gem::Version
46
- version: 5.0.0
40
+ version: 0.0.1
47
41
  - !ruby/object:Gem::Dependency
48
- name: code_string
42
+ name: i18n-complements
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
45
  - - ">="
52
46
  - !ruby/object:Gem::Version
53
- version: 0.0.1
47
+ version: '0'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
51
  requirements:
58
52
  - - ">="
59
53
  - !ruby/object:Gem::Version
60
- version: 0.0.1
54
+ version: '0'
61
55
  - !ruby/object:Gem::Dependency
62
- name: rubyzip
56
+ name: onoma
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rails
63
71
  requirement: !ruby/object:Gem::Requirement
64
72
  requirements:
65
73
  - - ">="
66
74
  - !ruby/object:Gem::Version
67
- version: '1.0'
75
+ version: '3.2'
76
+ - - "<"
77
+ - !ruby/object:Gem::Version
78
+ version: '6'
68
79
  type: :runtime
69
80
  prerelease: false
70
81
  version_requirements: !ruby/object:Gem::Requirement
71
82
  requirements:
72
83
  - - ">="
73
84
  - !ruby/object:Gem::Version
74
- version: '1.0'
85
+ version: '3.2'
86
+ - - "<"
87
+ - !ruby/object:Gem::Version
88
+ version: '6'
75
89
  - !ruby/object:Gem::Dependency
76
- name: i18n-complements
90
+ name: rodf
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.1'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.1'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubyzip
77
105
  requirement: !ruby/object:Gem::Requirement
78
106
  requirements:
79
107
  - - ">="
80
108
  - !ruby/object:Gem::Version
81
- version: '0'
109
+ version: '1.0'
82
110
  type: :runtime
83
111
  prerelease: false
84
112
  version_requirements: !ruby/object:Gem::Requirement
85
113
  requirements:
86
114
  - - ">="
87
115
  - !ruby/object:Gem::Version
88
- version: '0'
116
+ version: '1.0'
89
117
  - !ruby/object:Gem::Dependency
90
118
  name: sqlite3
91
119
  requirement: !ruby/object:Gem::Requirement
@@ -208,11 +236,11 @@ files:
208
236
  - test/people_controller_test.rb
209
237
  - test/table_test.rb
210
238
  - test/test_helper.rb
211
- homepage: http://github.com/ekylibre/active_list
239
+ homepage: http://gitlab.com/ekylibre/active_list
212
240
  licenses:
213
241
  - MIT
214
242
  metadata: {}
215
- post_install_message:
243
+ post_install_message:
216
244
  rdoc_options: []
217
245
  require_paths:
218
246
  - lib
@@ -227,9 +255,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
255
  - !ruby/object:Gem::Version
228
256
  version: '0'
229
257
  requirements: []
230
- rubyforge_project:
231
- rubygems_version: 2.6.14
232
- signing_key:
258
+ rubygems_version: 3.0.3
259
+ signing_key:
233
260
  specification_version: 4
234
261
  summary: Simple interactive tables for Rails app
235
262
  test_files: