Repository: leikind/wice_grid Branch: rails7 Commit: 512a2c200d5d Files: 396 Total size: 1.4 MB Directory structure: gitextract_k8x_hooj/ ├── .circleci/ │ ├── config.yml │ └── run-build-locally.sh ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ └── dependabot.yml ├── .gitignore ├── .inch.yml ├── .rspec ├── .rubocop.yml ├── Appraisals ├── CHANGELOG.md ├── Gemfile ├── Guardfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── SAVED_QUERIES_HOWTO.md ├── app/ │ └── views/ │ └── kaminari/ │ └── wice_grid/ │ ├── _gap.html.erb │ ├── _next_page.html.erb │ ├── _page.html.erb │ ├── _paginator.html.erb │ └── _prev_page.html.erb ├── config/ │ └── locales/ │ ├── cz.yml │ ├── de.yml │ ├── en.yml │ ├── es.yml │ ├── fr.yml │ ├── is.yml │ ├── it.yml │ ├── ja.yml │ ├── nl.yml │ ├── pt-BR.yml │ ├── pt.yml │ ├── ru.yml │ ├── sk.yml │ ├── uk.yml │ └── zh.yml ├── gemfiles/ │ ├── rails_5.0.gemfile │ ├── rails_5.1.gemfile │ └── rails_5.2.gemfile ├── lib/ │ ├── generators/ │ │ └── wice_grid/ │ │ ├── add_migration_for_serialized_queries_generator.rb │ │ ├── install_generator.rb │ │ └── templates/ │ │ ├── create_wice_grid_serialized_queries.rb │ │ └── wice_grid_config.rb │ ├── wice/ │ │ ├── active_record_column_wrapper.rb │ │ ├── columns/ │ │ │ ├── column_action.rb │ │ │ ├── column_boolean.rb │ │ │ ├── column_bootstrap_datepicker.rb │ │ │ ├── column_custom_dropdown.rb │ │ │ ├── column_float.rb │ │ │ ├── column_html5_datepicker.rb │ │ │ ├── column_integer.rb │ │ │ ├── column_jquery_datepicker.rb │ │ │ ├── column_processor_index.rb │ │ │ ├── column_rails_date_helper.rb │ │ │ ├── column_rails_datetime_helper.rb │ │ │ ├── column_range.rb │ │ │ ├── column_string.rb │ │ │ ├── common_date_datetime_mixin.rb │ │ │ ├── common_js_date_datetime_conditions_generator_mixin.rb │ │ │ ├── common_js_date_datetime_mixin.rb │ │ │ ├── common_rails_date_datetime_conditions_generator_mixin.rb │ │ │ └── common_standard_helper_date_datetime_mixin.rb │ │ ├── columns.rb │ │ ├── grid_output_buffer.rb │ │ ├── grid_renderer.rb │ │ ├── helpers/ │ │ │ ├── bs_calendar_helpers.rb │ │ │ ├── js_calendar_helpers.rb │ │ │ ├── wice_grid_misc_view_helpers.rb │ │ │ ├── wice_grid_serialized_queries_view_helpers.rb │ │ │ └── wice_grid_view_helpers.rb │ │ ├── kaminari_monkey_patching.rb │ │ ├── table_column_matrix.rb │ │ ├── wice_grid_controller.rb │ │ ├── wice_grid_core_ext.rb │ │ ├── wice_grid_misc.rb │ │ ├── wice_grid_serialized_queries_controller.rb │ │ ├── wice_grid_serialized_query.rb │ │ └── wice_grid_spreadsheet.rb │ └── wice_grid.rb ├── package.json ├── spec/ │ ├── acceptance_helper.rb │ ├── features/ │ │ ├── action_column_request_spec.rb │ │ ├── adding_rows_request_spec.rb │ │ ├── all_records_request_spec.rb │ │ ├── auto_reloads2_request_spec.rb │ │ ├── auto_reloads3_request_spec.rb │ │ ├── auto_reloads_request_spec.rb │ │ ├── basics1_request_spec.rb │ │ ├── basics2_request_spec.rb │ │ ├── basics3_request_spec.rb │ │ ├── basics4_request_spec.rb │ │ ├── basics5_request_spec.rb │ │ ├── basics6_request_spec.rb │ │ ├── blockless_column_definition_spec.rb │ │ ├── buttons_request_spec.rb │ │ ├── csv_and_detached_filters_spec.rb │ │ ├── csv_export_request_spec.rb │ │ ├── custom_filter_params_request_spec.rb │ │ ├── custom_filters1_request_spec.rb │ │ ├── custom_filters2_request_spec.rb │ │ ├── custom_filters3_request_spec.rb │ │ ├── custom_filters4_request_spec.rb │ │ ├── custom_ordering_on_calculated_request_spec.rb │ │ ├── custom_ordering_request_spec.rb │ │ ├── custom_ordering_with_arel_request_spec.rb │ │ ├── custom_ordering_with_proc_request_spec.rb │ │ ├── custom_ordering_with_ruby_request_spec.rb │ │ ├── dates_request_spec.rb │ │ ├── detached_filters_spec.rb │ │ ├── detached_filters_two_grids_spec.rb │ │ ├── disable_all_filters_spec.rb │ │ ├── hiding_checkboxes_in_action_column_request_spec.rb │ │ ├── integration_with_application_view_request_spec.rb │ │ ├── integration_with_forms_request_spec.rb │ │ ├── joining_tables_spec.rb │ │ ├── localization_request_spec.rb │ │ ├── many_grids_on_page_request_spec.rb │ │ ├── negation_request_spec.rb │ │ ├── no_records_request_spec.rb │ │ ├── numeric_filters_request_spec.rb │ │ ├── resultset_processings2_request_spec.rb │ │ ├── resultset_processings_request_spec.rb │ │ ├── saved_queries_request_spec.rb │ │ ├── shared.rb │ │ ├── shared_detached_filters.rb │ │ ├── styling_spec.rb │ │ ├── two_associations_spec.rb │ │ ├── upper_pagination_panel_request_spec.rb │ │ └── when_filtered_spec.rb │ ├── fixtures/ │ │ ├── .gitkeep │ │ ├── companies.yml │ │ ├── priorities.yml │ │ ├── project_roles.yml │ │ ├── projects.yml │ │ ├── statuses.yml │ │ ├── tasks.yml │ │ ├── tasks_users.yml │ │ ├── users.yml │ │ └── versions.yml │ ├── models/ │ │ ├── company_spec.rb │ │ ├── priority_spec.rb │ │ ├── project_spec.rb │ │ ├── status_spec.rb │ │ ├── task_spec.rb │ │ ├── user_project_participation_spec.rb │ │ ├── user_spec.rb │ │ └── version_spec.rb │ ├── rails_helper.rb │ ├── schema.rb │ ├── spec_helper.rb │ ├── support/ │ │ ├── active_record.rb │ │ ├── download_helper.rb │ │ └── test_app/ │ │ ├── Rakefile │ │ ├── app/ │ │ │ ├── assets/ │ │ │ │ ├── builds/ │ │ │ │ │ ├── .keep │ │ │ │ │ └── application.css │ │ │ │ ├── config/ │ │ │ │ │ └── manifest.js │ │ │ │ └── stylesheets/ │ │ │ │ ├── adding_rows.scss │ │ │ │ ├── application.scss │ │ │ │ ├── csv_and_detached_filters.scss │ │ │ │ └── many_grids_on_page.scss │ │ │ ├── controllers/ │ │ │ │ ├── action_column_controller.rb │ │ │ │ ├── adding_rows_controller.rb │ │ │ │ ├── all_records_controller.rb │ │ │ │ ├── application_controller.rb │ │ │ │ ├── auto_reloads2_controller.rb │ │ │ │ ├── auto_reloads3_controller.rb │ │ │ │ ├── auto_reloads_controller.rb │ │ │ │ ├── basics1_controller.rb │ │ │ │ ├── basics2_controller.rb │ │ │ │ ├── basics3_controller.rb │ │ │ │ ├── basics4_controller.rb │ │ │ │ ├── basics5_controller.rb │ │ │ │ ├── basics6_controller.rb │ │ │ │ ├── blockless_column_definition_controller.rb │ │ │ │ ├── buttons_controller.rb │ │ │ │ ├── csv_and_detached_filters_controller.rb │ │ │ │ ├── csv_export_controller.rb │ │ │ │ ├── custom_filter_params_controller.rb │ │ │ │ ├── custom_filters1_controller.rb │ │ │ │ ├── custom_filters2_controller.rb │ │ │ │ ├── custom_filters3_controller.rb │ │ │ │ ├── custom_filters4_controller.rb │ │ │ │ ├── custom_ordering_controller.rb │ │ │ │ ├── custom_ordering_on_calculated_controller.rb │ │ │ │ ├── custom_ordering_with_arel_controller.rb │ │ │ │ ├── custom_ordering_with_proc_controller.rb │ │ │ │ ├── custom_ordering_with_ruby_controller.rb │ │ │ │ ├── dates_controller.rb │ │ │ │ ├── detached_filters_controller.rb │ │ │ │ ├── detached_filters_two_grids_controller.rb │ │ │ │ ├── disable_all_filters_controller.rb │ │ │ │ ├── hiding_checkboxes_in_action_column_controller.rb │ │ │ │ ├── home_controller.rb │ │ │ │ ├── integration_with_application_view_controller.rb │ │ │ │ ├── integration_with_forms_controller.rb │ │ │ │ ├── joining_tables_controller.rb │ │ │ │ ├── localization_controller.rb │ │ │ │ ├── many_grids_on_page_controller.rb │ │ │ │ ├── negation_controller.rb │ │ │ │ ├── no_records_controller.rb │ │ │ │ ├── null_values_controller.rb │ │ │ │ ├── numeric_filters_controller.rb │ │ │ │ ├── queries_controller.rb │ │ │ │ ├── resultset_processings2_controller.rb │ │ │ │ ├── resultset_processings_controller.rb │ │ │ │ ├── saved_queries_controller.rb │ │ │ │ ├── styling_controller.rb │ │ │ │ ├── tasks_controller.rb │ │ │ │ ├── two_associations_controller.rb │ │ │ │ ├── upper_pagination_panel_controller.rb │ │ │ │ └── when_filtered_controller.rb │ │ │ ├── helpers/ │ │ │ │ └── application_helper.rb │ │ │ ├── javascript/ │ │ │ │ └── application.js │ │ │ ├── mailers/ │ │ │ │ └── .gitkeep │ │ │ ├── models/ │ │ │ │ ├── .gitkeep │ │ │ │ ├── company.rb │ │ │ │ ├── populate.rb │ │ │ │ ├── priority.rb │ │ │ │ ├── project.rb │ │ │ │ ├── project_role.rb │ │ │ │ ├── status.rb │ │ │ │ ├── task.rb │ │ │ │ ├── to_dropdown_mixin.rb │ │ │ │ ├── user.rb │ │ │ │ ├── user_project_participation.rb │ │ │ │ └── version.rb │ │ │ └── views/ │ │ │ ├── action_column/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── adding_rows/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── all_records/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── auto_reloads/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── auto_reloads2/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── auto_reloads3/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── basics1/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── basics2/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── basics3/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── basics4/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── basics5/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── basics6/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── blockless_column_definition/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── buttons/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── csv_and_detached_filters/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── csv_export/ │ │ │ │ ├── _projects_grid.html.erb │ │ │ │ ├── _tasks_grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_filter_params/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_filters1/ │ │ │ │ ├── _g1.html.erb │ │ │ │ ├── _g2.html.erb │ │ │ │ ├── _g3.html.erb │ │ │ │ ├── _g4.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_filters2/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_filters3/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_filters4/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_ordering/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_ordering_on_calculated/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_ordering_with_arel/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_ordering_with_proc/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── custom_ordering_with_ruby/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── dates/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── detached_filters/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── detached_filters_two_grids/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── disable_all_filters/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── hiding_checkboxes_in_action_column/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── integration_with_application_view/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── integration_with_forms/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── joining_tables/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── layouts/ │ │ │ │ └── application.html.haml │ │ │ ├── localization/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── many_grids_on_page/ │ │ │ │ ├── _tasks_grid1.html.erb │ │ │ │ ├── _tasks_grid2.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── negation/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── no_records/ │ │ │ │ ├── _empty_grid.html.haml │ │ │ │ ├── _grid1.html.erb │ │ │ │ ├── _grid2.html.erb │ │ │ │ ├── _grid3.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── null_values/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── numeric_filters/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── resultset_processings/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── resultset_processings2/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── saved_queries/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── styling/ │ │ │ │ ├── _grid1.html.erb │ │ │ │ ├── _grid2.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── tasks/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── two_associations/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ ├── upper_pagination_panel/ │ │ │ │ ├── _grid.html.erb │ │ │ │ └── index.html.haml │ │ │ └── when_filtered/ │ │ │ ├── _grid.html.erb │ │ │ └── index.html.haml │ │ ├── bin/ │ │ │ ├── dartsass │ │ │ ├── importmap │ │ │ ├── rails │ │ │ └── rake │ │ ├── config/ │ │ │ ├── application.rb │ │ │ ├── boot.rb │ │ │ ├── database.travis.yml │ │ │ ├── database.yml │ │ │ ├── database.yml.mysql │ │ │ ├── database.yml.postgresql │ │ │ ├── environment.rb │ │ │ ├── environments/ │ │ │ │ ├── development.rb │ │ │ │ ├── production.rb │ │ │ │ └── test.rb │ │ │ ├── importmap.rb │ │ │ ├── initializers/ │ │ │ │ ├── assets.rb │ │ │ │ ├── backtrace_silencers.rb │ │ │ │ ├── inflections.rb │ │ │ │ ├── mime_types.rb │ │ │ │ ├── secret_token.rb │ │ │ │ ├── session_store.rb │ │ │ │ ├── wice_grid_config.rb │ │ │ │ └── wrap_parameters.rb │ │ │ ├── locales/ │ │ │ │ ├── en.yml │ │ │ │ └── wice_grid.yml │ │ │ ├── puma.rb │ │ │ └── routes.rb │ │ ├── config.ru │ │ ├── db/ │ │ │ ├── migrate/ │ │ │ │ ├── 20120224193505_create_tasks.rb │ │ │ │ ├── 20120224193517_create_users.rb │ │ │ │ ├── 20120224193522_create_projects.rb │ │ │ │ ├── 20120224193529_create_priorities.rb │ │ │ │ ├── 20120224193537_create_statuses.rb │ │ │ │ ├── 20120224193543_create_versions.rb │ │ │ │ ├── 20120224193550_create_project_roles.rb │ │ │ │ ├── 20120224193610_create_companies.rb │ │ │ │ ├── 20120224195351_create_user_project_participations.rb │ │ │ │ ├── 20120224195521_add_tasks_users.rb │ │ │ │ └── 20120610091944_create_wice_grid_serialized_queries.rb │ │ │ ├── schema.rb │ │ │ └── seeds.rb │ │ ├── lib/ │ │ │ ├── ar_fixtures.rb │ │ │ ├── assets/ │ │ │ │ └── .gitkeep │ │ │ └── tasks/ │ │ │ ├── .gitkeep │ │ │ └── ar_fixtures.rake │ │ ├── public/ │ │ │ ├── 404.html │ │ │ ├── 422.html │ │ │ ├── 500.html │ │ │ └── robots.txt │ │ └── vendor/ │ │ └── javascript/ │ │ ├── .keep │ │ ├── @hotwired--stimulus.js │ │ ├── @stimulus-components--rails-nested-form.js │ │ ├── jquery.ui.datepicker.locales.js │ │ └── reload_on_change.js │ └── wice/ │ ├── grid_output_buffer_spec.rb │ ├── table_column_matrix_spec.rb │ ├── wice_grid_misc_spec.rb │ └── wice_grid_spreadsheet_spec.rb ├── vendor/ │ └── assets/ │ ├── javascripts/ │ │ ├── wice_grid.js │ │ ├── wice_grid_init.js.coffee │ │ ├── wice_grid_processor.js.coffee │ │ └── wice_grid_saved_queries_init.js.coffee │ └── stylesheets/ │ └── wice_grid.scss └── wice_grid.gemspec ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ build_job: &build_job working_directory: ~/wice_grid steps: - checkout # Install PhantomJS (if not found in cache) - run: name: Install phantomjs command: | if ! [ $(which phantomjs) ]; then sudo curl --output /usr/local/bin/phantomjs https://s3.amazonaws.com/circle-downloads/phantomjs-2.1.1 fi sudo chmod ugo+x /usr/local/bin/phantomjs # Restore Cached Dependencies - type: cache-restore name: Restore bundle cache key: wice_grid-{{ checksum "Gemfile.lock" }} # Bundle install dependencies - run: bundle install --path vendor/bundle # Install Appraisal gemfiles - run: bundle exec appraisal install # Cache Dependencies - type: cache-save name: Store bundle cache key: wice_grid-{{ checksum "Gemfile.lock" }} paths: - vendor/bundle # Run the tests - run: bundle exec appraisal rspec - store_artifacts: path: spec/support/test_app/tmp/capybara destination: screenshots version: 2 jobs: build-ruby24: <<: *build_job docker: - image: circleci/ruby:2.4 environment: RAILS_ENV: test build-ruby25: <<: *build_job docker: - image: circleci/ruby:2.5 environment: RAILS_ENV: test build-ruby26: <<: *build_job docker: - image: circleci/ruby:2.6 environment: RAILS_ENV: test workflows: version: 2 build: jobs: - build-ruby24 - build-ruby25 - build-ruby26 ================================================ FILE: .circleci/run-build-locally.sh ================================================ #!/usr/bin/env bash curl --user ${CIRCLE_TOKEN}: \ --request POST \ --form revision=fa279c82728d4e0d764f09f3db0f63269575fba4 \ --form config=@config.yml \ --form notify=false \ https://circleci.com/api/v1.1/project/github/patricklindsay/wice_grid/tree/master ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve --- ## Precheck - For bugs, do a quick search and make sure the bug has not yet been reported - Try to be clear and concise - the more information you provide the easier it is for us to resolve - Things to include; - Code samples - Stack traces - Screenshots - Sample application or a test case that reproduces the error. ## Environment - Ruby **[version]** - Rails **[version]** - WiceGrid **[version]** ## Description of issue ## Current behaviour ## Expected behaviour ## Additional context ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: bundler directory: "/" schedule: interval: monthly open-pull-requests-limit: 10 ignore: - dependency-name: activerecord - dependency-name: activesupport - dependency-name: actionpack - dependency-name: appraisal - dependency-name: byebug - dependency-name: capybara - dependency-name: capybara-screenshot - dependency-name: coderay - dependency-name: coffee-rails - dependency-name: faker - dependency-name: font-awesome-sass - dependency-name: haml - dependency-name: inch - dependency-name: jquery-rails - dependency-name: nokogiri - dependency-name: rack - dependency-name: rails - dependency-name: rake - dependency-name: rdoc - dependency-name: rspec - dependency-name: rspec-rails - dependency-name: sass-rails - dependency-name: selenium-webdriver - dependency-name: shoulda-matchers - dependency-name: simplecov - dependency-name: sqlite3 - dependency-name: turbolinks - dependency-name: yard ================================================ FILE: .gitignore ================================================ *.gem *.rbc .bundle .config .idea .yardoc .rbenv-version InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp .byebug_history spec/support/test_app/log spec/support/test_app/tmp spec/support/test_app/db/*.sqlite3* ================================================ FILE: .inch.yml ================================================ files: excluded: - lib/generators/wice_grid/add_migration_for_serialized_queries_generator.rb ================================================ FILE: .rspec ================================================ --color --require rails_helper --format d ================================================ FILE: .rubocop.yml ================================================ AllCops: DisabledByDefault: true Style/MultilineTernaryOperator: Enabled: true # Cop supports --auto-correct. Style/DefWithParentheses: Enabled: true # Cop supports --auto-correct. Style/MethodCallParentheses: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleInsidePipes, SupportedStyles. Style/SpaceAroundBlockParameters: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleForMultiline, SupportedStyles. Style/TrailingCommaInArguments: EnforcedStyleForMultiline: no_comma # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleForMultiline, SupportedStyles. Style/TrailingCommaInLiteral: EnforcedStyleForMultiline: no_comma # Cop supports --auto-correct. Style/SpaceBeforeFirstArg: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/FirstParameterIndentation: Enabled: true # Cop supports --auto-correct. # Configuration parameters: AllowSafeAssignment. Style/ParenthesesAroundCondition: Enabled: true # Cop supports --auto-correct. Style/SpecialGlobalVars: Enabled: true # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. Style/SymbolProc: Enabled: true # Cop supports --auto-correct. Style/Lambda: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/MethodDefParentheses: Enabled: true # Cop supports --auto-correct. Style/EmptyLiteral: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues. Style/HashSyntax: Enabled: true # Cop supports --auto-correct. Style/Not: Enabled: true # Cop supports --auto-correct. # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Enabled: true # Cop supports --auto-correct. Style/SpaceAfterComma: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/SignalException: Enabled: true # Cop supports --auto-correct. Style/LineEndConcatenation: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/AndOr: Enabled: true # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AutoCorrectEncodingComment. Style/Encoding: Enabled: true # Cop supports --auto-correct. Lint/UnusedMethodArgument: Enabled: true # Cop supports --auto-correct. Style/AlignArray: Enabled: true # Cop supports --auto-correct. Style/ColonMethodCall: Enabled: true # Cop supports --auto-correct. Lint/UnusedBlockArgument: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/DotPosition: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/EmptyElse: Enabled: true # Cop supports --auto-correct. # Configuration parameters: MultiSpaceAllowedForOperators. Style/SpaceAroundOperators: Enabled: true # Cop supports --auto-correct. Style/SpaceBeforeComma: Enabled: true # Cop supports --auto-correct. Style/SpaceInsideParens: Enabled: true # Cop supports --auto-correct. Style/DeprecatedHashMethods: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods. Style/BlockDelimiters: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/BracesAroundHashParameters: Enabled: true Lint/AmbiguousRegexpLiteral: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/StringLiterals: Enabled: true Lint/AmbiguousOperator: Enabled: true Lint/UselessAssignment: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/ClassCheck: Enabled: true # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/AlignParameters: Enabled: true Lint/ConditionPosition: Enabled: true ================================================ FILE: Appraisals ================================================ appraise "rails-5.0" do gem "rails", "~> 5.0.0" end appraise "rails-5.1" do gem "rails", "~> 5.1.0" end appraise "rails-5.2" do gem "rails", "~> 5.2.0" end ================================================ FILE: CHANGELOG.md ================================================ ## 7.1.4 * Allow Rails 7.0 * Wice::Columns#add_css_class does not update the value given to the :class option of Wice::GridRenderer#column method. ## 7.1.3 * Fixes [the bug](https://github.com/leikind/wice_grid/issues/395). ## 7.1.2 * This is the fix for the workaround of the [Facebook](https://www.facebook.com) problem. ## 7.1.1 * I have discovered that [Facebook](https://www.facebook.com) spoils the links that have WiceGrid filters when you share them. That lead to the exception in the application. This is a workaround for the case. ## 7.1.0 * Rails 7.1 uses the gem `dartsass-rails` instead of the deprecatad gem `sass-rails`. And the gem `importmap-rails` is the standard gem for Javascript. The support of them is added and the required gems for development are changed. * Turbolinks was replaced with Turbo. * Added the dependencies of `jquery-rails`. * I could not fix the problem with the Specs to run without a browser that is required with the environment variable `BROWSER` set to any value. Moreover not always all the test are green. The suggesons are welcome. ## 6.1.2 * Added Rails 6.1 support * Fix bug in "Dangerous query methods" deprecation warning fix in custom order when using Arel.sql ## 4.1.0 (28 November, 2018) * Add option `filter_control_options` to columns. Initial use is to pass in the options `start_year`, `end_year`, and `max_year_allowed` when using `rails_datetime_helper` - [#12](https://github.com/leikind/wice_grid/pull/18) * Fix deprecation (in Rails 5.0.x) and incorrect behaviour (in Rails 5.1.x) of CSV exports - [#4](https://github.com/leikind/wice_grid/pull/4) * Fix deprecation (in Rails 5.2) regarding "Dangerous query methods" in `order` clauses. Note that when using the `custom_order` option you must pass a value acceptable for ActiveRecord's `order` method, otherwise you will receive the same warning. - [#41](https://github.com/leikind/wice_grid/pull/41) * Add support for using `Arel::Attributes::Attribute`s with the `custom_order` option. - [#41](https://github.com/leikind/wice_grid/pull/41) * Added `sort_by` option to column to allow arbitrary, Ruby-based ordering. - [#3](https://github.com/leikind/wice_grid/pull/3) * Added Rails 5.2 support * Bump Kaminari to ~> 1.1.0 ## 4.0.1 (31 May, 2018) * Fixed tagbuilder issue with Rails 5.0.x - [#1](https://github.com/leikind/wice_grid/pull/1) * Moved testbed into repo - [#12](https://github.com/leikind/wice_grid/pull/12) * Remove old release notes and empty /test directory * Removed `Gemfile.lock` from `.gitignore` * Removed `codeclimate-test-reporter` dev dependancy ## 4.0.0 (18 Jan, 2018) ### Rails 5.0 & 5.1 Support Added support for Rails 5.0 & 5.1. Anything below Rails 5 is no longer supported. ### Focused Filter Has Cursor/Caret at the End of Input The cursor/caret used to appear at the beginning of the focused autocomplete in FF and IE. This caused annoyances for users of those browsers who typed slowly since the page would reload and when they started typing again the text would appear before their last entries. For example if someone was searching for 'john' but paused after 'joh', they would end up typing 'njoh', instead of 'john'. If they weren't paying attention, they might think there is no 'john', when they'd mistakenly searched for 'njoh'. ### Turbolinks 5 Support Handled by initializing WiceGrid on `turbolinks:render` as well as `page:load ready` ## 3.6.0 ## New API For Joined Tables Before 3.6.0 `:model_class` was used in column definitions for columns from joined tables. In 3.6.0 the API has changed to use associations. If, say, a `Task` `belongs_to` a `Priority`, a column definition should specify this association using `:assoc`: ```ruby g.column name: 'Priority', attribute: 'name', assoc: :priority do |task| task.priority.name if task.priority end ``` If, say, a `Task` `belongs_to` a `Project`, and a `Project` belongs to a `Customer`, `assoc:` should be a list of these associations: ```ruby g.column name: 'Customer', attribute: 'name', assoc: [:project, :customer] do |task| task.project.customer.name if task.project && task.project.customer end ``` ## Blockless Columns For Joined Tables Blockless columns used to only work for the main model. Now they can be used for joined tables, too. Instead of ```ruby g.column name: 'Priority', attribute: 'name', assoc: :priority do |task| task.priority.name if task.priority end ``` you can write ```ruby g.column name: 'Priority', attribute: 'name', assoc: :priority ``` Instead of ```ruby g.column name: 'Customer', attribute: 'name', assoc: [:project, :customer] do |task| task.project.customer.name if task.project && task.project.customer end ``` you can write ```ruby g.column name: 'Customer', attribute: 'name', assoc: [:project, :customer] ``` ## New Way To Choose Datepickers Before 3.6.0 to choose a datepicker type we used `:helper_style` in column definitions and `Wice::Defaults:HELPER_STYLE` in the configuration_file. In 3.6.0 `:helper_style` and `Wice::Defaults:HELPER_STYLE` are gone. Now each datepicker is simply a separate filter type, and to pick a datepicker we can use `:filter_type`, just like other filter types are chosen. Filter types for dates and datetimes are * `:rails_datetime_helper` * `:rails_date_helper` * `:jquery_datepicker` * `:bootstrap_datepicker` Example: ```ruby g.column name: 'Updated', attribute: 'updated_at', filter_type: :rails_datetime_helper do |task| task.updated_at.to_fs(:db) end ``` Default filter types for date and datetime columns are set by `Wice::Defaults:DEFAULT_FILTER_FOR_DATE` and `Wice::Defaults:DEFAULT_FILTER_FOR_DATETIME`. ## Icons There are no more icons inside of the gem. Instead, [Font Awesome](https://github.com/FortAwesome/font-awesome-sass) is used. ## CSS CSS is no longer copied to the applications asset directory. Instead the user is supposed to add ```sass @import "wice_grid"; @import "font-awesome-sprockets"; @import "font-awesome"; ``` to `application.scss`. `font-awesome-sass` is not a dependency of WiceGrid in case you decide to style WiceGrid icons differently, so you need to add it explicitly to your Gemfile: ```ruby gem 'font-awesome-sass', '~> 4.3' ``` ## CI_LIKE Setting a configuration value in Wice::Defaults::STRING_MATCHING_OPERATORS to CI_LIKE will result in the following SQL generated for string filters: ```sql UPPER(table.field) LIKE UPPER(?)" ``` ## USE_DEFAULT_SCOPE New `USE_DEFAULT_SCOPE` configuration value from @nathanvda. By default ActiveRecord calls are always executed inside `Model.unscoped{}`. Setting `USE_DEFAULT_SCOPE` to `true` will use the default scope for all queries. # 3.5.0 * In addition to two icons "SET ALL" and "UNSET ALL" in the action column, there is now an option to use a standard HTML checkbox. This is now the default. * Support for Bootstrap Datepicker. A suggested way to use Bootstrap Datepicker in a Rails app is https://github.com/Nerian/bootstrap-datepicker-rails. Configuration variable HELPER_STYLE sets the default flavor of date pickers. Can also be set per grid with helper_style: :bootstrap * :calendar jQuery UI datepicker * :bootstrap Bootstrap datepicker * :standard * Italian locale * Spanish locale * various fixes * Configuration variable ALLOW_SHOWING_ALL_QUERIES renamed to ALLOW_SHOWING_ALL_RECORDS # 3.4.14 Wice::Defaults::HIDE_ALL_LINK_FROM is nil by default # 3.4.13 New configuration variable Wice::Defaults::HIDE_ALL_LINK_FROM! When set and the total number of row exceeds its value, the "SHOW ALL" link disappears. # 3.4.12 fixes # 3.4.11 started adding HTML5 datepicker changed how relations are detected so that it can work with relation proxies (aka octopus) # 3.4.10 bug fixes better support for :group # 3.4.9 better support for Asset Pipeline bugfixes dropped support for Ruby 1.8 # 3.4.8 a friendlier exception message when a constant is missing in wice_grid_config.rb bugfixes # 3.4.6 Better support for Turbolinks Better support for ActiveRelation #references variable Wice::Defaults::PAGE_METHOD_NAME # 3.4.5 Support for ActiveRelation #references bugfixes # 3.4.4 bugfixes # 3.4.3 bugfixes # 3.4.2 External columns processors Operators '<','>','<=','>=','=' in the integer column Bugfixes # 3.4.1 Support for Bootstrap 3 # 3.4.0 Support for Rails 4 # 3.3.0 The with_paginated_resultset callback receives an ActiveRelation object, not a lambda Wice::Defaults::DATEPICKER_YEAR_RANGE added to the config to define the default year range in Datepicker (https://github.com/leikind/wice_grid/issues/61) Improvement of the javascript calendar control: if the FROM field is set to a value after TO, TO is set to the value of FROM. Vice versa: if the TO field is set to a value before FROM, FROM is set to the value of TO New view helpers filter_and_order_state_as_hash(grid) and filter_state_as_hash(grid) HTML tag caption supported Support for Ruby 2.0 2 errors fixed in the Saved Queries UI ( https://github.com/leikind/wice_grid/issues/89 ) Bug fixed: extra_request_parameters not propagating to the pagination panel Documentation improvements # 3.2.2 improvement of the javascript calendar control: if the FROM field is set to a value after TO, TO is set to the value of FROM. Vice versa: if the TO field is set to a value before FROM, FROM is set to the value of TO Wice::Defaults::DATEPICKER_YEAR_RANGE added to the config to define the default year range in Datepicker (https://github.com/leikind/wice_grid/issues/61) support for Ruby 2.0 supported 2 js errors fixed in the Saved Queries UI ( https://github.com/leikind/wice_grid/issues/89 helpers filter_and_order_state_as_hash(grid) and filter_state_as_hash(grid) the with_paginated_resultset callback receives an ActiveRelation object, not a lambda # 3.2.1 action_column can now also take a block. If the block returns a falsy value, no checkbox will be rendered. A fix: the css class submitted to column is also added to the tags of the column. Filter related code has been refactored: condition generators are unified together with view column processors into one module. Writing your own filters has been simplified. The default filter for numeric columns has been replaced by a simple one field filter which checks the values for equality, not the inclusion in a range. New column parameter :filter_type allows to load custom alternative filters. The old numeric range filter can still be used by specifying filter_type: :range. See lib/columns/column_processor_index.rb for the list of available filters. # 3.2.0 Fixes: https://github.com/leikind/wice_grid/issues/83 https://github.com/leikind/wice_grid/issues/82 action_column can now also take a block. If the block returns a falsy value, no checkbox will be rendered A fix: the css class submitted to column is also added to tags of the column Filter related code has been refactored: condition generators are unified together with view column processors into one module. Writing your own filters has been simplified. The default filter for numeric columns has been replaced by a simple one field filter which checks the values for equality, not the inclusion in a range. New column parameter :filter_type allows to load custom alternative filters. The old numeric range filter can still be used by specifying filter_type: :range. See lib/columns/column_processor_index.rb for the list of available filters. # 3.0.4 bugfixes # 3.0.3 bugxixes # 3.0.2 bugxixes # 3.0.1 Fixed the "Cannot modify SafeBuffer in place" problem and thus Rails # 3.0.8 and # 3.0.9 Support for ActiveRecord::Relation # 3.0.0 Rails 3 support 0.6 wice_grid_custom_filter_params used to be a view helper, not it is also accessible from the controller, to be used in cases like redirect_to(my_resource_path(wice_grid_custom_filter_params(...))) auto reloading filters helper export_to_csv_javascript to create custom CSV buttons option :hide_csv_button to hide the default CSV export button Method WiceGrid#selected_records and parameter :after were a bit of a mess and have been substituted by * WiceGrid#current_page_records returning records on the current page * WiceGrid#all_pages_records returning records browsable throughout all pages * :with_paginated_resultset - callback to process records on the current page * :with_resultset - callback to process records browsable throughout all pages Compliant with Rails 1.2.8 with or without rails_xss and erubis Ruby 1.9.1 compliance Dropdowns generated with :custom_filter => :auto and :custom_filter => [method chain] are now ordered by option labels how_filters => false is the same as :show_filters => :no and :show_filters => true is the same as :show_filters => :always action_column - Adds a column with checkboxes for each record. Useful for actions with multiple records, for example, delete the selected records. using merge_conditions to merge conditions :) WiceGrid is now compatible with the new Rails XSS behavior which will be the default in Rails # 3.0 and can be used in Rails 2.3.5 using the rails_xss plugin. Read http://github.com/nzkoz/rails_xss for more wice_grid_custom_filter_params support for with_scope and with_exclusive_scope :total_entries parameter added to initialize_grid (will_paginate) Localization assert_keys wherever possible == 0.5 Today "WiceGrid":http://leikind.org/pages/wicegrid has reached its next level of maturity and was awarded the tag of version 0.5. This version of WiceGrid is accompanied by an application called _WiceGrid Examples_ running "online":http://grid.leikind.org/ and with source code available on "GitHub":http://github.com/leikind/wice_grid_examples. Here's a list of changes as compared with "version 0.4":https://blog.wice.eu/2009/7/6/moving-to-github-and-wicegrid-version-0-4 : --- RHTML <%= grid(@tasks_grid) do |g| ... end -%> <% selected = @tasks_grid.selected_records %>

<%= selected.size %> records selected: <%= selected.map(&:id).to_sentence %>

--- "See an online example":http://grid.leikind.org/integration_with_application_view h4. placement of filter related icons A change in placement of filter related icons (filter icon, reset icon, show/hide icon): if the last column doesn't have any filter or a column name, icons will be placed in the header of this column, otherwise it falls back to the usual behavior when an additional table column is added. To change the behavior back to the old style, set @Wice::Defaults::REUSE_LAST_COLUMN_FOR_FILTER_ICONS@ to @false@ in the configuration file. "See an online example":http://grid.leikind.org/custom_filters2 h4. wice_grid_assets generator Copying asset files (images, css, js, and the configuration file) is now done by a plugin generator, not rake tasks: --- ./script/generate wice_grid_assets --- h4. blank slate Blank slate feature: it is now possible to replace the grid with some alternative view if no filters are active and there are no records to render: --- RHTML <%= grid(@grid) do |g| g.blank_slate do "There are no records" end g.column do |product| ... end end -%> --- There are also two alternative three ways to do it: --- Ruby g.blank_slate "some text to be rendered" --- and --- Ruby g.blank_slate :partial => "partial_name" --- "See an online example":http://grid.leikind.org/no_records h4. custom filters with symbols Improvement to custom filters, namely to --- Ruby :custom_filter => :symbol --- and --- Ruby :custom_filter => [:symbol1, :symbol2, :symbol3] --- Now, if the last method returns an array of 2 elements, the first element becomes the select option label and the second - the select option value (usually @id@). Before this change the value returned by the method had been used for both the value and the label of the select option. "See an online example":http://grid.leikind.org/custom_filters3 h4. custom filters and NULL Values @null@ and @not null@ in a generated custom filter dropdown are treated specially, as SQL @null@ statement and not as strings. Value @null@ is transformed into SQL condition @IS NULL@, and @not null@ into @IS NOT NULL@ . Thus, if in a filter defined by --- Ruby :custom_filter => {'No' => 'null', 'Yes' => 'not null', '1' => 1, '2' => '2', '3' => '3'} --- values @1@, @2@ and @'No'@ are selected (in a multi-select mode), this will result in the following SQL: --- SQL ( table.field IN ( '1', '2' ) OR table.field IS NULL ) --- "See an online example":http://grid.leikind.org/null_values h4. Wice::Defaults::STRING_MATCHING_OPERATORS in addition to the configuration constant @Wice::Defaults::STRING_MATCHING_OPERATOR@ to define the operator for matching strings (@LIKE@ in most cases), hash @Wice::Defaults::STRING_MATCHING_OPERATORS@ was added to specify string matching operators on per-database basis: --- Ruby Wice::Defaults::STRING_MATCHING_OPERATORS = { 'ActiveRecord::ConnectionAdapters::MysqlAdapter' => 'LIKE', 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter' => 'ILIKE' } --- A use-case for this is a Rails application connecting to two databases, one of which is MySQL, and the other is Postgresql. To use case-insensitive matching in Postgresql 'ILIKE' has to be used, but this operator is unknown to MySQL. h4. td_html_attrs and table_html_attrs shortcuts @:td_html_attrs@ in column definitions and @table_html_attrs@ in the table definitions are mostly used to add css classes, so a shorter way has been added to add css classes. Instead of --- RHTML <%= grid(@grid, :table_html_attrs => {:class => 'css_class1'}) do |g| g.column(:td_html_attrs => {:class => 'css_class2'}) do ... end end -%> --- It's possible to just use the new @:class@ option: --- RHTML <%= grid(@grid, :class => 'css_class1') do |g| g.column(:class => 'css_class2') do end end -%> --- h4. allow_multiple_selection New options for @column@: @:allow_multiple_selection@, use @:allow_multiple_selection => false@ to disable switching between single and multiple selection modes for custom dropdown boxes. "See an online example":http://grid.leikind.org/custom_filters4 h4. allow_ordering New parameter for @column@: @:allow_ordering@. Use @:allow_ordering => false@ to disable the ordering of the column. "See an online example":http://grid.leikind.org/basics6 h4. allow_showing_all_records Parameter @:allow_showing_all_records@ moved from @initialize_grid@ to the view helper. h4. other * Default styles updated. * Javascript code cleaned up and refactored. * Quite some number of bugs fixed == 0.4 * Detached filters: it has become possible to detach filters and place them anywhere on the page, before or after the grid. Read section "Detached Filters" in the README. * More control over Submit and Reset buttons. Two new view helpers: submit_grid_javascript returns javascript which applies current filters; reset_grid_javascript returns javascript which resets the grid, clearing the state of filters. This allows to create your own Submit and Reset buttons anywhere on the page with the help of button_to_function: <%= button_to_function "Submit", submit_grid_javascript(@grid) %> <%= button_to_function "Reset", reset_grid_javascript(@grid) %> To complement this feature there are two new parameters in the +grid+ helper :hide_submit_button and :hide_reset_button which hide default buttons in the grid if set to true. Together with detached filter this allows to completely get rid of the filter row with filters and icons. * erb_mode option has been moved to the grid view helper - watch for warnings and remove the parameter from initialize_grid if you have it there. * helper include_wice_grid_assets will require WiceGrid javascripts and stylesheets on demand, that is, only if at least one initialize_grid has been called in the controller. Otherwise the helper returns an empty string. However, you can force the old unconditioned behavior if you need by submitting parameter :load_on_demand set to false: <%= include_wice_grid_assets(:load_on_demand => false) %> * Compatibility with Rails asset caching. Helpers names_of_wice_grid_stylesheets and names_of_wice_grid_javascripts return names of stylesheet and javascript files and can be used with stylesheet_link_tag and javascript_include_tag with :cache => true. Using this trick you have to deal with the parameters correctly, mind that Prototype has to be loaded before WiceGrid javascripts: <%= stylesheet_link_tag *(['core', 'modalbox'] + names_of_wice_grid_stylesheets + [ {:cache => true}]) %> <%= javascript_include_tag *([:defaults] + names_of_wice_grid_javascripts + [ 'ui', 'swfobject', {:cache => true}]) %> * When a page with a WiceGrid instance is loaded there is check a small chunk of javascript that checks whether Prototype is loaded and whether the main WiceGrid javascript files is loaded and is of the correct version. Problems are reported to the user via alert() dialog boxes. This check has now been disabled in the production mode. * The default field separator in generated CSV (comma) can now be overridden by setting :enable_export_to_csv to a string instead of +true+. * It is possible to add your own handcrafted HTML after and/or before each grid row. This works similar to +row_attributes+, by adding blocks after_row and before_row: <%= grid(@tasks_grid) do |g| g.before_row do |task| if task.active? "Custom line for #{t.name}" # this would add a row # before every active task row else nil end end ....... end %> * Bug fixes * Refactoring ============= === 03/04/2009 Possibility to add custom lines after and/or before a grid row. === 16/03/2009 Option to override the default field separator in generated CSV (comma). === 13/02/2009 a bug fix for incorrect generation if dom ids for javascript calendar filter. Happened only for columns belonging to joined tables === 12/01/2009 WiceGrid 0.3 released === 12/01/2009 All records mode === 10/12/2008 custom_filter made Rails-compliant, a new flavor - Array of two-element arrays === 4/12/2008 A single helper to include all assets in a page A javascript error message if Prototype is not loaded A javascript error message if wice_grid.js is not loaded Added status info to the pagination line: « Previous 1 2 3 Next » 1-20 / 50 === 3/12/2008 First implementation of saved queries === 25/11/2008 Negation for string filters: match records where this fiels DOES NOT include the given fragment. === 24/11/2008 The string matching operator for string filters (LIKE) has been moved to wice_grid_config.rb in order to make it easier to substitute it with something else, for example, ILIKE of Postgresql. === 19/11/2008 Experimental feature: :table_alias parameter to allow ordering and filtering for joining associations referring the same table. (See "Joined associations referring to the same table" in README) === 18/11/2008 Refactoring === 6/11/2008 Ability to provide a callback to a Proc object or a method, the callback will be called with the objects of the current selection of objects (throughout all pages). Can be used to use the WiceGrid filters set up by the user for further processing of the user's selection of objects. === 5/11/2008 Javascript calendars as Date/Datetime filters === 4/11/2008 Ability to inject custom sql code into the ORDER BY clause, for example, ORDER BY char_length(table1.foo) === 4/11/2008 Creates a new branch for version 2.3 === 21/10/2008 A bugfix related to custom filters influencing other columns with filters A more informative error message if the grid can't find the underlying database column for a view column (incorrect :column_name and :model) === 8/10/2008 New view helper parameter :sorting_dependant_row_cycling - When set to true (by default it is false) the row styles +odd+ and +even+ will be changed only when the content of the cell belonging to the sorted column changes. In other words, rows with identical values in the ordered column will have the same style (color). === 3/10/2008 For simple columns like g.column :column_name => 'Username', :attribute_name => 'username' do |account| account.username end the following blockless shortcut can be used: g.column :column_name => 'Username', :attribute_name => 'username' In this case +attribute_name+ will be used as the method name to send to the ActiveRecord instance. === revision 27 (30/09/2008) * CSV export * Custom filters can switch between a dropdown list and a multiple select list, thus allowing to search for records matching more that one value (operator OR) === revision 17 (19/08/2008) * A bug fixed: extra_request_parameters did not propagate to will_paginate page panel. Now it does. === revision 13 (6/08/2008) * File config.rb renamed. * New parameters for +column+ : * :boolean_filter_true_label - overrides the default value for BOOLEAN_FILTER_TRUE_LABEL ('+yes+') in the config. Only has effect in a column with a boolean filter. * :boolean_filter_false_label - overrides the default value for BOOLEAN_FILTER_FALSE_LABEL ('+no+') in the config. Only has effect in a column with a boolean filter. * :filter_all_label - overrides the default value for BOOLEAN_FILTER_FALSE_LABEL ('--') in the config. Has effect in a column with a boolean filter _or_ a custom filter. === revision 11 * New row_attributes method to dynamically generate HTML attributes for the tag: <%= grid(@portal_applications_grid) do |g| g.row_attributes{ |portal_application| {:id => "#{@portal_applications_grid.name}_row_#{portal_application.id}"} } g.column{ |portal_application| ... } g.column{ |portal_application| ... } end -%> * The column block can now optionally return an array of two values, where the first element is the cell contents and the second is a hash of HTML attributes to be added for the tag of the current cell. === revision 10 * New parameter +grid+ parameter: :extra_request_parameters. (Read http://redmine.wice.eu/api/wice_grid/classes/Wice/GridViewHelper.html#M000002) === 0 ================================================ FILE: Gemfile ================================================ source 'https://rubygems.org' # Use custom version of jquery-ui-rails and font-awesome-sass gem 'jquery-ui-rails', github: 'dima4p/jquery-ui-rails' gem 'font-awesome-sass', github: 'dima4p/font-awesome-sass' gemspec ================================================ FILE: Guardfile ================================================ # A sample Guardfile # More info at https://github.com/guard/guard#readme ## Uncomment and set this to only include directories you want to watch # directories %w(app lib config test spec features) \ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")} ## Note: if you are using the `directories` clause above and you are not ## watching the project directory ('.'), then you will want to move ## the Guardfile to a watched dir and symlink it back, e.g. # # $ mkdir config # $ mv Guardfile config/ # $ ln -s config/Guardfile . # # and, you'll have to watch "config/Guardfile" instead of "Guardfile" # Note: The cmd option is now required due to the increasing number of ways # rspec may be run, below are examples of the most common uses. # * bundler: 'bundle exec rspec' # * bundler binstubs: 'bin/rspec' # * spring: 'bin/rspec' (This will use spring if running and you have # installed the spring binstubs per the docs) # * zeus: 'zeus rspec' (requires the server to be started separately) # * 'just' rspec: 'rspec' guard :rspec, cmd: "bundle exec rspec", all_after_pass: false, all_on_start: false, failed_mode: :focus do require "guard/rspec/dsl" dsl = Guard::RSpec::Dsl.new(self) # Feel free to open issues for suggestions and improvements # RSpec files rspec = dsl.rspec watch(rspec.spec_helper) { rspec.spec_dir } watch(rspec.spec_support) { rspec.spec_dir } watch(rspec.spec_files) # Ruby files ruby = dsl.ruby dsl.watch_spec_files_for(ruby.lib_files) # Rails files rails = dsl.rails(view_extensions: %w(erb haml slim)) dsl.watch_spec_files_for(rails.app_files) dsl.watch_spec_files_for(rails.views) watch(rails.controllers) do |m| [ rspec.spec.call("routing/#{m[1]}_routing"), rspec.spec.call("controllers/#{m[1]}_controller"), rspec.spec.call("acceptance/#{m[1]}") ] end # Rails config changes watch(rails.spec_helper) { rspec.spec_dir } watch(rails.routes) { "#{rspec.spec_dir}/routing" } watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" } # Capybara features specs watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") } watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") } # Turnip features and steps watch(%r{^spec/acceptance/(.+)\.feature$}) watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m| Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance" end end ================================================ FILE: MIT-LICENSE ================================================ Copyright (c) 2008-2014 Yuri Leikind Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![Version](http://img.shields.io/gem/v/wice_grid.svg)](https://rubygems.org/gems/wice_grid) [![CircleCI](https://circleci.com/gh/leikind/wice_grid.svg?style=svg)](https://circleci.com/gh/leikind/wice_grid) [![Inline docs](http://inch-ci.org/github/leikind/wice_grid.svg)](http://inch-ci.org/github/leikind/wice_grid) [![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#license) # WiceGrid - [Intro](#intro) - [Requirements and Rails versions](#requirements-and-rails-versions) - [Installation](#installation) - [Basics](#basics) - [Rendering filter panel](#rendering-filter-panel) - [Initial Ordering](#initial-ordering) - [Records Per Page](#records-per-page) - [Conditions](#conditions) - [Queries with join tables](#queries-with-join-tables) - [Joined associations referring to the same table](#joined-associations-referring-to-the-same-table) - [More than one grid on a page](#more-than-one_grid-on-a-page) - [Custom Ordering](#custom-ordering) - [Custom Sorting](#custom-sorting) - [Filters](#filters) - [Custom dropdown filters](#custom-dropdown-filters) - [Numeric Filters](#numeric-filters) - [Date and DateTime Filters](#date-and-datetime-filters) - [Detached Filters](#detached-filters) - [Defaults](#defaults) - [Testing](#testing) - [Bug reports](#bug-reports) ## Intro WiceGrid is a Rails grid plugin. One of the goals of this plugin was to allow the programmer to define the contents of the cell on their own, just like one does when rendering a collection via a simple table (and this is what differentiates WiceGrid from various scaffolding solutions), but automate implementation of filters, ordering, paginations, CSV export, and so on. Ruby blocks provide an elegant means for this. WiceGrid builds the call to the ActiveRecord layer for you and creates a table view with the results of the call including: * Pagination * Sortable columns * Filtering by multiple columns * Export to CSV * Saved queries Filters are added automatically according to the type of the underlying DB column. Filtering by more than one column at the same time is possible. More than one such grid can appear on a page, and manipulations with one grid do not have any impact on others. WiceGrid does not take a collection as an input, it works directly with ActiveRecord. WiceGrid does not use XHR calls to reload itself, instead simple GET requests are used for this, nevertheless, all other page parameters are respected and preserved. WiceGrid works well with Turbolinks. WiceGrid views do not contain forms so you can include it in your own forms. WiceGrid is known to work with MySQL, Postgres, and Oracle. Continue reading for more information or check out our [CHANGELOG](https://github.com/leikind/wice_grid/blob/rails7/CHANGELOG.md) to find out whats been going on. ## Requirements and Rails versions ``` # Rails 7 with importmap, HotWire, and dartsass-rails gem 'wice_grid', '~> 7.1' # Rails 5, 6, and 7.0 without importmap (see below) gem 'wice_grid', '~> 6.1' # Rails 4 gem 'wice_grid', '3.6.2' ``` WiceGrid relies on jQuery and coffeescript and the dependencies are added to the gem specification. If you need a JS Datepicker, WiceGrid supports jQuery Datepicker or Bootstrap Datepicker, so you might need one of those. See the section "Installation" for details on how to use datepickers. ## Installation Here is described the case when the gems `importmap` and `dartsass-rails` are in use. For the case of `sass-rails` and other JavaScript approach see the branch [rails6](https://github.com/leikind/wice_grid/tree/rails6). Add the following lines to your `Gemfile`: ```ruby gem "wice_grid" gem 'font-awesome-sass', github: 'dima4p/font-awesome-sass' ``` The current version of the gem `font-awesome-sass` has a [bug](https://github.com/FortAwesome/font-awesome-sass/issues/222). As soon as it is fixed, you can use the standard version. If the application uses Date and DateTime filters, you have to use jQuery Datepicker from the gem `jquery-ui-rails`. ```ruby gem 'jquery-ui-rails', github: 'dima4p/jquery-ui-rails' ``` The regular version of the gem also have an [incompatibility](https://github.com/jquery-ui-rails/jquery-ui-rails/pull/153) with `dartsass-rails` that is fixed in the custom version. I hope it will be fixed too. Alternatively you can also use Bootstrap Datepicker. ```ruby gem 'bootstrap' ``` Note: `font-awesome-sass` is not a dependency of WiceGrid in case you decide to style WiceGrid icons differently. But if you use it it must be the version that is compatible with `dartsass-rails`. As well as the gem `jquery-ui-rails`. Then run `bundle install`. Run the generator: ``` rails g wice_grid:install ``` This adds the following file: * `config/initializers/wice_grid_config.rb` You have to edit it if required in order to define which kind of the Datepicker to use changing the value for `DEFAULT_FILTER_FOR_DATE` and `DEFAULT_FILTER_FOR_DATETIME`. In the file `config/importmap.rb` add the following lines: ```ruby pin "jquery", to: "jquery3.min.js", preload: true pin "wice_grid", to: "wice_grid.js", preload: true pin "jquery-ui", to: "jquery-ui.js", preload: true ``` You do not need the last line if you do not use jQuery Datepicker. If you are going to use `bootstrap` add also the lines: ```ruby pin "bootstrap", to: "bootstrap.min.js", preload: true pin "@popperjs/core", to: "popper.js", preload: true ``` In the file `app/assets/config/manifest.js` add the following lines: ``` //= link wice_grid.js //= link jquery3.min.js //= link jquery-ui.js //= link bootstrap.min.js //= link popper.js ``` The last two lines are required if you use `bootstrap`. And the middle is not needed if you do not use jQuery Datepicker. If the application uses Date and DateTime filters, you have to use one of jQuery Datepicker, Bootstrap Datepicker or the standard Rails date helper. Here is an example of `app/javascript/application.js` if jQuery Datepicker is used: ```javascript import "jquery" // this import first import "jquery-ui" import "wice_grid" ``` Here is `app/javascript/application.js` if Bootstrap Datepicker is used: ```javascript import "jquery" // this import first import "bootstrap" import "wice_grid" ``` Require WiceGrid and Font Awesome CSS in your `app/assets/stylesheets/application.scss`: ```scss @import "wice_grid"; @import "font-awesome"; @import "bootstrap"; ``` This will provide very basic styles, not specifying exactly how the table should look like. WiceGrid uses icons from Font Awesome. Should you decide to write you own styles for WiceGrid, you can just remove these imports and write your own styles. If the application does not use Bootstrap you do not need the last line. But the markup generated by WiceGrid will have correct classes and will fit nicely if you include Bootstrap. ## Basics The simplest example of a WiceGrid for one simple DB table is the following: Controller: ```ruby @tasks_grid = initialize_grid(Task) ``` It is also possible to use an `ActiveRecord::Relation` instance as the first argument: ```ruby @tasks_grid = initialize_grid(Task.where(active: true)) ``` View: ```erb <%= grid(@tasks_grid) do |g| g.column do |task| task.id end g.column do |task| task.title end g.column do |task| task.description end g.column do |task| task.archived? ? 'Yes' : 'No' end g.column do |task| link_to('Edit', edit_task_path(task)) end end -%> ``` Code `g.column do |task| ... end` defines everything related to a column in the resulting view table including column names, sorting, filtering, the content of the column cells, etc. The return value of the block is the table cell content. Column names are defined with parameter `:name`: ```erb <%= grid(@tasks_grid) do |g| g.column name: 'ID' do |task| task.id end g.column name: 'Title' do |task| task.title end g.column name: 'Description' do |task| task.description end g.column name: 'Archived' do |task| task.archived? ? 'Yes' : 'No' end g.column do |task| link_to('Edit', edit_task_path(task)) end end -%> ``` To add filtering and ordering, declare to which column in the underlying database table(s) the view column corresponds using parameter `:attribute`: ```erb <%= grid(@tasks_grid) do |g| g.column name: 'ID', attribute: 'id' do |task| task.id end g.column name: 'Title', attribute: 'title' do |task| task.title end g.column name: 'Description', attribute: 'description' do |task| task.description end g.column name: 'Archived', attribute: 'archived' do |task| task.archived? ? 'Yes' : 'No' end g.column do |task| link_to('Edit', edit_task_path(task)) end end -%> ``` This will add sorting links and filters for columns `Username` and `Active`. The plugin automatically creates filters according to the type of the database column. In the above example a text field will be created for column Title (title is a string), for column `Archived` a dropdown filter will be created with options 'Yes', 'No', and '--', and for the integer ID two short text fields are added which can contain the numeric range (more than, less than). It is important to remember that `:attribute` is the name of the database column, not a model attribute. Of course, all database columns have corresponding model attributes, but not all model attributes map to columns in the same table with the same name. Read more about available filters in the documentation for the column method. Read the section about custom dropdown filters for more advanced filters. For columns like ```ruby g.column name: 'Title', attribute: 'title' do |task| task.title end ``` where the block contains just a call to the same attribute declared by :attribute, the block can be omitted: ```erb <%= grid(@tasks_grid) do |g| g.column name: 'ID', attribute: 'id' g.column name: 'Title', attribute: 'title' g.column name: 'Description', attribute: 'description' g.column name: 'Archived', attribute: 'archived' do |task| task.archived? ? 'Yes' : 'No' end g.column do |task| link_to('Edit', edit_task_path(task)) end end -%> ``` In this case `name` will be used as the method name to send to the ActiveRecord instance. If only ordering is needed, and no filter, we can turn off filters using `:filter` : ```ruby g.column name: 'ID', attribute: 'id', filter: false ``` If no ordering links are needed, use `ordering: false`: ```ruby g.column name: 'Added', attribute: 'created_at', ordering: false ``` It is important to understand that it is up to the developer to make sure that the value returned by a column block (the content of a cell) corresponds to the underlying database column specified by `:attribute` (and `:assoc` discussed below). ### Rendering filter panel The filter panel can be shown and hidden clicking the icon with binoculars. The way the filter panel is shown after the page is loaded is controlled via parameter `:show_filters` of the `grid` helper. Possible values are: * `:when_filtered` - the filter is shown when the current table is the result of filtering * `:always` - always show the filter * `:no` - never show the filter Example: ```erb <%= grid(@tasks_grid, show_filters: :always) do |g| ...... end -%> ``` Filter related icons (filter icon, reset icon, show/hide icon) are placed in the header of the last column if it doesn't have any filter or a column name, otherwise an additional table column is added. To always place the icons in the additional column, set `Wice::Defaults::REUSE_LAST_COLUMN_FOR_FILTER_ICONS` to `false` in the configuration file. ### Initial ordering Initializing the grid we can also define the column by which the record will be ordered on the first rendering of the grid, when the user has not set their ordering setting by clicking the column label, and the order direction: ```ruby @tasks_grid = initialize_grid(Task, order: 'tasks.title', order_direction: 'desc' ) ``` ### Records per page The number of rows per page is set with `:per_page`: ```ruby @tasks_grid = initialize_grid(Task, per_page: 40) ``` ### Conditions The `initialize_grid` method supports a `:conditions` parameter which is passed on to the underlying ActiveRecord, so it can be in any format processable by ActiveRecord: ```ruby @tasks_grid = initialize_grid(Task, conditions: ["archived = false and estimated_time > ?", 100] ) @tasks_grid = initialize_grid(Task, include: :project, conditions: {archived: false, project: {active: true}} ) ``` A good example is substituting a common pattern like ```ruby @user_groups = @portal_application.user_groups ``` with WiceGrid code: ```ruby @user_groups_grid = initialize_grid( UserGroup, conditions: ['portal_application_id = ?', @portal_application] ) ``` Alternatively, instead of a Class object as the first parameter, you can use ActiveRecord::Relation: ```ruby @tasks_grid = initialize_grid( Task.where(archived: false, projects: {active: true}).joins(:project) ) ``` Please note that though all queries inside of WiceGrid are run without the default scope, if you use an ActiveRecord::Relation instance to initialize grid, it will already include the default scope. Thus you might consider using `unscoped`: ```ruby @tasks_grid = initialize_grid( Task.unscoped.where(archived: false, projects: {active: true}).joins(:project) ) ``` ### Queries with join tables To join other tables, use `:include`: ```ruby @products_grid = initialize_grid(Product, include: :category, order: 'products.name', per_page: 20 ) ``` The value of `:include` can be an array of association names: ```ruby include: [:category, :users, :status] ``` If you need to join tables to joined tables, use hashes: ```ruby include: [:category, {users: :group}, :status] ``` Note that if we want to order initially by a column from a joined table we have to specify the table and the column name with the sql dot notation, that is, `products.name`. To show columns of joined tables in the view table, specify the corresponding association with `:assoc`: ```erb <%= grid(@products_grid) do |g| g.column name: 'Product Name', attribute: 'name' do |product| # primary table link_to(product.name, product_path(product)) end g.column name: 'Category', attribute: 'name', assoc: :category do |product| # joined table product.category.name end %> ``` Please note that the blockless definition of the column can also be used with joined tables: ``` g.column name: 'Category', attribute: 'name', assoc: :category ``` If an association is mentioned in the column definition, it can be omitted from `:include` in `initialize_grid`. Thus, the above example can be rewritten without `:category` in `:include`: ```ruby @products_grid = initialize_grid(Product, order: 'products.name', per_page: 20 ) ``` ```erb <%= grid(@products_grid) do |g| g.column name: 'Product Name', attribute: 'name' do |product| # primary table link_to(product.name, product_path(product)) end g.column name: 'Category', attribute: 'name', assoc: :category %> ``` ### Joined associations referring to the same table In case there are two joined associations both referring to the same table, ActiveRecord constructs a query where the second join provides an alias for the joined table. To enable WiceGrid to order and filter by columns belonging to different associations but originating from the same table, set `:table_alias` to this alias: Model: ```ruby class Project < ActiveRecord::Base belongs_to :customer, class_name: 'Company' belongs_to :supplier, class_name: 'Company' end ``` Controller: ```ruby @projects_grid = initialize_grid(Project) ``` View: ```erb <%= grid(@projects_grid, show_filters: :always) do |g| g.column name: 'Project Name', attribute: 'name' g.column name: 'Customer company', assoc: :customer, attribute: 'name' g.column name: 'Supplier company', assoc: :supplier, attribute: 'name', table_alias: 'suppliers_projects' end -%> ``` ### More than one grid on a page It is possible to use more that one grid on a page, each with its own state. To do so, you must specify the name of the grid in `initialize_grid` using parameter `:name`. The name serves as the base name for HTTP parameters, DOM IDs, etc, so it is important that all grids on a page have different names. The default name is 'grid'. The name can only contain alphanumeric characters. ```ruby @projects_grid = initialize_grid(Project, name: 'g1') @tasks_grid = initialize_grid(Task, name: 'g2') ``` ### Custom Ordering It is possible to change the way results are ordered injecting a chunk of SQL code, for example, use `ORDER BY INET_ATON(ip_address)` instead of `ORDER BY ip_address`. To do so, provide parameter `:custom_order` in the initialization of the grid with a `Hash` where keys are fully qualified names of database columns, and values are anything that can be passed to ActiveRecord's `order` method (without specifying `ASC` or `DESC`.) #### String Starting in Rails 5.2, you may need to whitelist `String` values with `Arel.sql` to avoid a warning or error. ```ruby @hosts_grid = initialize_grid(Host, custom_order: { 'hosts.ip_address' => Arel.sql('INET_ATON(hosts.ip_address)') }) ``` It is possible to use `?` instead of the name of the column in the `Hash` value: ```ruby @hosts_grid = initialize_grid(Host, custom_order: { 'hosts.ip_address' => Arel.sql('INET_ATON( ? )') }) ``` #### Arel::Attributes::Attribute Assuming you wish to display `hosts.ip_address` but sort by another column named `hosts.ip_address_number`: ```ruby @hosts_grid = initialize_grid(Host, custom_order: { 'hosts.ip_address' => Arel::Table.new(:hosts)[:ip_address_number] }) ``` #### Proc You can use a `Proc` to return a `String` or `Arel::Attributes::Attribute` as above. ```ruby @hosts_grid = initialize_grid(Host, custom_order: { 'hosts.ip_address' => lambda{|f| Arel.sql(request[:numeric_sorting] ? "INET_ATON( #{f} )" : f) } }) ``` ### Custom Sorting While `:custom_order` lets you define SQL that determines the results order, you may want to sort the result by arbritrary Ruby code. The `:sort_by` option on columns lets you define a `Proc` that determines the sorting on that column. This `Proc` is passed to Ruby's `Enumerable#sort_by`. ```ruby grid.column name: 'Status Name', attribute: 'name', sort_by: ->(status) { [status.number_of_vowels, status] } ``` You can also use `:sort_by` to add sorting on values that are not columns in the database. In this case, you must also define an arbitrary `:attribute` option that serves as the request's sort key parameter. ```ruby grid.column name: 'Task Count', attribute: 'task_count', sort_by: ->(status) { status.tasks.count } do |status| status.tasks.count end ``` Note that `sort_by` will load all records into memory to sort them (even the ones not on the current page), so it may not be appropriate for use with a large number of results. ## Filters Each column filter type is supported by a `column processor`. Each `column processor` is responsible for * generating HTML and supporting Javascript for the filter, input fields, dropdowns, javascript calendars, etc * converting HTTP parameters from those input fields into ActiveRelation instances By default column filters depend on the type of the underlying database column. You can override these defaults in two ways: * defining a custom filter with `:custom_filter`. Read more about it section "Custom dropdown filters". * overriding the `column processor` type with `:filter_type`. Which Column Processor is instantiated for which data types is defined in file `lib/wice/columns/column_processor_index.rb`: ```ruby module Wice module Columns COLUMN_PROCESSOR_INDEX = ActiveSupport::OrderedHash[ #:nodoc: :action, 'column_action', # Special processor for action column, columns with checkboxes :text, 'column_string', :string, 'column_string', :rails_datetime_helper, 'column_rails_datetime_helper', # standard Rails datepicker helper :rails_date_helper, 'column_rails_date_helper', # standard Rails date helper :jquery_datepicker, 'column_jquery_datepicker', :bootstrap_datepicker, 'column_bootstrap_datepicker', :html5_datepicker, 'column_html5_datepicker', # not ready :integer, 'column_integer', :range, 'column_range', :float, 'column_float', :decimal, 'column_float', :custom, 'column_custom_dropdown', # Special processor for custom filter columns :boolean, 'column_boolean' ] end end ``` A good example for using `:filter_type` to change th default is numeric columns. By default `'column_integer'` is instantiated for `integer` columns, and it renders one input field. But it is also possible to use another Column Processor called `'column_range'` which renders two input fields and searches for values in the given the range instead of searching for values which equal the given search term. It also possible to define and use your own column processors outside of the plugin, in you application. Read more about this in section "Defining your own external filter processors". ### Custom dropdown filters It is possible to construct custom dropdown filters. A custom dropdown filter is essentially a dropdown list. Depending on the value of `column` parameter`:custom_filter` different modes are available: #### Array of two-element arrays or a hash An array of two-element arrays or a hash are semantically identical ways of creating a custom filter. Every first item of the two-element array is used for the label of the select option while the second element is the value of the select option. In case of a hash the keys become the labels of the generated dropdown list, while the values will be values of options of the dropdown list: ```ruby g.column name: 'Status', attribute: 'status', custom_filter: {'Development' => 'development', 'Testing' => 'testing', 'Production' => 'production'} g.column name: 'Status', attribute: 'status', custom_filter: [['Development', 'development'], ['Testing', 'testing'], ['Production', 'production']] ``` It is also possible to submit a array of strings or numbers, in this case every item will be used both as the value of the select option and as its label: ```ruby g.column name: 'Status', attribute: 'status', custom_filter: ['development', 'testing', 'production'] ``` #### :auto `:auto` - a powerful option which populates the dropdown list with all unique values of the column specified by `:attribute` and `:assoc`, if present. ```ruby g.column name: 'Status', attribute: 'status', custom_filter: :auto ``` In the above example all statuses will appear in the dropdown even if they don't appear in the current resultset. #### Custom filters and associations (joined tables) In most cases custom fields are needed for one-to-many and many-to-many associations. To correctly build a filter condition foreign keys have to be used, not the actual values rendered in the column. For example, if there is a column: ```ruby g.column name: 'Project Name', attribute: 'name', assoc: :project do |task| task.project.name if task.project end ``` adding `:custom_filter` like this: ```ruby g.column name: 'Project Name', attribute: 'name', assoc: :project, custom_filter: Project.find(:all).map{|pr| [pr.name, pr.name]} do |task| task.project.name if task.project end ``` is bad style and can fail, because the resulting condition will compare the name of the project, `projects.name` to a string, and in some databases it is possible that different records (projects in our example) have the same name. To use filter with foreign keys, it is advised to change the declaration of the column from `projects.name`, to `tasks.project_id`, and build the dropdown with foreign keys as values: ```ruby g.column name: 'Project Name', attribute: 'tasks.project_id', custom_filter: Project.find(:all).map{|pr| [pr.id, pr.name]} do |task| task.project.name if task.project end ``` However, this will break the ordering of the column - the column will be ordered by the integer foreign key. To fix this, we can override the ordering using `:custom_order`: ```ruby @tasks_grid = initialize_grid(Task, include: :project, custom_order: { 'tasks.project_id' => 'projects.name' } ) ``` #### Any other symbol (method name) or an array of symbols (method names) For one symbol (different from `:auto`) the dropdown list is populated by all unique values returned by the method with this name sent to all ActiveRecord objects throughout all pages. The conditions set up by the user are ignored, that is, the records used are all those found on all pages without any filters active. For an array of symbols, the first method name is sent to the ActiveRecord object if it responds to this method, the second method name is sent to the returned value unless it is `nil`, and so on. In other words, a single symbol mode is the same as an array of symbols where the array contains just one element. ```ruby g.column name: 'Version', attribute: 'expected_version_id', custom_filter: [:expected_version, :to_option] do |task| task.expected_version.name if task.expected_version end ``` There are two important differences from `:auto`: 1. The method does not have to be a field in the result set, it is just some value computed in the method after the database call and ActiveRecord instantiation. 2. Filtering by any option of such a custom filter will bring a non-empty list, unlike with `:auto`. This mode has one major drawback - this mode requires an additional query without `offset` and `limit` clauses to instantiate _all_ ActiveRecord objects, and performance-wise it brings all the advantages of pagination to nothing. Thus, memory- and performance-wise this can be really bad for some queries and tables and should be used with care. If the final method returns a atomic value like a string or an integer, it is used for both the value and the label of the select option element: ```html ``` However, if the retuned value is a two element array, the first element is used for the option label and the second - for the value. Typically, a model method like the following: ```ruby def to_option [name, id] end ``` together with ```ruby custom_filter: :to_option ``` would do the trick: ```html ``` Alternatively, a hash with the single key-value pair can be used, where the key will be used for the label, and the key - for the value: ```ruby def to_option {name => id} end ``` #### Special treatment of values 'null' and 'not null' Values `null` and `not null` in a generated custom filter are treated specially, as SQL `null` statement and not as strings. Value `null` is transformed into SQL condition `IS NULL`, and `not null` into `IS NOT NULL`. Thus, if in a filter defined by ```ruby custom_filter: {'No' => 'null', 'Yes' => 'not null', '1' => 1, '2' => '2', '3' => '3'} ``` values '1', '2' and 'No' are selected (in a multi-select mode), this will result in the following SQL: ```sql ( table.field IN ( '1', '2' ) OR table.field IS NULL ) ``` #### Multiple selection By default it is possible for any dropdown list to switch between single and multiple selection modes. To only allow single selection use `:allow_multiple_selection`: ```ruby g.column name: 'Expected in version', attribute: 'expected_version_id', custom_filter: [:expected_version, :to_option], allow_multiple_selection: false do |task| ... end ``` ### Numeric Filters Before version 3.2.1 the filter used for numeric columns was a range filter with two limits. Beginning with version 3.2.1 the default is a direct comparison filter with one input field. The old range filter can still be loaded using parameter `:filter_type` with value `:range`: ```ruby g.column filter_type: :range do |task| ... end ``` ### Date and DateTime Filters WiceGrid provides four filters for selecting dates and time: * ```:jquery_datepicker``` - Jquery datepicker (works for datetime, too) * ```:bootstrap_datepicker``` - Bootstrap datepicker (works for datetime, too) * ```:rails_date_helper``` - standard Rails date helper * ```:rails_datetime_helper``` - standard Rails datetime helper Specify a date/datetime filter just like you specify any other filter: ``` g.column name: 'Updated', attribute: 'updated_at', filter_type: :rails_datetime_helper do |task| task.updated_at.to_fs(:db) end ``` Default filters are defined in configuration constants Wice::Defaults::DEFAULT_FILTER_FOR_DATE and Wice::Defaults::DEFAULT_FILTER_FOR_DATETIME. #### jQuery UI DatePicker `(HELPER_STYLE = :calendar)` By default WiceGrid uses jQuery UI datepicker[http://jqueryui.com/demos/datepicker/] for Date and DateTime filters. Because this is part of the standard jQuery UI codebase, it is not bundled together with the plugin, and it is the responsibility of the programmer to include all necessary assets including localization files if the application is multilingual. jQuery UI datepicker does not have any time related controls, and when dealing with DateTime filters, the time value is ignored. Constants `DATE_FORMAT` and `DATETIME_FORMAT` in the configuration file define the format of dates the user will see, as well as the format of the string sent in a HTTP parameter. If you change the formats, make sure that lamdbas defined in `DATETIME_PARSER` and `DATE_PARSER` return valid DateTime and Date objects. jQuery `datepicker` uses a different format flavor, therefore there is an additional constant `DATE_FORMAT_JQUERY`. While `DATE_FORMAT_JQUERY` is fed to `datepicker`, `DATE_FORMAT` is still used for presenting initial date values in filters, so make sure that `DATE_FORMAT_JQUERY` and `DATE_FORMAT` result in an identical date representation. Constant `DATEPICKER_YEAR_RANGE` defines the range of years in the Datepicker year dropdown. Alternatively, you can always change this range dynamically with the following javascript: ```js $( ".hasDatepicker" ).datepicker( "option", "yearRange", "2000:2042" ); ``` #### jQuery UI DatePicker `(HELPER_STYLE = :bootstrap)` WiceGrid also supports [Bootstrap Datepicker](https://github.com/Nerian/bootstrap-datepicker-rails). #### Rails standard input fields `(HELPER_STYLE = :standard)` Another option is standard Rails helpers for date fields, these are separate select fields for years, months and days (also for hour and minute if it is a datetime field). ### Detached Filters Filters can also be detached from the grid table and placed anywhere on page. This is a 3-step process. First, define the grid with helper `define_grid` instead of `grid`. Everything should be done the same way as with `grid`, but every column which will have an external filter, add `detach_with_id: :some_filter_name`` in the column definition. The value of `:detach_with_id` is an arbitrary string or a symbol value which will be used later to identify the filter. ```erb <%= define_grid(@tasks_grid, show_filters: :always) do |g| g.column name: 'Title', attribute: 'title', detach_with_id: :title_filter do |task| link_to('Edit', edit_task_path(task.title)) end g.column name: 'Archived', attribute: 'archived', detach_with_id: :archived_filter do |task| task.archived? ? 'Yes' : 'No' end g.column name: 'Added', attribute: 'created_at', detach_with_id: :created_at_filter do |task| task.created_at.to_fs(:short) end end -%> ``` Then, use `grid_filter(grid, :some_filter_name)` to render filters: ```erb <% # rendering filter with key :title_filter %> <%= grid_filter @tasks_grid, :title_filter %> <% # rendering filter with key :archived_filter %> <%= grid_filter @tasks_grid, :archived_filter %> <% # rendering filter with key :created_at_filter %> <%= grid_filter @tasks_grid, :created_at_filter %> <% # Rendering the grid body %> <%= grid(@tasks_grid) %> ``` Finally, use `render_grid(@grid)` to actually output the grid table. Using custom submit and reset buttons together with `hide_submit_button: true` and `hide_reset_button: true` allows to completely get rid of the default filter row and the default icons (see section 'Submit/Reset Buttons'). If a column was declared with `:detach_with_id`, but never output with `grid_filter`, filtering the grid in development mode will result in an warning javascript message and the missing filter will be ignored. There is no such message in production. ### Defining your own external filter processors It possible to define and use your own column processors outside of the plugin, in you application. The first step is to edit `Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS` in `wice_grid_config.rb`: ```ruby Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS = { my_own_filter: ['ViewColumnMyOwnFilter', 'ConditionsGeneratorMyOwnFilter'], another_filter: ['ViewColumnAnotherFilter', 'ConditionsGeneratorAnotherFilter'] } ``` The first element in the two-item array is the name of a class responsible for rendering the filter view. The second element is the name of a class responsible for processing filter parameters. For examples of these two classes look at the existing column processors in `lib/wice/columns/` The structure of these two classes is as follows: ```ruby class ViewColumnMyOwnFilter < Wice::Columns::ViewColumn def render_filter_internal(params) ... end def yield_declaration_of_column_filter { templates: [...], ids: [...] } end end class ConditionsGeneratorMyOwnFilter < Wice::Columns::ConditionsGeneratorColumn def generate_conditions(table_name, opts) ... end end ``` To use an external column processor use `:filter_type` in a column definition: ```ruby column name: 'name', attribute: 'attribute', filter_type: :my_own_filter do |rec| ... end ``` ## Defaults Default values like can be changed in `config/initializers/wice_grid_config.rb`. ## Submit/Reset buttons Instead of using default Submit and Reset icons you can use external HTML elements to trigger these actions. Add a button or any other clickable HTML element with class `wg-external-submit-button` or `wg-external-reset-button`, and attribute `data-grid-name` whose value is the name of the grid: ```html ``` To hide the default icons use `hide_submit_button: true` and `hide_reset_button: true` in the `grid` helper. ## Auto-reloading filters It is possible to configure a grid to reload itself once a filter has been changed. It works with all filter types including the JS calendar, the only exception is the standard Rails date/datetime filters. Use option `:auto_reload` in the column definiton: ```erb <%= grid(@tasks_grid, show_filters: :always, hide_submit_button: true) do |g| # String g.column name: 'Title', attribute: 'title', auto_reload: true # Boolean g.column name: 'Archived', attribute: 'archived', auto_reload: true # Custom (dropdown) g.column name: 'Status', attribute: 'status_id', custom_filter: Status.to_dropdown, auto_reload: true do |task| task.status.name if task.status end # Datetime g.column name: 'Added', attribute: 'created_at', auto_reload: true, helper_style: :calendar do |task| task.created_at.to_fs(:short) end end -%> ``` To make this behavior default change constant `AUTO_RELOAD` in the configuration file. ## Styling the grid ### Adding classes and styles The `grid` helper accepts parameter `:html` which is a hash of HTML attributes for the table tag. Another `grid` parameter is `header_tr_html` which is a hash of HTML attributes to be added to the first `tr` tag (or two first `tr`'s if the filter row is present). `:html` is a parameter for the `column` method setting HTML attributes of `td` tags for a certain column. ### Adding classes and styles dynamically WiceGrid offers ways to dynamically add classes and styles to `TR` and `TD` based on the current ActiveRecord instance. For ``, let the `column` return an array where the first item is the usual string output whole the second is a hash of HTML attributes to be added for the `` tag of the current cell: ```ruby g.column do |portal_application| css_class = portal_application.public? ? 'public' : 'private' [portal_application.name, {class: css_class}] end ``` For adding classes/styles to `` use special clause `row_attributes` , similar to `column`, only returning a hash: ```erb <%= grid(@versions_grid) do |g| g.row_attributes do |version| if version.in_production? {style: 'background-color: rgb(255, 255, 204);'} end end g.column{|version| ... } g.column{|version| ... } end -%> ``` Naturally, there can be only one `row_attributes` definition for a WiceGrid instance. Various classes do not overwrite each other, instead, they are concatenated. ## Adding rows to the grid It is possible to add your own handcrafted HTML after and/or before each grid row. This works similar to `row_attributes`, by adding blocks `after_row`, `before_row`, and `last_row`: ```erb <%= grid(@tasks_grid) do |g| g.before_row do |task, number_of_columns| if task.active? "Custom line for #{t.name}" # this would add a row # before every active task row else nil end end g.last_row do |number_of_columns| # This row will always be added to the bottom of the grid content_tag(:tr, content_tag(:td, 'Last row', colspan: 10), class: 'last_row') end ....... end %> ``` It is up for the developer to return the correct HTML code, or return `nil` if no row is needed for this record. Naturally, there is only one `before_row` definition and one `after_row` definition for a WiceGrid instance. The second variable injected into to `before_row` and `after_row` block, and the first parameter injected into the `last_row` is the number of columns in the current grid. ## Rendering a grid without records If the grid does not contain any records to show, it is possible show some alternative view instead of an empty grid. Bear in mind that if the user sets up the filters in such a way that the selection of records is empty, this will still render the grid and it will be possible to reset the grid clicking on the Reset button. Thus, this only works if the initial number of records is 0. ```erb <%= grid(@grid) do |g| g.blank_slate do "There are no records" end g.column do |product| ... end end -%> ``` There are two alternative ways to do the same, submitting a string to `blank_slate`: ```ruby g.blank_slate "some text to be rendered" ``` Or a partial: ```ruby g.blank_slate partial: "partial_name" ``` ## Action Column It is possible to add a column with checkboxes for each record. This is useful for actions with multiple records, for example, deleting selected records. Please note that `action_column` only creates the checkboxes and the 'Select All' and 'Deselect All' buttons, and the form itself as well as processing the parameters should be taken care of by the application code. ```erb <%= grid(@tasks_grid, show_filters: :always) do |g| ... g.action_column ... end -%> ``` By default the name of the HTTP parameter follows pattern `"#{grid_name}[#{param_name}][]"`, thus `params[grid_name][param_name]` will contain an array of object IDs. You can hide a certain action checkbox if you add the usual block to `g.action_column`, just like with the `g.column` definition. If the block returns `nil` or `false` no checkbox will be rendered. ```erb <%= grid(@tasks_grid, show_filters: :always) do |g| ... g.action_column do |task| task.finished? end ... end -%> ``` WiceGrid is form-friendly: submitting grid in a form retains the state of the form. ## Integration of the grid with other forms on page Imagine that the user should be able to change the behavior of the grid using some other control on the page, and not a grid filter. For example, on a page showing tasks, change between 'Show active tasks' to 'Show archived tasks' using a dropdown box. WiceGrid allows to keep the status of the grid with all the filtering and sorting using helper `dump_filter_parameters_as_hidden_fields` which takes a grid object and dumps all current sorting and filtering parameters as hidden fields. Just include `dump_filter_parameters_as_hidden_fields(@grid)` inside your form, and the newly rendered grid will keep ordering and filtering. ```erb <% form_tag('', method: :get) do %> <%= dump_filter_parameters_as_hidden_fields(@tasks_grid) %> <%= select_tag 'archived', options_for_select([['View active tasks', 0], ['View archived tasks', 1]], @archived ? 1 : 0), onchange: 'this.form.submit()' %> <% end -%> ``` ## Show All Records It is possible to switch to the All Records mode clicking on link "show all" in the bottom right corner. This functionality should be used with care. To turn this mode off for all grid instances, change constant `ALLOW_SHOWING_ALL_RECORDS` in `config/initializers/wice_grid_config.rb` to `false`. To do so for a specific grid, use initializer parameter `:allow_showing_all_records`. Configuration constant `START_SHOWING_WARNING_FROM` sets the threshold number of all records after which clicking on the link results in a javascript confirmation dialog. ## CSV Export It is possible to export the data displayed on a grid to a CSV file. The dumped data is the current resultset with all the current filters and sorting applied, only without the pagination constraint (i.e. all pages). To enable CSV export add parameters `enable_export_to_csv` and `csv_file_name` to the initialization of the grid: ```ruby @projects_grid = initialize_grid(Project, include: [:customer, :supplier], name: 'g2', enable_export_to_csv: true, csv_file_name: 'projects' ) ``` `csv_file_name` is the name of the downloaded file. This parameter is optional, if it is missing, the name of the grid is used instead. The export icon will appear at the bottom right corner of the grid. If the program you are importing the generated CSV into has problem processing UTF-8, you can change the character encoding using the `csv_encoding` option. P.e. setting `csv_encoding: 'CP1252:UTF-8'` will make older versions of Excel happy. The format used is `:`. Next, each grid view helper should be placed in a partial of its own, requiring it from the master template for the usual flow. There must be no HTML or ERB code in this partial except for the grid helper. By convention the name of such a partial follows the following pattern: ``` _GRID_NAME_grid.html.erb ``` In other words, a grid named `tasks` is expected to be found in a template called `_tasks_grid.html.erb` (remember that the default name of grids is '`grid`'.) Next, method `export_grid_if_requested` should be added to the end of each action containing grids with enabled CSV export. `export_grid_if_requested` intercepts CSV export requests and evaluates the partial with the required grid helper. The naming convention for grid partials can be easily overridden by supplying a hash parameter to `export_grid_if_requested` where each key is the name of a grid, and the value is the name of the template (like it is specified for `render`, i.e. without '_' and extensions): ```ruby export_grid_if_requested('g1' => 'tasks_grid', 'g2' => 'projects_grid') ``` If the request is not a CSV export request, `export_grid_if_requested` does nothing and returns `false`, if it is a CSV export request, the method returns `true`. If the action has no explicit `render` call, it's OK to just place `export_grid_if_requested` as the last line of the action: ```ruby def index @tasks_grid = initialize_grid(Task, name: 'g1', enable_export_to_csv: true, csv_file_name: 'tasks' ) @projects_grid = initialize_grid(Project, name: 'g2', enable_export_to_csv: true, csv_file_name: 'projects' ) export_grid_if_requested end ``` Otherwise, to avoid double rendering, use the return value of the method to conditionally call your `render` : ```ruby def index ........... export_grid_if_requested || render(action: 'my_template') end ``` It's also possible to supply a block which will be called if no CSV export is requested: ```ruby def index ........... export_grid_if_requested do render(action: 'my_template') end end ``` If a column has to be excluded from the CSV export, set `column` parameter `in_csv` to `false`: ```ruby g.column in_csv: false do |task| link_to('Edit', edit_task_path(task)) end ``` If a column must appear both in HTML and CSV, but with different output, duplicate the column and use parameters `in_csv` and `in_html` to include one of them to html output only, the other to CSV only: ```ruby # html version g.column name: 'Title', attribute: 'title', in_csv: false do |task| link_to('Edit', edit_task_path(task.title)) end # plain text version g.column name: 'Title', in_html: false do |task| task.title end ``` The default field separator in generated CSV is a comma, but it's possible to override it: ```ruby @products_grid = initialize_grid(Product, enable_export_to_csv: true, csv_field_separator: ';', csv_file_name: 'products' ) ``` If you need an external CSV export button , add class `wg-external-csv-export-button` to any clickable element on page and set its attribute `data-grid-name` to the name of the grid: ```html ``` If you need to disable the default export icon in the grid, add `hide_csv_button: true` to the `grid` helper. ## Access to Records From Outside The Grid There are two ways you can access the records outside the grid - using methods of the WiceGrid object and using callbacks. ### Accessing Records Via The WiceGrid Object Method `current_page_records` returns exactly the same list of objects displayed on page: ```erb <%= grid(@tasks_grid) do |g| ... end -%>

IDs of records on the current page: <%= @tasks_grid.current_page_records.map(&:id).to_sentence %>

``` Method `all_pages_records` returns a list of objects browsable through all pages with the current filters: ```erb <%= grid(@tasks_grid) do |g| ... end -%>

IDs of all records: <%= @tasks_grid.all_pages_records.map(&:id).to_sentence %>

``` Mind that this helper results in an additional SQL query. Because of the current implementation of WiceGrid these helpers work only after the declaration of the grid in the view. This is due to the lazy nature of WiceGrid - the actual call to the database is made during the execution of the `grid` helper, because to build the correct query columns declarations are required. ### Accessing Records Via Callbacks It is possible to set up callbacks which are executed from within the plugin just after the call to the database. The callbacks are called before rendering the grid cells, so the results of this processing can be used in the grid. There are 3 ways you can set up such callbacks: Via a lambda object: ```ruby def index @tasks_grid = initialize_grid(Task, with_paginated_resultset: ->(records){ ... } ) end ``` Via a symbol which is the name of a controller method: ```ruby def index @tasks_grid = initialize_grid(Task, with_paginated_resultset: :process_selection ) end def process_selection(records) ... end ``` Via a separate block: ```ruby def index @tasks_grid = initialize_grid(Task) @tasks_grid.with_paginated_resultset do |records| ... end end ``` There are two callbacks: * `:with_paginated_resultset` - used to process records of the current page * `:with_resultset` - used to process all records browsable through all pages with the current filters While the `:with_paginated_resultset` callback just receives the list of records, `:with_resultset` receives an ActiveRelation object which can be used to obtain the list of all records: ```ruby def index @tasks_grid = initialize_grid(Task) @tasks_grid.with_resultset do |active_relation| all_records = active_relation.all ... end end ``` This lazy nature exists for performance reasons. Reading all records leads to an additional call, and there can be cases when processing all records should be triggered only under certain circumstances: ```ruby def index @tasks_grid = initialize_grid(Task) @tasks_grid.with_resultset do |active_relation| if params[:process_all_records] all_records = active_relation.all ... end end end ``` ## Testing To run tests: 1. `git clone https://github.com/leikind/wice_grid.git` 2. `cd wice_grid` 3. `bundle` 4. Install phantomjs (e.g. `brew install phantomjs` or `apt-get install phantomjs` or something else) 5. `bundle exec appraisal rspec` Tests against Rails 5.0, 5.1 & 5.2. To test against a specific version, for example Rails 5.2 run `bundle exec appraisal rails-5.2 rspec` This repository contains a Rails application for testing purposes. To fire up this application manually, run `cd spec/support/test_app/bin; RAILS_ENV=test rails s`. ## Bug reports If you discover a problem with Wicegrid, we would love to know about it. Please use the [GitHub issue tracker](https://github.com/leikind/wice_grid/issues) ================================================ FILE: Rakefile ================================================ require 'rubygems' require 'bundler' require 'bundler/gem_tasks' begin Bundler.setup(:default, :development) rescue Bundler::BundlerError => e $stderr.puts e.message $stderr.puts 'Run `bundle install` to install missing gems' exit e.status_code end require 'rake' require 'rspec/core' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) desc 'Run RSpec with code coverage' task :coverage do ENV['COVERAGE'] = 'true' Rake::Task['spec'].execute end # Experimenting with documentation system we will keep both Yardoc and Rdoc for some time, plus Inch require 'yard' require 'yard/rake/yardoc_task' desc 'Generate YARDOC documentation for the plugin' YARD::Rake::YardocTask.new(:yardoc) do |t| OTHER_PATHS = %w() t.files = ['lib/**/*.rb', OTHER_PATHS] t.options = %w(--main=README.md --file TODO.md,CHANGELOG.md,SAVED_QUERIES_HOWTO.md,MIT-LICENSE) t.stats_options = ['--list-undoc'] end gem 'rdoc' require 'rdoc/task' Rake::RDocTask.new(:rdoc) do |rdoc| rdoc.rdoc_dir = 'rdoc' rdoc.title = 'WiceGrid' rdoc.options << '--line-numbers' << '--inline-source' rdoc.rdoc_files.include('README.md') rdoc.rdoc_files.include('SAVED_QUERIES_HOWTO.md') rdoc.rdoc_files.include('CHANGELOG.md') rdoc.rdoc_files.include('lib/**/*.rb') end task default: [:spec, :yardoc] ================================================ FILE: SAVED_QUERIES_HOWTO.md ================================================ ## Saving Queries How-To WiceGrid allows to save the state of filters as a custom query and later restore it from the list of saved queries. ### Step 1: Create the database table to store queries To get started create the database table to store queries. Run the following generator: ``` rails g wice_grid:add_migration_for_serialized_queries ``` This add a migration file with the definition of the table. Run the migrate task: ``` bundle 'rake db:migrate' ``` ### Step 2: Create the controller to handle AJAX queries. Creation and deletion of queries is implemented as AJAX calls, and a controller is needed to handle these calls. Create an empty controller with any name and add method +save_wice_grid_queries+ to it: ``` class QueriesController < ApplicationController save_wice_grid_queries end ``` This is it. The controller now has the required action methods. ### Step 3: Add routes If the name of the query controller is QueriesController, add the following to `routes.rb`: ``` Wice::define_routes(self, 'queries') ``` ### Step 4: Add the saved query panel to the view. To show the list of saved queries and the form to create new queries in a view, add the following helper to the view: ``` <%= saved_queries_panel(@grid_object) %> ``` Voila! Just like WiceGrid itself, the query panel contains no forms and is thus form-friendly. *Important*: Saved queries of all grids in the application are stored in one table and differentiated by the name of the grid, thus, for all forms with saved queries it is important to define different names! (use parameter `:name` in +initialize_grid+) It is also possible to initialize a grid with an initial saved query providing the id of the query record or the ActiveRecord itself to parameter `saved_query`: ``` @products_grid = initialize_grid(Product, name: 'prod_grid', saved_query: SavedQuery.find_by_id_and_grid_name(12, 'prod_grid') ) ``` ## Adding Application Specific Logic to Saving/Restoring Queries WiceGrid allows to add application specific logic to saving and restoring queries by substituting the default ActiveRecord model provided by WiceGrid with a custom model. Copy `lib/wice_grid_serialized_query.rb` from the gem to `app/models/`, rename the file and the class to your liking. After renaming the model to SavedQuery it looks like this: ``` class SavedQuery < ActiveRecord::Base #:nodoc: serialize :query validates_uniqueness_of :name, scope: :grid_name, on: :create, message: 'A query with this name already exists' validates_presence_of :name, message: 'Please submit the name of the custom query' def self.list(name, controller) conditions = {grid_name: name} self.find(:all, conditions: conditions) end end ``` It is required that the model provides class method +list+ which accepts two parameters: the name of the WiceGrid instance and the controller object, and returns a list of queries. The controller object is needed to provide the application context. For instance, if it is needed to store queries for each user, we could add +user_id+ to the table and modify the code so it looks like the following: ``` class SavedQuery < ActiveRecord::Base serialize :query validates_uniqueness_of :name, scope: :grid_name, on: :create, message: 'A query with this name already exists' validates_presence_of :name, message: 'Please submit the name of the custom query' belongs_to :identity # ! def self.list(name, controller) conditions = {grid_name: name} if controller.current_user # ! conditions[:user_id] = controller.current_user.id # provided that method current_user is defined in ApplicationController and returns the currrent user. end self.find(:all, conditions: conditions) end end ``` The following step is to make sure that a new query is saved with the correct +user_id+. To do so, change the helper `saved_queries_panel(@grid_object)` to the following: ``` <%= saved_queries_panel(@identities_grid, extra_parameters: {user_id: @current_user.id}) %> ``` For every key in has :extra_parameters there must exist a field in the model - this hash will be used as a parameter to `attributes=` method of the query object. Finally, let WiceGrid know which model to use for saving queries by changing constant +QUERY_STORE_MODEL+ in `lib/wice_grid_config.rb` to the name of the custom model (as a string), in the above example this would look like the following: ``` QUERY_STORE_MODEL = 'SavedQuery' ``` ================================================ FILE: app/views/kaminari/wice_grid/_gap.html.erb ================================================
  • ...
  • ================================================ FILE: app/views/kaminari/wice_grid/_next_page.html.erb ================================================
  • <%= link_to_unless current_page.last?, ::Wice::NlMessage['next_label'], url, rel: 'next', remote: remote %>
  • ================================================ FILE: app/views/kaminari/wice_grid/_page.html.erb ================================================
  • class="active"<%end%>><%= link_to page, url, {remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil} %>
  • ================================================ FILE: app/views/kaminari/wice_grid/_paginator.html.erb ================================================ <%= paginator.render do %> <% end %> ================================================ FILE: app/views/kaminari/wice_grid/_prev_page.html.erb ================================================
  • <%= link_to_unless current_page.first?, ::Wice::NlMessage['previous_label'], url, rel: 'prev', remote: remote %>
  • ================================================ FILE: config/locales/cz.yml ================================================ cz: wice_grid: show_filter_tooltip: Zobrazit filtr hide_filter_tooltip: Skrýt filtr csv_export_tooltip: Exportovat do CSV filter_tooltip: Filtr reset_filter_tooltip: Zrušit filtr boolean_filter_true_label: "áno" boolean_filter_false_label: "ne" previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: Dátum od # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: Dátum do # The title of the checkox to turn on negation negation_checkbox_title: Inverzní výběr # link to switch to show all records show_all_records_label: Zobrazit všechno # tooltip for the link to switch to show all records show_all_records_tooltip: Zobrazí všechny záznamy v databáze # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Určitě chceš zobrazit všechny záznamy v databáze? # link to paginated view switch_back_to_paginated_mode_label: zpět na stránkové zobrazení # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Přepnout do stránkového zobrazení # Title of the date string. date_string_tooltip: Kliknutím zmažeš saved_query_panel_title: Uložené filtry save_query_button_label: Uložit aktuálni stav filtrů saved_query_deletion_confirmation: Seš si jistej? saved_query_deletion_link_title: Smazat uloženej filtr saved_query_link_title: Použít uloženej filtr validates_uniqueness_error: Filtr už existuje. validates_presence_error: Prosím zadej název filtru. query_deleted_message: Uložený filtr byl smazán. query_saved_message: Filtr byl uložen. select_all: Vyber všechno deselect_all: Zrušit výběr expand: Rozšířit collapse: Kolaps ================================================ FILE: config/locales/de.yml ================================================ de: wice_grid: show_filter_tooltip: Filter einblenden hide_filter_tooltip: Filter ausblenden csv_export_tooltip: CSV Export filter_tooltip: Filter reset_filter_tooltip: Reset boolean_filter_true_label: "Ja" boolean_filter_false_label: "Nein" previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: Von # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: Bis # The title of the checkox to turn on negation negation_checkbox_title: Ausschließen # link to switch to show all records show_all_records_label: Alle anzeigen # tooltip for the link to switch to show all records show_all_records_tooltip: Alle anzeigen # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Wollen Sie wirkliche alle Einträge anzeigen? # link to paginated view switch_back_to_paginated_mode_label: Zurück zur seitenbasierten Ansicht # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Zurück zur seitenbasierten Ansicht # Title of the date string. date_string_tooltip: Löschen saved_query_panel_title: Gespeicherte Abfragen save_query_button_label: Filter speichern saved_query_deletion_confirmation: Sind Sie sicher? saved_query_deletion_link_title: Abfrage löschen saved_query_link_title: Abfrage laden validates_uniqueness_error: Eine Abfrage mit dieser Bezeichnung existiert bereits! validates_presence_error: Bitte vergeben Sie einen Namen! query_deleted_message: Abfrage gelöscht. query_saved_message: Abfrage gespeichert. select_all: Alle auswählen deselect_all: Auswahl aufheben expand: Ausklappen collapse: Einklappen ================================================ FILE: config/locales/en.yml ================================================ en: wice_grid: show_filter_tooltip: Show filter hide_filter_tooltip: Hide filter csv_export_tooltip: Export to CSV filter_tooltip: Filter reset_filter_tooltip: Reset boolean_filter_true_label: "yes" boolean_filter_false_label: "no" previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: From # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: To # The title of the checkox to turn on negation negation_checkbox_title: Exclude # link to switch to show all records show_all_records_label: show all # tooltip for the link to switch to show all records show_all_records_tooltip: Show all records # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Are you sure you want to display all records? # link to paginated view switch_back_to_paginated_mode_label: back to paginated view # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Switch back to the view with pages # Title of the date string. date_string_tooltip: Click to delete saved_query_panel_title: Saved Queries save_query_button_label: Save the state of filters saved_query_deletion_confirmation: Are you sure? saved_query_deletion_link_title: Delete query saved_query_link_title: Load query validates_uniqueness_error: A query with this name already exists validates_presence_error: Please submit the name of the custom query query_deleted_message: Saved query deleted. query_saved_message: Query saved. select_all: Select all deselect_all: Remove selection expand: Expand collapse: Collapse ================================================ FILE: config/locales/es.yml ================================================ es: wice_grid: show_filter_tooltip: Mostrar filtro hide_filter_tooltip: Ocultar filtro csv_export_tooltip: Exportar a CSV filter_tooltip: Filtro reset_filter_tooltip: Reset boolean_filter_true_label: "Si" boolean_filter_false_label: "No" previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: Desde # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: Hasta # The title of the checkox to turn on negation negation_checkbox_title: Excluir # link to switch to show all records show_all_records_label: mostrar todos # tooltip for the link to switch to show all records show_all_records_tooltip: Mostrar todos los registros # Warning message shown when the user wants to switch to all-records mode all_queries_warning: ¿Estás seguro que quieres mostrar todos los registros? # link to paginated view switch_back_to_paginated_mode_label: volver a la vista paginada # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Cambiar atrás a la vista con páginas # Title of the date string. date_string_tooltip: Click en borrar saved_query_panel_title: Consultas salvadas save_query_button_label: Salvar el estado de los filtros saved_query_deletion_confirmation: ¿Estás seguro? saved_query_deletion_link_title: Borrar consulta saved_query_link_title: Cargar consulta validates_uniqueness_error: Una consulta con este nombre ya existe validates_presence_error: Por favor tramita el nombre la consulta modificada query_deleted_message: Salvada la consulta borrado. query_saved_message: Consulta salvada. select_all: Selecciona todo deselect_all: Borra la selección expand: Expandir collapse: Contraer ================================================ FILE: config/locales/fr.yml ================================================ fr: wice_grid: show_filter_tooltip: Afficher le filtre hide_filter_tooltip: Cacher le filtrer csv_export_tooltip: Exporter en CSV filter_tooltip: Filtrer reset_filter_tooltip: Effacer boolean_filter_true_label: oui boolean_filter_false_label: non previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: De # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: à # The title of the checkox to turn on negation negation_checkbox_title: Exclure # link to switch to show all records show_all_records_label: Voir tous # tooltip for the link to switch to show all records show_all_records_tooltip: Voir tous les enregistrements # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Etes-vous certain de vouloir afficher tous les enregistrements? # link to paginated view switch_back_to_paginated_mode_label: Retour à la vue paginée # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Retour à la vue par pages # Title of the date string. date_string_tooltip: Cliquez pour effacer saved_query_panel_title: Requêtes sauvées save_query_button_label: Sauver l'état des filtres saved_query_deletion_confirmation: Etes vous sûr? saved_query_deletion_link_title: Effacer la requête saved_query_link_title: Charger la requête validates_uniqueness_error: Un requête existante porte déjà ce nom validates_presence_error: Veuillez indiquer le nom de la requête que vous souhaitez sauver query_deleted_message: La requête a été effacée. query_saved_message: La requête a été sauvée. select_all: Sélectionner tout deselect_all: Désélectionner tout expand: Étendre collapse: Écrouler ================================================ FILE: config/locales/is.yml ================================================ is: wice_grid: show_filter_tooltip: Sýna síumöguleika hide_filter_tooltip: Fela síumoguleika csv_export_tooltip: Flytja út CSV filter_tooltip: Sía reset_filter_tooltip: Hreinsa boolean_filter_true_label: Já boolean_filter_false_label: Nei previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: Frá # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: Til # The title of the checkox to turn on negation negation_checkbox_title: Undanskilja # link to switch to show all records show_all_records_label: Sýna allt # tooltip for the link to switch to show all records show_all_records_tooltip: Sýna öll gögn # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Ertu viss um að þú viljir láta sýna öll gögn? # link to paginated view switch_back_to_paginated_mode_label: Aftur til síðuhorfs # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Aftur til síðuhorfs # Title of the date string. date_string_tooltip: Smella á til að eyða saved_query_panel_title: Vistaðar leitarskipanir save_query_button_label: Vista síuval saved_query_deletion_confirmation: Ertu viss? saved_query_deletion_link_title: Eyða leitarskipun saved_query_link_title: Hlaða leitarskipun validates_uniqueness_error: Leitarskipun með þessu nafni er þegar til validates_presence_error: Vinsamlegast gefið heiti fyrir leitarskipun query_deleted_message: Vistaðri leitarskipun hefur verið eytt query_saved_message: leitaskipun hefur verið vistuð select_all: Velja allt deselect_all: Fjarlægðu val expand: Stækka collapse: Hrun ================================================ FILE: config/locales/it.yml ================================================ it: wice_grid: show_filter_tooltip: Mostra filtri hide_filter_tooltip: Nascondi filtri csv_export_tooltip: Esporta in CSV filter_tooltip: Filtri reset_filter_tooltip: Cancella boolean_filter_true_label: "si" boolean_filter_false_label: "no" previous_label: « next_label: » date_selector_tooltip_from: Da date_selector_tooltip_to: A negation_checkbox_title: Escludi show_all_records_label: Tutti i risultati show_all_records_tooltip: Mostra tutti i risultati all_queries_warning: Sei sicuro di voler mostrare tutti i risultati? switch_back_to_paginated_mode_label: torna alla vista paginata switch_back_to_paginated_mode_tooltip: Per tornare alla paginazione date_string_tooltip: Clicca per cancellare saved_query_panel_title: Ricerche salvate save_query_button_label: Salva il valore dei filtri saved_query_deletion_confirmation: Sei sicuro? saved_query_deletion_link_title: Cancella la ricerca saved_query_link_title: Carica ricerca validates_uniqueness_error: Una ricerca con questo nome già esiste validates_presence_error: Per favore inserisci il nome per questa ricerca personalizzata query_deleted_message: Ricerca cancellata query_saved_message: Ricerca salvata select_all: Seleziona tutti deselect_all: Deseleziona tutti expand: Espandi collapse: Chiudi ================================================ FILE: config/locales/ja.yml ================================================ ja: wice_grid: show_filter_tooltip: フィルタ表示 hide_filter_tooltip: フィルタ非表示 csv_export_tooltip: CSVエクスポート filter_tooltip: フィルタ reset_filter_tooltip: リセット boolean_filter_true_label: "yes" boolean_filter_false_label: "no" previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: From # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: To # The title of the checkox to turn on negation negation_checkbox_title: 除外する # link to switch to show all records show_all_records_label: すべて表示 # tooltip for the link to switch to show all records show_all_records_tooltip: 前レコード表示 # Warning message shown when the user wants to switch to all-records mode all_queries_warning: 全てのレコードを表示しますか? # link to paginated view switch_back_to_paginated_mode_label: 戻る # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: 戻る # Title of the date string. date_string_tooltip: 削除 saved_query_panel_title: クエリ保存 save_query_button_label: フィルタ保存 saved_query_deletion_confirmation: 本当によろしいですか? saved_query_deletion_link_title: クエリ削除 saved_query_link_title: クエリのロード validates_uniqueness_error: 同じ名前がすでに存在します validates_presence_error: クエリ名を記入して下さい query_deleted_message: クエリの削除が完了しました query_saved_message: クエリを保存しました select_all: 全てを選択 deselect_all: 選択を解除 expand: 開く collapse: 閉じる ================================================ FILE: config/locales/nl.yml ================================================ nl: wice_grid: show_filter_tooltip: Filter tonen hide_filter_tooltip: Filter verbergen csv_export_tooltip: Als CSV exporteren filter_tooltip: Filter reset_filter_tooltip: Terugstellen boolean_filter_true_label: ja boolean_filter_false_label: neen previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: Vanaf # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: Tot # The title of the checkox to turn on negation negation_checkbox_title: Uitsluiten # link to switch to show all records show_all_records_label: Alle rijen tonen # tooltip for the link to switch to show all records show_all_records_tooltip: Alle rijen tonen # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Bent u zeker dat u alle rijen wilt tonen? # link to paginated view switch_back_to_paginated_mode_label: Terug naar pagina's # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Terug naar pagina's # Title of the date string. date_string_tooltip: Klik om te verwijderen saved_query_panel_title: Opgeslagen query's save_query_button_label: Query opslaan saved_query_deletion_confirmation: Bent u zeker? saved_query_deletion_link_title: Query verwijderen saved_query_link_title: Query laden validates_uniqueness_error: Deze query bestaat reeds validates_presence_error: Gelieve de naam van de query in te vullen query_deleted_message: Opgeslagen query verwijderd. query_saved_message: Query opgeslagen select_all: Selecteer alle rijen deselect_all: Deselecteer alle rijen expand: Uitklappen collapse: Inklappen ================================================ FILE: config/locales/pt-BR.yml ================================================ pt-BR: wice_grid: show_filter_tooltip: Mostrar o filtro hide_filter_tooltip: Esconder o filtro csv_export_tooltip: Exportar em CSV filter_tooltip: Filtrar reset_filter_tooltip: Reinicializar boolean_filter_true_label: sim boolean_filter_false_label: não previous_label: « next_label: » date_selector_tooltip_from: De date_selector_tooltip_to: a negation_checkbox_title: Excluir show_all_records_label: Ver todos show_all_records_tooltip: Ver todos os registos all_queries_warning: Tem certeza que gostaria de visualizar todos os registos? switch_back_to_paginated_mode_label: Voltar à lista paginada switch_back_to_paginated_mode_tooltip: Voltar à lista por página date_string_tooltip: Clique para apagar saved_query_panel_title: Filtros gravados save_query_button_label: Gravar o estado dos filtros saved_query_deletion_confirmation: Tem certeza? saved_query_deletion_link_title: Apagar o filtro saved_query_link_title: Carregar o filtro validates_uniqueness_error: Já existe um filtro com o mesmo nome validates_presence_error: Indique o nome do filtro que deseja gravar query_deleted_message: O filtro foi apagado. query_saved_message: O filtro foi gravado. select_all: Selecionar todos deselect_all: Remover seleção expand: Expandir collapse: Colapso ================================================ FILE: config/locales/pt.yml ================================================ pt: wice_grid: show_filter_tooltip: Mostrar o filtro hide_filter_tooltip: Esconder o filtro csv_export_tooltip: Exportar em CSV filter_tooltip: Filtrar reset_filter_tooltip: Reinicializar boolean_filter_true_label: sim boolean_filter_false_label: não previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: De # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: a # The title of the checkox to turn on negation negation_checkbox_title: Excluir # link to switch to show all records show_all_records_label: Ver todos # tooltip for the link to switch to show all records show_all_records_tooltip: Ver todos os registos # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Tem a certeza de querer visualizar todos os registos? # link to paginated view switch_back_to_paginated_mode_label: Retorno à vista paginada # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Retorno à vista por página # Title of the date string. date_string_tooltip: Cliquar para apagar saved_query_panel_title: Queries gravadas save_query_button_label: Gravar o estado dos filtros saved_query_deletion_confirmation: Tem a certeza? saved_query_deletion_link_title: Apagar a query saved_query_link_title: Caregar a query validates_uniqueness_error: Já existe uma query com o mesmo nome validates_presence_error: Queira indicar o nome da query que deseja gravar query_deleted_message: A query foi apagada. query_saved_message: A query foi gravada. select_all: Selecionar todos deselect_all: Remover seleção expand: Expandir collapse: Colapso ================================================ FILE: config/locales/ru.yml ================================================ ru: wice_grid: show_filter_tooltip: Показать фильтр hide_filter_tooltip: Спрятать фильтр csv_export_tooltip: Экспорт в CSV filter_tooltip: Фильтровать reset_filter_tooltip: Сброс boolean_filter_true_label: да boolean_filter_false_label: нет previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: С # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: До # The title of the checkox to turn on negation negation_checkbox_title: Исключая строки # link to switch to show all records show_all_records_label: показать все # tooltip for the link to switch to show all records show_all_records_tooltip: Показать все записи # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Вы уверены в том, что хотите отобразить все записи? # link to paginated view switch_back_to_paginated_mode_label: Назад к постраничному выводу # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Назад к постраничному выводу # Title of the date string. date_string_tooltip: Кликните, чтобы удалить дату saved_query_panel_title: Сохранённые фильтры save_query_button_label: Сохранить фильтр saved_query_deletion_confirmation: Вы уверены? saved_query_deletion_link_title: Удалить сохранённый фильтр saved_query_link_title: Загрузить сохранённый фильтр validates_uniqueness_error: Сохранённый фильтр с таким именем уже существует validates_presence_error: Пожалуйста введите имя сохраняемого фильтра query_deleted_message: Сохранённый фильтр удалён. query_saved_message: Сохранённый фильтр сохранён. select_all: Отметить все deselect_all: Убрать выделение expand: Развернуть collapse: Свернуть ================================================ FILE: config/locales/sk.yml ================================================ sk: wice_grid: show_filter_tooltip: Zobraziť filter hide_filter_tooltip: Skryť filter csv_export_tooltip: Exportovať do CSV filter_tooltip: Filter reset_filter_tooltip: Zrušiť filtre boolean_filter_true_label: "áno" boolean_filter_false_label: "nie" previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: Dátum od # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: Dátum do # The title of the checkox to turn on negation negation_checkbox_title: Inverzný výber # link to switch to show all records show_all_records_label: Zobraziť všetko # tooltip for the link to switch to show all records show_all_records_tooltip: Zobrazí všetky záznamy v databáze # Warning message shown when the user wants to switch to all-records mode all_queries_warning: Určite chceš zobraziť všetky záznamy v databáze? # link to paginated view switch_back_to_paginated_mode_label: späť na stránkové zobrazenie # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: Prepnúť do stránkového zobrazenia # Title of the date string. date_string_tooltip: Kliknutím zmažeš saved_query_panel_title: Uložené filtre/výsledky save_query_button_label: Uložiť stav filtrov saved_query_deletion_confirmation: Si si istý? saved_query_deletion_link_title: Vymazať uložený filter saved_query_link_title: Nahrať uložený filter validates_uniqueness_error: Filter s uvedeným názvom už existuje. validates_presence_error: Prosím zadaj názov uloženého filtra. query_deleted_message: Uložený filter bol vymazaný. query_saved_message: Filter bol uložený. select_all: Vyber všetko deselect_all: Zrušiť výber expand: Zväčšiť collapse: Kolaps ================================================ FILE: config/locales/uk.yml ================================================ uk: wice_grid: show_filter_tooltip: "Показати фільтр" hide_filter_tooltip: "Сховати фільтр" csv_export_tooltip: "Експорт в CSV" filter_tooltip: "Фільтрувати" reset_filter_tooltip: "Скинути фільтр" boolean_filter_true_label: "так" boolean_filter_false_label: "ні" previous_label: "«" next_label: "»" # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: "Від" # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: "До" # The title of the checkox to turn on negation negation_checkbox_title: "За винятком" # link to switch to show all records show_all_records_label: "показати всі" # tooltip for the link to switch to show all records show_all_records_tooltip: "Показати всі записи" # Warning message shown when the user wants to switch to all-records mode all_queries_warning: "Ви впевнені в тому, що хочете відобразити всі записи?" # link to paginated view switch_back_to_paginated_mode_label: "Назад до посторінкового виводу" # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: "Назад до посторінкового виводу" # Title of the date string. date_string_tooltip: "Натисніть, щоб видалити дату" saved_query_panel_title: "Збережені фільтри" save_query_button_label: "Зберегти фільтр" saved_query_deletion_confirmation: "Ви впевнені?" saved_query_deletion_link_title: "Видалити збережуваний фільтр" saved_query_link_title: "Завантажити збережуваний фільтр" validates_uniqueness_error: "Збережуваний фільтр з такою назвою вже існує" validates_presence_error: "Будь ласка, введіть назву Збережуваного фільтра" query_deleted_message: "Збережуваний фільтр видалено." query_saved_message: "Збережуваний фільтр збережено." select_all: "Виділити все" deselect_all: "Забрати виділення" expand: Розгорнути collapse: Згорнути ================================================ FILE: config/locales/zh.yml ================================================ zh: wice_grid: show_filter_tooltip: 显示过滤 hide_filter_tooltip: 隐藏过滤 csv_export_tooltip: 输出CSV档 filter_tooltip: 过滤 reset_filter_tooltip: 清除过滤 boolean_filter_true_label: "是" boolean_filter_false_label: "否" previous_label: « next_label: » # Title of the icon clicking on which will show the calendar to set the FROM date. date_selector_tooltip_from: 从 # Title of the icon clicking on which will show the calendar to set the TO date. date_selector_tooltip_to: 至 # The title of the checkox to turn on negation negation_checkbox_title: 排除 # link to switch to show all records show_all_records_label: 显示全部 # tooltip for the link to switch to show all records show_all_records_tooltip: 显示全部记录 # Warning message shown when the user wants to switch to all-records mode all_queries_warning: 确定要显示全部记录? # link to paginated view switch_back_to_paginated_mode_label: 回到分页显示 # tooltip for the link to paginated view switch_back_to_paginated_mode_tooltip: 切换到分页显示 # Title of the date string. date_string_tooltip: 按下以清除 saved_query_panel_title: 查询存储 save_query_button_label: 储存查询状态 saved_query_deletion_confirmation: 确定删除? saved_query_deletion_link_title: 删除查询 saved_query_link_title: 读取查询 validates_uniqueness_error: 已存在相同名称的查询 validates_presence_error: 请为此查询命名 query_deleted_message: 查询已删除 query_saved_message: 查询已储存 select_all: 全选 deselect_all: 全清 expand: 擴大 collapse: 坍方 ================================================ FILE: gemfiles/rails_5.0.gemfile ================================================ # This file was generated by Appraisal source "https://rubygems.org" gem "rails", "~> 5.0.0" gemspec path: "../" ================================================ FILE: gemfiles/rails_5.1.gemfile ================================================ # This file was generated by Appraisal source "https://rubygems.org" gem "rails", "~> 5.1.0" gemspec path: "../" ================================================ FILE: gemfiles/rails_5.2.gemfile ================================================ # This file was generated by Appraisal source "https://rubygems.org" gem "rails", "~> 5.2.0" gemspec path: "../" ================================================ FILE: lib/generators/wice_grid/add_migration_for_serialized_queries_generator.rb ================================================ module WiceGrid #:nodoc: module Generators #:nodoc: class AddMigrationForSerializedQueriesGenerator < Rails::Generators::Base #:nodoc: include Rails::Generators::Migration desc 'Add a migration which creates a table for serialized queries' source_root File.expand_path('../templates', __FILE__) def self.next_migration_number(_path) Time.now.utc.strftime('%Y%m%d%H%M%S') end def create_model_file migration_template 'create_wice_grid_serialized_queries.rb', 'db/migrate/create_wice_grid_serialized_queries.rb' end end end end ================================================ FILE: lib/generators/wice_grid/install_generator.rb ================================================ module WiceGrid #:nodoc: module Generators #:nodoc: class InstallGenerator < Rails::Generators::Base #:nodoc: desc 'Copy WiceGrid wice_grid_config.rb to config/initializers' source_root File.expand_path('../templates', __FILE__) def copy_stuff #:nodoc: template 'wice_grid_config.rb', 'config/initializers/wice_grid_config.rb' end end end end ================================================ FILE: lib/generators/wice_grid/templates/create_wice_grid_serialized_queries.rb ================================================ class CreateWiceGridSerializedQueries < ::ActiveRecord::Migration #:nodoc: def change #:nodoc: create_table :wice_grid_serialized_queries do |t| t.column :name, :string t.column :grid_name, :string t.column :query, :text t.timestamps end add_index :wice_grid_serialized_queries, :grid_name add_index :wice_grid_serialized_queries, [:grid_name, :id] end end ================================================ FILE: lib/generators/wice_grid/templates/wice_grid_config.rb ================================================ if defined?(Wice::Defaults) # Default number of rows to show per page. Wice::Defaults::PER_PAGE = 20 # Default order direction Wice::Defaults::ORDER_DIRECTION = 'asc' # Default name for a grid. A grid name is the basis for a lot of # names including parameter names, DOM IDs, etc # The shorter the name is the shorter the request URI will be. Wice::Defaults::GRID_NAME = 'grid' # If REUSE_LAST_COLUMN_FOR_FILTER_ICONS is true and the last column doesn't have any filter and column name, it will be used # for filter related icons (filter icon, reset icon, show/hide icon), otherwise an additional table column is added. Wice::Defaults::REUSE_LAST_COLUMN_FOR_FILTER_ICONS = true # The label of the first option of a custom dropdown list meaning 'All items' Wice::Defaults::CUSTOM_FILTER_ALL_LABEL = '--' # A list of classes for the table tag of the grid Wice::Defaults::DEFAULT_TABLE_CLASSES = ['table', 'table-bordered', 'table-striped'] # Allow switching between a single and multiple selection modes in custom filters (dropdown boxes) Wice::Defaults::ALLOW_MULTIPLE_SELECTION = true # Show the upper pagination panel by default or not Wice::Defaults::SHOW_UPPER_PAGINATION_PANEL = false # Disabling CSV export by default Wice::Defaults::ENABLE_EXPORT_TO_CSV = false # Default CSV field separator Wice::Defaults::CSV_FIELD_SEPARATOR = ',' # Default CSV encoding (p.e. 'CP1252:UTF-8' to make Microsoft Excel(tm) happy) Wice::Defaults::CSV_ENCODING = nil # The strategy when to show the filter. # * :when_filtered - when the table is the result of filtering # * :always - show the filter always # * :no - never show the filter Wice::Defaults::SHOW_FILTER = :always # A boolean value specifying if a change in a filter triggers reloading of the grid. Wice::Defaults::AUTO_RELOAD = false # SQL operator used for matching strings in string filters. Wice::Defaults::STRING_MATCHING_OPERATOR = 'LIKE' # STRING_MATCHING_OPERATOR = 'ILIKE' # Use this for Postgresql case-insensitive matching. # Defining one string matching operator globally for the whole application turns is not enough # when you connect to two databases one of which is MySQL and the other is Postgresql. # If the key for an adapter is missing it will fall back to Wice::Defaults::STRING_MATCHING_OPERATOR. # # 'CI_LIKE' is a special value. Setting a value in STRING_MATCHING_OPERATORS to CI_LIKE will result in the following SQL: # # UPPER(table.field) LIKE UPPER(?)" Wice::Defaults::STRING_MATCHING_OPERATORS = { 'ActiveRecord::ConnectionAdapters::MysqlAdapter' => 'LIKE', 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter' => 'ILIKE' } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Advanced Filters # # Switch of the negation checkbox in all text filters Wice::Defaults::NEGATION_IN_STRING_FILTERS = false # Each WiceGrid filter column is defined in two classes, one used for rendering the filter, the other # for generating query conditions. All these columns are in lib/wice/columns/*.rb . # File lib/wice/columns/column_processor_index.rb lists all predefined processors. # In most cases a processor is chosen automatically based on the DB column type, # for example, integer columns # can have two of processors, the default one with one input field, and a processor called "range", # with 2 input fields. In this case it is possible to specify a processor in the column definition: # # g.column filter_type: :range # # It is also possible to define you own processors: # # Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS = { # some_key_identifying_new_column_type: ['AViewColumnProcessorClass', 'ConditionsGeneratorClass'], # another_key_identifying_new_column_type: ['AnotherViewColumnProcessorClass', 'AnotherConditionsGeneratorClass'] # } # # Column processor keys/names should not coincide with the existing keys/names (see lib/wice/columns/column_processor_index.rb) # the value is a 2-element array with 2 strings, the first should be a name of view processor class inherited from # Wice::Columns::ViewColumn, the second should be a name of conditions generator class inherited from # Wice::Columns::ConditionsGeneratorColumn . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Showing All Records # # Enable or disable showing all records (non-paginated table) Wice::Defaults::ALLOW_SHOWING_ALL_RECORDS = true # If number of all queries is more than this value, the user will be given a warning message Wice::Defaults::START_SHOWING_WARNING_FROM = 100 # Hide the "show all" link if the number of all records is more than... # Force-resets back to pagination starting from this value. # Set to nil to always show it Wice::Defaults::SHOW_ALL_ALLOWED_UP_TO = nil # # set to nil to skip the check Wice::Defaults::SWITCH_BACK_TO_PAGINATION_FROM = nil # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Saving Queries # # ActiveRecord model to store queries. Read the documentation for details # QUERY_STORE_MODEL = 'WiceGridSerializedQuery' Wice::Defaults::QUERY_STORE_MODEL = 'WiceGridSerializedQuery' # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Here go settings related to the date/datetime filters # # Default column filters # Possible values: # * :jquery_datepicker - Jquery datepicker (works for datetime, too) # * :bootstrap_datepicker - Bootstrap datepicker (works for datetime, too) # * :rails_date_helper - standard Rails date helper # * :rails_datetime_helper - standard Rails datetime helper Wice::Defaults::DEFAULT_FILTER_FOR_DATE = :jquery_datepicker Wice::Defaults::DEFAULT_FILTER_FOR_DATETIME = :jquery_datepicker # Format of the datetime displayed. # If you change the format, make sure to check if +DATETIME_PARSER+ can still parse this string. Wice::Defaults::DATETIME_FORMAT = '%Y-%m-%d %H:%M' # Format of the date displayed. # If you change the format, make sure to check if +DATE_PARSER+ can still parse this string. Wice::Defaults::DATE_FORMAT = '%Y-%m-%d' # Format of the date displayed in jQuery's Datepicker # If you change the format, make sure to check if +DATE_PARSER+ can still parse this string. Wice::Defaults::DATE_FORMAT_JQUERY = 'yy-mm-dd' # Format of the date displayed in Bootstrap's Datepicker # If you change the format, make sure to check if +DATE_PARSER+ can still parse this string. Wice::Defaults::DATE_FORMAT_BOOTSTRAP = 'yyyy-mm-dd' # With Calendar helpers enabled the parameter sent is the string displayed. This lambda will be given a date string in the # format defined by +DATETIME_FORMAT+ and must generate a DateTime object. # In many cases Time.zone.parse is enough, for instance, %Y-%m-%d. If you change the format, make sure to check this code # and modify it if needed. Wice::Defaults::DATETIME_PARSER = lambda do|datetime_string| if datetime_string.blank? nil elsif Time.zone Time.zone.parse(datetime_string) else Time.parse(datetime_string) end end # The range of years to display in jQuery Datepicker. # It can always be changed dynamically with the following javascript: # $( ".hasDatepicker" ).datepicker( "option", "yearRange", "2000:2042" ); Wice::Defaults::DATEPICKER_YEAR_RANGE = (from = Date.current.year - 10).to_s + ':' + (from + 15).to_s # With Calendar helpers enabled the parameter sent is the string displayed. This lambda will be given a date string in the # format defined by +DATETIME+ and must generate a Date object. # In many cases Date.parse is enough, for instance, %Y-%m-%d. If you change the format, make sure to check this code # and modify it if needed. Wice::Defaults::DATE_PARSER = lambda do|date_string| if date_string.blank? nil else begin Date.parse(date_string) rescue ArgumentError nil end end end # The name of the page method (should correspond to Kaminari.config.page_method_name) Wice::Defaults::PAGE_METHOD_NAME = :page # The name of the theme to use for the pagination with Kaminari Wice::Defaults::PAGINATION_THEME = :wice_grid # By default ActiveRecord calls are always executed inside Model.unscoped{}. # Setting USE_DEFAULT_SCOPE to true will use the default scope for all queries. Wice::Defaults::USE_DEFAULT_SCOPE = false end ================================================ FILE: lib/wice/active_record_column_wrapper.rb ================================================ module Wice # to be mixed in into ActiveRecord::ConnectionAdapters::Column module WiceGridExtentionToActiveRecordColumn #:nodoc: # reference to the ActiveRecord model class attr_accessor :model end class ActiveRecordColumnWrapper #:nodoc: def initialize(column, all_filter_params, main_table, table_alias, custom_filter_active, filter_type) #:nodoc: @column = column # nil | Symbol @filter_type = filter_type # Hash { String => String | Array[String]} @all_filter_params = all_filter_params # nil | Boolean @main_table = main_table # nil | String @table_alias = table_alias # nil | Array[String] | Array[Array[...]] | Array[Symbol] @custom_filter_active = custom_filter_active end def wg_initialize_request_parameters #:nodoc: @request_params = nil return if @all_filter_params.nil? # if the parameter does not specify the table name we only allow columns in the default table to use these parameters if @main_table && @request_params = @all_filter_params[@column.name] current_parameter_name = @column.name elsif @request_params = @all_filter_params[alias_or_table_name(@table_alias) + '.' + @column.name] current_parameter_name = alias_or_table_name(@table_alias) + '.' + @column.name end # Preprocess incoming parameters for datetime, if what's coming in is # a datetime (with custom_filter it can be anything else, and not # the datetime hash {fr: ..., to: ...}) if @request_params if (@column.type == :datetime || @column.type == :timestamp) && @request_params.is_a?(Hash) [:fr, :to].each do |sym| if @request_params[sym] if @request_params[sym].is_a?(String) @request_params[sym] = Wice::ConfigurationProvider.value_for(:DATETIME_PARSER).call(@request_params[sym]) elsif @request_params[sym].is_a?(Hash) @request_params[sym] = Wice::GridTools.params_2_datetime(@request_params[sym]) end end end end # Preprocess incoming parameters for date, if what's coming in is # a date (with custom_filter it can be anything else, and not # the date hash {fr: ..., to: ...}) if @column.type == :date && @request_params.is_a?(Hash) [:fr, :to].each do |sym| if @request_params[sym] if @request_params[sym].is_a?(String) @request_params[sym] = Wice::ConfigurationProvider.value_for(:DATE_PARSER).call(@request_params[sym]) elsif @request_params[sym].is_a?(Hash) @request_params[sym] = ::Wice::GridTools.params_2_date(@request_params[sym]) end end end end end [wg_generate_conditions, current_parameter_name] end def wg_generate_conditions #:nodoc: return nil if @request_params.nil? if @custom_filter_active custom_processor_klass = ::Wice::Columns.get_conditions_generator_column_processor(:custom) custom_processor = custom_processor_klass.new(self) return custom_processor.generate_conditions(@table_alias, @request_params) end column_type = @filter_type || @column.type.to_s.intern filter_type = case column_type when :date ConfigurationProvider.value_for(:DEFAULT_FILTER_FOR_DATE) when :datetime ConfigurationProvider.value_for(:DEFAULT_FILTER_FOR_DATETIME) when :timestamp ConfigurationProvider.value_for(:DEFAULT_FILTER_FOR_DATETIME) else column_type end processor_class = ::Wice::Columns.get_conditions_generator_column_processor(filter_type) if processor_class return processor_class.new(self, column_type).generate_conditions(@table_alias, @request_params) else Wice.log("No processor for database type #{column_type}!!!") nil end end def name #:nodoc: @column.name end def model #:nodoc: @column.model end def array? if @column.sql_type_metadata.respond_to? :array @column.sql_type_metadata.array else @column.sql_type_metadata.sql_type.index('[]') end rescue false end def alias_or_table_name(table_alias) #:nodoc: table_alias || @column.model.table_name end end end ================================================ FILE: lib/wice/columns/column_action.rb ================================================ module Wice module Columns #:nodoc: class ViewColumnAction < ViewColumn #:nodoc: def initialize(grid_obj, html, param_name, select_all_buttons, object_property, html_check_box, view, block = nil) #:nodoc: @view = view @html_check_box = html_check_box @select_all_buttons = select_all_buttons self.grid = grid_obj self.html = html Wice::WgHash.add_or_append_class_value!(self.html, 'sel') grid_name = self.grid.name @param_name = param_name @cell_rendering_block = lambda do |object, params| if block && !block.call(object) '' else selected = params[grid_name] && params[grid_name][param_name] && params[grid_name][param_name].index(object.send(object_property).to_s) check_box_tag("#{grid_name}[#{param_name}][]", object.send(object_property), selected, id: nil) end end end def in_html #:nodoc: true end def capable_of_hosting_filter_related_icons? #:nodoc: false end def name #:nodoc: return '' unless @select_all_buttons if @html_check_box check_box_tag :select_all, 1, false, class: 'wg-select-all' else content_tag(:div, content_tag(:i, '', class: 'fa-regular fa-square-check'), class: 'clickable select-all', title: NlMessage['select_all']) + ' ' + content_tag(:div, content_tag(:i, '', class: 'fa-regular fa-square'), class: 'clickable deselect-all', title: NlMessage['deselect_all']) end end end ConditionsGeneratorColumnAction = ConditionsGeneratorColumn #:nodoc: end end ================================================ FILE: lib/wice/columns/column_boolean.rb ================================================ module Wice module Columns #:nodoc: class ViewColumnBoolean < ViewColumnCustomDropdown #:nodoc: include ActionView::Helpers::FormOptionsHelper # Text for the true value attr_accessor :boolean_filter_true_label # Text for the false value attr_accessor :boolean_filter_false_label def render_filter_internal(params) #:nodoc: @custom_filter = { @filter_all_label => nil, @boolean_filter_true_label => 't', @boolean_filter_false_label => 'f' } @turn_off_select_toggling = true super(params) end end class ConditionsGeneratorColumnBoolean < ConditionsGeneratorColumn #:nodoc: def generate_conditions(table_alias, opts) #:nodoc: unless opts.is_a?(Array) && opts.size == 1 Wice.log "invalid parameters for the grid boolean filter - must be an one item array: #{opts.inspect}" return false end opts = opts[0] if opts == 'f' [" (#{@column_wrapper.alias_or_table_name(table_alias)}.#{@column_wrapper.name} = ? or #{@column_wrapper.alias_or_table_name(table_alias)}.#{@column_wrapper.name} is null) ", false] elsif opts == 't' [" #{@column_wrapper.alias_or_table_name(table_alias)}.#{@column_wrapper.name} = ?", true] end end end end end ================================================ FILE: lib/wice/columns/column_bootstrap_datepicker.rb ================================================ module Wice module Columns #:nodoc: class ViewColumnBootstrapDatepicker < ViewColumn #:nodoc: include Wice::BsCalendarHelpers include Wice::Columns::CommonDateDatetimeMixin include Wice::Columns::CommonJsDateDatetimeMixin def do_render(params) #:nodoc: calendar_data_from = prepare_data_for_bscalendar( initial_date: params[:fr], name: @name1, fire_event: auto_reload, grid_name: self.grid.name ) calendar_data_to = prepare_data_for_bscalendar( initial_date: params[:to], name: @name2, fire_event: auto_reload, grid_name: self.grid.name ) calendar_data_from.the_other_datepicker_id_to = calendar_data_to.dom_id calendar_data_to.the_other_datepicker_id_from = calendar_data_from.dom_id html1 = date_calendar_bs calendar_data_from html2 = date_calendar_bs calendar_data_to %(
    #{html1}#{html2}
    ) end def has_auto_reloading_calendar? #:nodoc: auto_reload end end class ConditionsGeneratorColumnBootstrapDatepicker < ConditionsGeneratorColumn #:nodoc: include Wice::Columns::CommonJsDateDatetimeConditionsGeneratorMixin end end end ================================================ FILE: lib/wice/columns/column_custom_dropdown.rb ================================================ module Wice module Columns #:nodoc: class ViewColumnCustomDropdown < ViewColumn #:nodoc: include ActionView::Helpers::FormOptionsHelper # text in the filter dropdown for an empty