rolemodel_tables 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/javascript/controllers/advanced_table_search_controller.js +53 -0
- data/app/views/kaminari/_first_page.html.slim +10 -0
- data/app/views/kaminari/_gap.html.slim +9 -0
- data/app/views/kaminari/_last_page.html.slim +10 -0
- data/app/views/kaminari/_next_page.html.slim +10 -0
- data/app/views/kaminari/_page.html.slim +14 -0
- data/app/views/kaminari/_paginator.html.slim +20 -0
- data/app/views/kaminari/_prev_page.html.slim +10 -0
- data/app/views/rolemodel_tables/_advanced_search_controls.html.slim +21 -0
- data/lib/rolemodel_tables/engine.rb +4 -0
- data/lib/rolemodel_tables/models/filter_view.rb +39 -0
- data/lib/rolemodel_tables/models/filters/collection_filter.rb +30 -0
- data/lib/rolemodel_tables/models/filters/date_filter.rb +23 -0
- data/lib/rolemodel_tables/models/filters/date_range_filter.rb +29 -0
- data/lib/rolemodel_tables/models/filters/number_range_filter.rb +33 -0
- data/lib/rolemodel_tables/models/filters/text_filter.rb +27 -0
- data/lib/rolemodel_tables/models/ransack_filter.rb +33 -0
- data/lib/rolemodel_tables/version.rb +5 -0
- data/lib/rolemodel_tables.rb +13 -0
- data/rolemodel_tables.gemspec +32 -0
- metadata +21 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f1a42253a5583547dba681eda52f60b16127b513500d0ae4b05257e5dbd6945
|
4
|
+
data.tar.gz: 0b8b0c4ffe45ff940cde6041013b2b2fafb39e440cfd3294ca8648a5096961c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06556643aff1a83c64d7c9e443009a2e0c835d6d6921331dfd5c0c29cbbc473f3744129a616964f08f59ddd454d9d8c8d98f358e0d508376bd55c9c16c99f9ba
|
7
|
+
data.tar.gz: b040bfe87341bda6a691467f5b582d4b16b149b503d0bad845e90cf0295c64cd39214af49c6b7cf503e5450fe9e6d73c31a5a49d84c7777996348bb72ab59f65
|
@@ -0,0 +1,53 @@
|
|
1
|
+
const { Controller } = require("@hotwired/stimulus")
|
2
|
+
|
3
|
+
class AdvancedTableSearchController extends Controller {
|
4
|
+
static values = {
|
5
|
+
advancedLabel: String,
|
6
|
+
}
|
7
|
+
|
8
|
+
static targets = [
|
9
|
+
'showSearchHiddenInput',
|
10
|
+
'toggleButton',
|
11
|
+
'controlsContainer',
|
12
|
+
'keywordSearchButton',
|
13
|
+
'toggleableArea',
|
14
|
+
]
|
15
|
+
|
16
|
+
connect() {
|
17
|
+
/* global window */
|
18
|
+
// Did the user initiate a search via the form? If so, reveal the form
|
19
|
+
// on page load. The form is initially hidden.
|
20
|
+
const searchParams = new URLSearchParams(window.location.search)
|
21
|
+
if (searchParams.get('show_advanced_search') === 'true') {
|
22
|
+
this.toggle()
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
toggle() {
|
27
|
+
this.toggleableAreaTarget.classList.toggle('hidden')
|
28
|
+
|
29
|
+
this.keywordSearchButtonTarget.classList.toggle('hidden')
|
30
|
+
|
31
|
+
const text = this.toggleableAreaTarget.classList.contains('hidden') ? 'Show' : 'Hide'
|
32
|
+
this.toggleButtonTarget.innerText = `${text} ${this.advancedLabelValue}`
|
33
|
+
|
34
|
+
const advancedSearchState = this.toggleableAreaTarget.classList.contains('hidden') ? 'false' : 'true'
|
35
|
+
this.showSearchHiddenInputTarget.value = advancedSearchState
|
36
|
+
|
37
|
+
const searchParams = new URLSearchParams(window.location.search)
|
38
|
+
searchParams.set('show_advanced_search', advancedSearchState)
|
39
|
+
const newRelativePathQuery = this._newRelativePathQuery(searchParams)
|
40
|
+
window.history.pushState(null, '', newRelativePathQuery)
|
41
|
+
}
|
42
|
+
|
43
|
+
clear() {
|
44
|
+
const searchParams = new URLSearchParams({ show_advanced_search: 'true' })
|
45
|
+
window.location = this._newRelativePathQuery(searchParams)
|
46
|
+
}
|
47
|
+
|
48
|
+
_newRelativePathQuery(searchParams) {
|
49
|
+
return `${window.location.pathname}?${searchParams.toString()}`
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
export default AdvancedTableSearchController
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/ Link to the "First" page
|
2
|
+
/ - available local variables
|
3
|
+
/ url: url to the first page
|
4
|
+
/ current_page: a page object for the currently displayed page
|
5
|
+
/ total_pages: total number of pages
|
6
|
+
/ per_page: number of items to fetch per page
|
7
|
+
/ remote: data-remote
|
8
|
+
|
9
|
+
span
|
10
|
+
= link_to_unless current_page.first?, icon('first_page'), url, remote: remote, class: 'btn btn--outline btn--small'
|
@@ -0,0 +1,9 @@
|
|
1
|
+
/ Non-link tag that stands for skipped pages...
|
2
|
+
/ - available local variables
|
3
|
+
/ current_page: a page object for the currently displayed page
|
4
|
+
/ total_pages: total number of pages
|
5
|
+
/ per_page: number of items to fetch per page
|
6
|
+
/ remote: data-remote
|
7
|
+
|
8
|
+
span
|
9
|
+
.btn.btn--small.pagination__gap = t('views.pagination.truncate').html_safe
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/ Link to the "Last" page
|
2
|
+
/ - available local variables
|
3
|
+
/ url: url to the last page
|
4
|
+
/ current_page: a page object for the currently displayed page
|
5
|
+
/ total_pages: total number of pages
|
6
|
+
/ per_page: number of items to fetch per page
|
7
|
+
/ remote: data-remote
|
8
|
+
|
9
|
+
span
|
10
|
+
= link_to_unless current_page.last?, icon('last_page'), url, remote: remote, class: 'btn btn--outline btn--small'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/ Link to the "Next" page
|
2
|
+
/ - available local variables
|
3
|
+
/ url: url to the next page
|
4
|
+
/ current_page: a page object for the currently displayed page
|
5
|
+
/ total_pages: total number of pages
|
6
|
+
/ per_page: number of items to fetch per page
|
7
|
+
/ remote: data-remote
|
8
|
+
|
9
|
+
span
|
10
|
+
= link_to_unless current_page.last?, icon('chevron_right'), url, rel: 'next', remote: remote, class: 'btn btn--outline btn--small'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
/ Link showing page number
|
2
|
+
/ - available local variables
|
3
|
+
/ page: a page object for "this" page
|
4
|
+
/ url: url to this page
|
5
|
+
/ current_page: a page object for the currently displayed page
|
6
|
+
/ total_pages: total number of pages
|
7
|
+
/ per_page: number of items to fetch per page
|
8
|
+
/ remote: data-remote
|
9
|
+
|
10
|
+
span.page
|
11
|
+
- if page.current?
|
12
|
+
.btn.btn--small.pagination__current = page
|
13
|
+
- else
|
14
|
+
= link_to page, url, remote: remote, rel: page.rel, class: 'btn btn--small btn--outline'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
/ The container tag
|
2
|
+
/ - available local variables
|
3
|
+
/ current_page: a page object for the currently displayed page
|
4
|
+
/ total_pages: total number of pages
|
5
|
+
/ per_page: number of items to fetch per page
|
6
|
+
/ remote: data-remote
|
7
|
+
/ paginator: the paginator that renders the pagination tags inside
|
8
|
+
|
9
|
+
== paginator.render do
|
10
|
+
nav.pagination role="navigation" aria-label="pager"
|
11
|
+
== first_page_tag unless current_page.first?
|
12
|
+
== prev_page_tag unless current_page.first?
|
13
|
+
- each_page do |page|
|
14
|
+
- if page.display_tag?
|
15
|
+
== page_tag page
|
16
|
+
- elsif !page.was_truncated?
|
17
|
+
== gap_tag
|
18
|
+
- unless current_page.out_of_range?
|
19
|
+
== next_page_tag unless current_page.last?
|
20
|
+
== last_page_tag unless current_page.last?
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/ Link to the "Previous" page
|
2
|
+
/ - available local variables
|
3
|
+
/ url: url to the previous page
|
4
|
+
/ current_page: a page object for the currently displayed page
|
5
|
+
/ total_pages: total number of pages
|
6
|
+
/ per_page: number of items to fetch per page
|
7
|
+
/ remote: data-remote
|
8
|
+
|
9
|
+
span
|
10
|
+
= link_to_unless current_page.first?, icon('chevron_left'), url, rel: 'prev', remote: remote, class: 'btn btn--outline btn--small'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
.margin-y-md data-controller='advanced-table-search submit-on-select' data-advanced-table-search-advanced-label-value='Advanced Filters'
|
2
|
+
= search_form_for filter_view.ransack, url: request.path, html: { class: 'simple_form' , data: { 'submit-on-select-target' => 'form' }} do |f|
|
3
|
+
= hidden_field_tag :show_advanced_search, params.fetch(:show_advanced_search, false), data: { 'advanced-table-search-target': 'showSearchHiddenInput' }
|
4
|
+
|
5
|
+
.card.margin-bottom-md data-advanced-table-search-target='controlsContainer'
|
6
|
+
.flex.items-center.gap-sm
|
7
|
+
/ TODO: INLINE STYLES BAD
|
8
|
+
.flex.items-center.gap-sm style='width: 300px'
|
9
|
+
= f.search_field filter_view.quick_search, placeholder: 'Keyword', class: 'form__input'
|
10
|
+
= f.button :search, class: 'btn btn--primary', data: { 'advanced-table-search-target': 'keywordSearchButton' }
|
11
|
+
|
12
|
+
button.project-search-toggle data-advanced-table-search-target='toggleButton' type='button' data-action='click->advanced-table-search#toggle' Show Advanced Filters
|
13
|
+
|
14
|
+
.margin-top-md.hidden data-advanced-table-search-target='toggleableArea'
|
15
|
+
.flex.flex-wrap
|
16
|
+
- filter_view.advanced_filters.each do |filter|
|
17
|
+
= filter.form_row(f).html_safe
|
18
|
+
|
19
|
+
.flex.justify-end.items-center.gap-sm.margin-top-md
|
20
|
+
button.btn.btn--outline type='button' data-action='click->advanced-table-search#clear' Clear
|
21
|
+
= f.button :search, class: 'btn btn--primary'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoleModelTables
|
4
|
+
# Parent class for all filter views.
|
5
|
+
class FilterView
|
6
|
+
class_attribute :default_sort, :quick_search, :advanced_filters
|
7
|
+
attr_reader :ransack
|
8
|
+
|
9
|
+
def initialize(scoped_collection)
|
10
|
+
@scoped_collection = scoped_collection
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.default_sort(sort_option)
|
14
|
+
self.default_sort = sort_option
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.quick_search(ransack_matcher)
|
18
|
+
self.quick_search = ransack_matcher
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.advanced_filter(ransack_matcher, klass_name, form_options = {})
|
22
|
+
self.advanced_filters ||= {}
|
23
|
+
self.advanced_filters[ransack_matcher] = "RoleModelTables::Filters::#{klass_name}".constantize.new(ransack_matcher, form_options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def advanced_filters
|
27
|
+
self.class.advanced_filters.map do |_, filter_instance|
|
28
|
+
filter_instance.filter = self
|
29
|
+
filter_instance
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def search(params)
|
34
|
+
@ransack = @scoped_collection.ransack(params[:query])
|
35
|
+
@ransack.sorts = default_sort if @ransack.sorts.empty?
|
36
|
+
@ransack.result.page(params[:page])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoleModelTables
|
4
|
+
module Filters
|
5
|
+
# Handle collection dropdowns.
|
6
|
+
class CollectionFilter < RansackFilter
|
7
|
+
def form_row(form)
|
8
|
+
content_tag(:div, class: 'form__group') do
|
9
|
+
[
|
10
|
+
label(form),
|
11
|
+
collection_select(form)
|
12
|
+
].join(' ').html_safe
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def collection_select(form)
|
19
|
+
form.collection_select(
|
20
|
+
ransack_matcher,
|
21
|
+
form_options.fetch(:collection, -> { [] }).call(filter),
|
22
|
+
form_options.fetch(:option_value, ''),
|
23
|
+
form_options.fetch(:option_label, ''),
|
24
|
+
{ include_blank: 'Any' },
|
25
|
+
{ class: 'form__dropdown' }
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoleModelTables
|
4
|
+
module Filters
|
5
|
+
# Handles a single date field filter.
|
6
|
+
class DateFilter < RansackFilter
|
7
|
+
def form_row(form)
|
8
|
+
content_tag(:div, class: 'form__group') do
|
9
|
+
[
|
10
|
+
label(form),
|
11
|
+
date_field(form)
|
12
|
+
].join(' ').html_safe
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def date_field(form)
|
19
|
+
form.date_field(ransack_matcher, { class: 'form__input' })
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoleModelTables
|
4
|
+
module Filters
|
5
|
+
# Handles a date range filter.
|
6
|
+
class DateRangeFilter < RansackFilter
|
7
|
+
def form_row(form)
|
8
|
+
content_tag(:div, class: 'form__group') do
|
9
|
+
[
|
10
|
+
label(form),
|
11
|
+
content_tag(:div, class: 'flex items-center gap-sm') do
|
12
|
+
[
|
13
|
+
date_field(form, :gteq),
|
14
|
+
content_tag(:span, '-'),
|
15
|
+
date_field(form, :lteq)
|
16
|
+
].join(' ').html_safe
|
17
|
+
end
|
18
|
+
].join(' ').html_safe
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def date_field(form, predicate)
|
25
|
+
form.date_field("#{ransack_matcher}_#{predicate}", { class: 'form__input' })
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoleModelTables
|
4
|
+
module Filters
|
5
|
+
# Handles a number range filter.
|
6
|
+
class NumberRangeFilter < RansackFilter
|
7
|
+
def form_row(form)
|
8
|
+
content_tag(:div, class: 'form__group') do
|
9
|
+
[
|
10
|
+
label(form),
|
11
|
+
content_tag(:div, class: 'flex items-center gap-sm') do
|
12
|
+
[
|
13
|
+
search_field(form, :gteq),
|
14
|
+
content_tag(:span, '-'),
|
15
|
+
search_field(form, :lteq)
|
16
|
+
].join(' ').html_safe
|
17
|
+
end
|
18
|
+
].join(' ').html_safe
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def search_field(form, predicate)
|
25
|
+
form.search_field(
|
26
|
+
"#{ransack_matcher}_#{predicate}",
|
27
|
+
class: 'form__input',
|
28
|
+
placeholder: form_options.fetch(:range_begin_placeholder, '')
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoleModelTables
|
4
|
+
module Filters
|
5
|
+
# Handles a text field filter.
|
6
|
+
class TextFilter < RansackFilter
|
7
|
+
def form_row(form)
|
8
|
+
content_tag(:div, class: 'form__group') do
|
9
|
+
[
|
10
|
+
label(form),
|
11
|
+
search_field(form)
|
12
|
+
].join(' ').html_safe
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def search_field(form)
|
19
|
+
form.search_field(
|
20
|
+
ransack_matcher,
|
21
|
+
class: 'form__input',
|
22
|
+
placeholder: form_options.fetch(:placeholder, '')
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoleModelTables
|
4
|
+
module Filters
|
5
|
+
# Parent class for all custom filters.
|
6
|
+
class RansackFilter
|
7
|
+
include ActionView::Context
|
8
|
+
include ActionView::Helpers::TagHelper
|
9
|
+
|
10
|
+
attr_reader :ransack_matcher, :form_options
|
11
|
+
attr_accessor :filter
|
12
|
+
|
13
|
+
def initialize(ransack_matcher, form_options = {})
|
14
|
+
@ransack_matcher = ransack_matcher
|
15
|
+
@form_options = form_options
|
16
|
+
end
|
17
|
+
|
18
|
+
def form_row(_form)
|
19
|
+
throw 'Subclass must override'
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def label(form)
|
25
|
+
form.label(
|
26
|
+
ransack_matcher,
|
27
|
+
form_options.fetch(:label, ransack_matcher.to_s.humanize),
|
28
|
+
class: 'form__label'
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RoleModelTables
|
4
|
+
class Error < StandardError; end
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rolemodel_tables/engine'
|
8
|
+
require 'rolemodel_tables/models/filter_view'
|
9
|
+
require 'rolemodel_tables/models/ransack_filter'
|
10
|
+
|
11
|
+
Dir.chdir(__dir__) do
|
12
|
+
Dir['rolemodel_tables/models/filters/*.rb'].each { |file| require file }
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'rolemodel_tables/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = 'rolemodel_tables'
|
9
|
+
s.version = RoleModelTables::VERSION
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.authors = ['Jeremy Walton']
|
12
|
+
s.email = 'consult@rolemodelsoftware.com'
|
13
|
+
|
14
|
+
s.summary = 'Rolemodel Tables!'
|
15
|
+
s.description = 'Helpers for advanced table functionality.'
|
16
|
+
s.homepage = 'https://github.com/RoleModel/rolemodel_tables'
|
17
|
+
|
18
|
+
s.license = 'MIT'
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
s.files = Dir.chdir(File.expand_path(__dir__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
24
|
+
end
|
25
|
+
s.require_paths = ['lib']
|
26
|
+
|
27
|
+
s.required_ruby_version = '>= 2.1.0'
|
28
|
+
|
29
|
+
s.add_dependency 'kaminari', '~> 1.2'
|
30
|
+
s.add_dependency 'ransack', '~> 2.5'
|
31
|
+
s.add_dependency 'slim', '~> 4.1'
|
32
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rolemodel_tables
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Walton
|
@@ -61,6 +61,26 @@ files:
|
|
61
61
|
- ".gitignore"
|
62
62
|
- LICENSE
|
63
63
|
- README.md
|
64
|
+
- app/javascript/controllers/advanced_table_search_controller.js
|
65
|
+
- app/views/kaminari/_first_page.html.slim
|
66
|
+
- app/views/kaminari/_gap.html.slim
|
67
|
+
- app/views/kaminari/_last_page.html.slim
|
68
|
+
- app/views/kaminari/_next_page.html.slim
|
69
|
+
- app/views/kaminari/_page.html.slim
|
70
|
+
- app/views/kaminari/_paginator.html.slim
|
71
|
+
- app/views/kaminari/_prev_page.html.slim
|
72
|
+
- app/views/rolemodel_tables/_advanced_search_controls.html.slim
|
73
|
+
- lib/rolemodel_tables.rb
|
74
|
+
- lib/rolemodel_tables/engine.rb
|
75
|
+
- lib/rolemodel_tables/models/filter_view.rb
|
76
|
+
- lib/rolemodel_tables/models/filters/collection_filter.rb
|
77
|
+
- lib/rolemodel_tables/models/filters/date_filter.rb
|
78
|
+
- lib/rolemodel_tables/models/filters/date_range_filter.rb
|
79
|
+
- lib/rolemodel_tables/models/filters/number_range_filter.rb
|
80
|
+
- lib/rolemodel_tables/models/filters/text_filter.rb
|
81
|
+
- lib/rolemodel_tables/models/ransack_filter.rb
|
82
|
+
- lib/rolemodel_tables/version.rb
|
83
|
+
- rolemodel_tables.gemspec
|
64
84
|
homepage: https://github.com/RoleModel/rolemodel_tables
|
65
85
|
licenses:
|
66
86
|
- MIT
|