carload 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -0
- data/app/assets/stylesheets/carload/_select2.scss +15 -0
- data/app/assets/stylesheets/carload/colors.scss.erb +5 -0
- data/app/assets/stylesheets/carload/common.scss +16 -10
- data/app/assets/stylesheets/carload/dashboard.scss +16 -14
- data/app/controllers/carload/dashboard_controller.rb +19 -3
- data/app/helpers/carload/application_helper.rb +16 -0
- data/app/helpers/carload/dashboard_helper.rb +73 -65
- data/app/views/carload/dashboard/_form.html.erb +14 -2
- data/app/views/carload/dashboard/index.html.erb +1 -7
- data/app/views/carload/dashboard/shared/_search_fields.html.erb +6 -10
- data/app/views/layouts/carload/dashboard.html.erb +2 -2
- data/db/migrate/20161030074822_carload_enable_zhparser_extension.rb +7 -0
- data/lib/carload/association_pipelines.rb +52 -0
- data/lib/carload/dashboard.rb +5 -108
- data/lib/carload/engine.rb +49 -0
- data/lib/carload/model_spec.rb +98 -0
- data/lib/carload/version.rb +1 -1
- data/lib/carload.rb +5 -1
- data/lib/generators/carload/dash_generator.rb +19 -18
- data/lib/generators/carload/install_generator.rb +10 -0
- data/lib/generators/carload/templates/carload.rb +14 -2
- metadata +9 -6
- data/app/assets/stylesheets/carload/colors.scss +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9dc8244c3cd30b96538af399c5d325a32c7b46f2
|
4
|
+
data.tar.gz: 038e2a71da51255577b35a15fc21a8b6b66fb7b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a24677546621e1a187042e2e27ea5b603495590543b624133ab06151ddb423e2d7ad07695241277f52f3b9efc212bee433b87583140749be9e91a9693818d23
|
7
|
+
data.tar.gz: 99b19227d0f62ad5ae1081e5fa1deb04becb1373ec97f88f40514d9493e265b3e4bc84907d829afa908bad798287e7c62da828f22c9798fa3f60d18ccf42e770
|
data/README.md
CHANGED
@@ -26,9 +26,24 @@ You can edit the initializer `config/initializers/carload.rb` for example:
|
|
26
26
|
|
27
27
|
```ruby
|
28
28
|
Carload.setup do |config|
|
29
|
+
# Set the title that will be displayed on the browser tab area.
|
30
|
+
config.page.title = nil
|
31
|
+
|
32
|
+
# Set the footer text that will be displayed on each page.
|
33
|
+
config.page.footer = nil
|
34
|
+
|
35
|
+
# Set the colors of page elements.
|
36
|
+
config.page.main_color = nil
|
37
|
+
config.page.text_color = 'black'
|
38
|
+
config.page.button_color = nil
|
39
|
+
config.page.button_text_color = nil
|
40
|
+
|
29
41
|
# Specify which authentication solution is used. Currently, we only support Devise.
|
30
42
|
config.auth_solution = :devise
|
31
43
|
|
44
|
+
# Set which file upload solution is used. Currently, we only support Carrierwave.
|
45
|
+
config.upload_solution = :carrierwave
|
46
|
+
|
32
47
|
# Set the actions used to discern user's permission to access dashboard.
|
33
48
|
#
|
34
49
|
# config.dashboard.permits_user.<method> = '...'
|
@@ -87,6 +102,8 @@ class Dashboard < Carload::Dashboard
|
|
87
102
|
end
|
88
103
|
```
|
89
104
|
|
105
|
+
- You can run another generator `rails g carload:dash <model_name>` to generate the above content automatically (it may ask you some question).
|
106
|
+
|
90
107
|
- Make sure you have the necessary I18n translation files, for example:
|
91
108
|
|
92
109
|
```yaml
|
@@ -15,3 +15,18 @@
|
|
15
15
|
.select2-container {
|
16
16
|
min-width: 174px;
|
17
17
|
}
|
18
|
+
|
19
|
+
.select2-search__field {
|
20
|
+
line-height: 22px;
|
21
|
+
}
|
22
|
+
.select2-search__field:focus {
|
23
|
+
box-shadow: none !important;
|
24
|
+
}
|
25
|
+
.select2-selection__choice {
|
26
|
+
line-height: 22px;
|
27
|
+
background-color: $main-color !important;
|
28
|
+
color: white;
|
29
|
+
}
|
30
|
+
.select2-selection__choice__remove {
|
31
|
+
color: white !important;
|
32
|
+
}
|
@@ -0,0 +1,5 @@
|
|
1
|
+
$main-color: <%= Carload.page.main_color || '#8C7DA6' %>;
|
2
|
+
$text-color: <%= Carload.page.text_color || '#3F3F44' %>;
|
3
|
+
$button-color-1: <%= Carload.page.button_color_1 || Carload.page.button_color || '#60b044' %>;
|
4
|
+
$button-color-2: <%= Carload.page.button_color_2 || Carload.page.button_color || '#8add6d' %>;
|
5
|
+
$button-text-color: <%= Carload.page.button_text_color || 'white' %>;
|
@@ -51,19 +51,21 @@ th {
|
|
51
51
|
}
|
52
52
|
|
53
53
|
.btn-primary {
|
54
|
-
color:
|
54
|
+
color: $button-text-color;
|
55
55
|
text-shadow: 0 -1px 0 rgba(0,0,0,0.15);
|
56
|
-
background-color:
|
57
|
-
background-image: -webkit-linear-gradient(
|
58
|
-
background-image: linear-gradient(
|
59
|
-
border
|
56
|
+
background-color: $button-color-1;
|
57
|
+
background-image: -webkit-linear-gradient($button-color-2, $button-color-1);
|
58
|
+
background-image: linear-gradient($button-color-2, $button-color-1);
|
59
|
+
border: none;
|
60
|
+
height: 34px;
|
60
61
|
}
|
61
62
|
.btn-primary:hover {
|
62
|
-
color:
|
63
|
-
background-color:
|
64
|
-
background-image: -webkit-linear-gradient(
|
65
|
-
background-image: linear-gradient(
|
66
|
-
border
|
63
|
+
color: lighten($button-text-color, 20%);
|
64
|
+
background-color: lighten($button-color-1, 20%);
|
65
|
+
background-image: -webkit-linear-gradient(lighten($button-color-2, 20%), lighten($button-color-1, 20%));
|
66
|
+
background-image: linear-gradient(lighten($button-color-2, 20%), lighten($button-color-1, 20%));
|
67
|
+
border: none;
|
68
|
+
height: 34px;
|
67
69
|
}
|
68
70
|
|
69
71
|
.form-control:hover {
|
@@ -75,3 +77,7 @@ th {
|
|
75
77
|
border-color: $main-color !important;
|
76
78
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px $main-color !important;
|
77
79
|
}
|
80
|
+
|
81
|
+
.label-primary {
|
82
|
+
background-color: $main-color;
|
83
|
+
}
|
@@ -25,11 +25,13 @@ $half-spacing: 8px;
|
|
25
25
|
border-top-right-radius: 0;
|
26
26
|
}
|
27
27
|
a {
|
28
|
+
color: $text-color;
|
28
29
|
border-color: $main-color;
|
29
30
|
}
|
30
31
|
a:hover {
|
31
32
|
background-color: lighten($main-color, 20%);
|
32
33
|
border-color: lighten($main-color, 20%);
|
34
|
+
color: white;
|
33
35
|
}
|
34
36
|
a.active {
|
35
37
|
background-color: $main-color;
|
@@ -64,6 +66,9 @@ $half-spacing: 8px;
|
|
64
66
|
td {
|
65
67
|
overflow: scroll;
|
66
68
|
}
|
69
|
+
th:first-child, td:first-child {
|
70
|
+
padding-left: $spacing;
|
71
|
+
}
|
67
72
|
th:last-child {
|
68
73
|
width: 50px;
|
69
74
|
}
|
@@ -78,22 +83,19 @@ $half-spacing: 8px;
|
|
78
83
|
#dashboard-index-buttons {
|
79
84
|
display: inline-block;
|
80
85
|
margin-right: $spacing;
|
81
|
-
|
82
|
-
#new_q {
|
83
|
-
display: inline-block;
|
86
|
+
line-height: 34px;
|
84
87
|
}
|
85
88
|
|
86
|
-
|
87
|
-
|
88
|
-
.
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
#search-input {
|
90
|
+
position: relative;
|
91
|
+
.icon {
|
92
|
+
position: absolute;
|
93
|
+
bottom: 8px;
|
94
|
+
left: 8px;
|
95
|
+
color: #ccc;
|
92
96
|
}
|
93
|
-
|
94
|
-
|
97
|
+
input {
|
98
|
+
width: 100%;
|
99
|
+
padding-left: 30px;
|
95
100
|
}
|
96
101
|
}
|
97
|
-
.search-buttons {
|
98
|
-
display: inline-flex;
|
99
|
-
}
|
@@ -8,14 +8,20 @@ module Carload
|
|
8
8
|
|
9
9
|
before_action :set_model
|
10
10
|
before_action :set_object, only: [:edit, :update, :destroy]
|
11
|
+
before_action :transform_polymorphic_params, only: [:create, :update]
|
12
|
+
|
11
13
|
include Croppable
|
14
|
+
include ApplicationHelper
|
12
15
|
|
13
16
|
def index
|
14
17
|
authorize :carload_dashboard, :index? unless Carload.auth_solution == :none
|
15
|
-
|
16
|
-
|
18
|
+
if params[:search].present?
|
19
|
+
@query = params[:search][:query]
|
20
|
+
@objects = @model_class.search(params[:search][:query]).page(params[:page])
|
21
|
+
else
|
22
|
+
@objects = @model_class.page(params[:page])
|
23
|
+
end
|
17
24
|
@show_attributes = Dashboard.model(@model_name).index_page[:shows][:attributes] + [:created_at, :updated_at]
|
18
|
-
@search_attributes = Dashboard.model(@model_name).index_page[:searches][:attributes]
|
19
25
|
render "dashboard/#{@model_names}/index.html.erb"
|
20
26
|
end
|
21
27
|
|
@@ -82,5 +88,15 @@ module Carload
|
|
82
88
|
def rescue_unmanaged_model_error exception
|
83
89
|
redirect_to dashboard_error_path(message: exception.message)
|
84
90
|
end
|
91
|
+
|
92
|
+
def transform_polymorphic_params
|
93
|
+
polymorphic_params = {}
|
94
|
+
params[@model_name].each do |key, value|
|
95
|
+
if polymorphic? key
|
96
|
+
polymorphic_params["#{key}_id"], polymorphic_params["#{key}_type"] = value.split(',')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
params[@model_name].merge! polymorphic_params
|
100
|
+
end
|
85
101
|
end
|
86
102
|
end
|
@@ -8,8 +8,24 @@ module Carload
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
def polymorphic? attribute_name
|
12
|
+
Dashboard.model(@model_name).associated_models.each_value do |associated_model|
|
13
|
+
return associated_model[:name] if attribute_name =~ /#{associated_model[:name]}/ and associated_model[:polymorphic]
|
14
|
+
end
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
11
18
|
def image? attribute_name
|
12
19
|
attribute_name.to_s =~ /image|logo|img/
|
13
20
|
end
|
21
|
+
|
22
|
+
def id_or_ids associated_model
|
23
|
+
case associated_model[:association_type]
|
24
|
+
when :has_many
|
25
|
+
"#{associated_model[:name]}_ids"
|
26
|
+
else
|
27
|
+
"#{associated_model[:name]}_id"
|
28
|
+
end
|
29
|
+
end
|
14
30
|
end
|
15
31
|
end
|
@@ -1,59 +1,45 @@
|
|
1
1
|
module Carload
|
2
2
|
module DashboardHelper
|
3
|
-
def generate_input form, model_name, attribute_name,
|
4
|
-
if
|
3
|
+
def generate_input form, model_name, attribute_name, options = {}
|
4
|
+
if options[:polymorphic]
|
5
|
+
form.input attribute_name,
|
6
|
+
collection: @model_class.send(attribute_name.to_s.pluralize),
|
7
|
+
selected: options[:value],
|
8
|
+
input_html: { class: 'use-select2' }
|
9
|
+
elsif attribute_name =~ /_id$/
|
5
10
|
associated_model = attribute_name.gsub(/_id$/, '').to_sym
|
6
|
-
|
7
|
-
label_attribute =
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
value_method: :id,
|
16
|
-
input_html: {
|
17
|
-
class: 'use-select2',
|
18
|
-
data: {
|
19
|
-
placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{real_model}.#{label_attribute}"))
|
20
|
-
}
|
21
|
-
},
|
22
|
-
wrapper_html: {
|
23
|
-
id: "#{real_model.camelize}-#{label_attribute}"
|
24
|
-
}
|
25
|
-
)
|
26
|
-
end
|
27
|
-
# Add JavaScript to select which real model to work on.
|
28
|
-
forms << <<-EOT
|
29
|
-
<script>
|
30
|
-
$('.#{model_name}_#{associated_model}_id').hide()
|
31
|
-
if ($('##{model_name}_#{associated_model}_type').val() != '') {
|
32
|
-
$('#' + $('##{model_name}_#{associated_model}_type').val() + '-#{label_attribute}').show()
|
33
|
-
}
|
34
|
-
$('##{model_name}_#{associated_model}_type').change(function() {
|
35
|
-
$('.#{model_name}_#{associated_model}_id').hide()
|
36
|
-
$('#' + $(this).val() + '-#{label_attribute}').show()
|
37
|
-
$('.package_packagable_id > .select2-container').css('width', '100%')
|
38
|
-
})
|
39
|
-
</script>
|
40
|
-
EOT
|
41
|
-
raw forms.html_safe
|
42
|
-
else
|
43
|
-
form.association associated_model,
|
44
|
-
label_method: label_attribute,
|
45
|
-
label: t("activerecord.models.#{associated_model}"),
|
46
|
-
input_html: {
|
47
|
-
class: 'use-select2',
|
48
|
-
data: {
|
49
|
-
placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{associated_model}.#{label_attribute}"))
|
50
|
-
}
|
11
|
+
association_specs = Dashboard.model(model_name).associated_models[associated_model]
|
12
|
+
label_attribute = association_specs[:choose_by]
|
13
|
+
form.association associated_model,
|
14
|
+
label_method: label_attribute,
|
15
|
+
label: t("activerecord.models.#{associated_model}"),
|
16
|
+
input_html: {
|
17
|
+
class: 'use-select2',
|
18
|
+
data: {
|
19
|
+
placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{associated_model}.#{label_attribute}"))
|
51
20
|
}
|
52
|
-
|
21
|
+
}
|
22
|
+
elsif attribute_name =~ /_ids$/
|
23
|
+
# Mandle many-to-many association.
|
24
|
+
associated_model = attribute_name.gsub(/_ids$/, '').to_sym
|
25
|
+
association_specs = Dashboard.model(model_name).associated_models[associated_model]
|
26
|
+
label_attribute = association_specs[:choose_by]
|
27
|
+
form.input attribute_name,
|
28
|
+
label: t("activerecord.attributes.#{associated_model}.#{label_attribute}") + " (#{t("activerecord.models.#{associated_model}")})",
|
29
|
+
collection: associated_model.to_s.camelize.constantize.all,
|
30
|
+
label_method: label_attribute,
|
31
|
+
value_method: :id,
|
32
|
+
input_html: {
|
33
|
+
class: 'use-select2',
|
34
|
+
multiple: true,
|
35
|
+
data: {
|
36
|
+
placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{associated_model}.#{label_attribute}"))
|
37
|
+
}
|
38
|
+
}
|
53
39
|
elsif attribute_name =~ /_type$/
|
54
40
|
associated_model = attribute_name.gsub(/_type$/, '').to_sym
|
55
|
-
|
56
|
-
form.input attribute_name, collection:
|
41
|
+
association_specs = Dashboard.model(model_name).associated_models[associated_model]
|
42
|
+
form.input attribute_name, collection: association_specs[:instance_models].map{ |x| x.to_s.camelize },
|
57
43
|
input_html: {
|
58
44
|
class: 'use-select2',
|
59
45
|
data: {
|
@@ -67,20 +53,29 @@ module Carload
|
|
67
53
|
end
|
68
54
|
end
|
69
55
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
56
|
+
def generate_show_title attribute
|
57
|
+
case attribute
|
58
|
+
when Symbol
|
59
|
+
begin
|
60
|
+
t("activerecord.attributes.#{@model_name}.#{attribute}", raise: true)
|
61
|
+
rescue
|
62
|
+
t("carload.activerecord.#{attribute}", raise: true)
|
63
|
+
end
|
64
|
+
when String
|
65
|
+
begin
|
66
|
+
t("activerecord.attributes.#{@model_name}.#{attribute}", raise: true)
|
67
|
+
rescue
|
68
|
+
"#{t("activerecord.attributes.#{attribute}", raise: true)} (#{t("activerecord.models.#{attribute.split('.').first.to_s.singularize}", raise: true)})"
|
69
|
+
end
|
70
|
+
when Array
|
71
|
+
if attribute.first == :pluck
|
72
|
+
raise UnsupportedError.new("attribute #{attribute}") if attribute.size != 3
|
73
|
+
model_name = attribute[1].to_s.singularize
|
74
|
+
attribute_name = attribute[2]
|
75
|
+
"#{t("activerecord.attributes.#{model_name}.#{attribute_name}", raise: true)} (#{t("activerecord.models.#{model_name}", raise: true)})"
|
76
|
+
else
|
77
|
+
"#{t("activerecord.attributes.#{attribute.join('.')}", raise: true)} (#{t("activerecord.models.#{attribute[0].to_s.singularize}", raise: true)})"
|
78
|
+
end
|
84
79
|
end
|
85
80
|
end
|
86
81
|
|
@@ -89,7 +84,20 @@ module Carload
|
|
89
84
|
when Symbol
|
90
85
|
object.send attribute
|
91
86
|
when String
|
92
|
-
eval "object.#{attribute.gsub('.', '&.')}"
|
87
|
+
res = eval "object.#{attribute.gsub('.', '&.')}"
|
88
|
+
case res
|
89
|
+
when String
|
90
|
+
res
|
91
|
+
when Array
|
92
|
+
raw res.map { |x| "<span class='label label-primary'>#{x}</span>" }.join(' ')
|
93
|
+
end
|
94
|
+
when Array
|
95
|
+
if attribute.first == :pluck
|
96
|
+
raise UnsupportedError.new("attribute #{attribute}") if attribute.size != 3
|
97
|
+
generate_show object, "#{attribute[1].to_s.pluralize}.pluck(:#{attribute[2]})"
|
98
|
+
else
|
99
|
+
generate_show object, attribute.join('.')
|
100
|
+
end
|
93
101
|
end
|
94
102
|
end
|
95
103
|
end
|
@@ -1,7 +1,19 @@
|
|
1
1
|
<%= simple_form_for @object, url: "/carload/dashboard/#{@model_names}/#{@object.id}" do |f| %>
|
2
|
+
<!-- Normal attributes -->
|
2
3
|
<% @model_class.columns_hash.each do |name, column| %>
|
3
|
-
<% next if
|
4
|
-
<%= generate_input
|
4
|
+
<% next if Carload::ModelSpec::SkippedAttributes.include? name or polymorphic? name %>
|
5
|
+
<%= generate_input f, @model_name, name %>
|
6
|
+
<% end %>
|
7
|
+
<!-- Polymorphics -->
|
8
|
+
<% Dashboard.model(@model_name).associated_models.each_value do |associated_model| %>
|
9
|
+
<% next if associated_model[:polymorphic] != true %>
|
10
|
+
<%= generate_input f, @model_name, associated_model[:name], polymorphic: true,
|
11
|
+
value: "#{@object.send("#{associated_model[:name]}_id")},#{@object.send("#{associated_model[:name]}_type")}" %>
|
12
|
+
<% end %>
|
13
|
+
<!-- Join tables -->
|
14
|
+
<% Dashboard.model(@model_name).associated_models.each_value do |associated_model| %>
|
15
|
+
<% next if not associated_model[:join_table] %>
|
16
|
+
<%= generate_input f, @model_name, id_or_ids(associated_model) %>
|
5
17
|
<% end %>
|
6
18
|
<%= f.button :submit, t('carload.action.submit'), class: 'btn btn-primary' %>
|
7
19
|
<% end %>
|
@@ -9,13 +9,7 @@
|
|
9
9
|
<tr>
|
10
10
|
<th width='20'>ID</th>
|
11
11
|
<% @show_attributes.each do |attribute| %>
|
12
|
-
<th width='100'>
|
13
|
-
<% begin %>
|
14
|
-
<%= t("activerecord.attributes.#{@model_name}.#{attribute}", raise: true) %>
|
15
|
-
<% rescue %>
|
16
|
-
<%= t("carload.activerecord.#{attribute}") %>
|
17
|
-
<% end %>
|
18
|
-
</th>
|
12
|
+
<th width='100'><%= generate_show_title attribute rescue attribute %></th>
|
19
13
|
<% end %>
|
20
14
|
<th width='20'><%= t('carload.actions') %></th>
|
21
15
|
</tr>
|
@@ -1,10 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
<div class='search-buttons'>
|
8
|
-
<%= f.submit t('carload.action.search'), class: 'btn btn-primary' %>
|
9
|
-
</div>
|
10
|
-
<% end %>
|
1
|
+
<div id='search-input'>
|
2
|
+
<%= form_for :search, url: dashboard_search_path(@model_names), method: :get do |f| %>
|
3
|
+
<span class='icon'><%= fa_icon('search') %></span>
|
4
|
+
<%= f.text_field :query, class: 'form-control', placeholder: 'Search ...', value: @query %>
|
5
|
+
<% end %>
|
6
|
+
</div>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<title
|
4
|
+
<title><%= Carload.page.title || 'Carload' %></title>
|
5
5
|
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0'/>
|
6
6
|
<%= stylesheet_link_tag 'carload/dashboard', media: 'all' %>
|
7
7
|
<%= javascript_include_tag 'carload/dashboard' %>
|
@@ -25,7 +25,7 @@
|
|
25
25
|
</div>
|
26
26
|
|
27
27
|
<footer class='center'>
|
28
|
-
<%= raw t('carload.footer.message') %>
|
28
|
+
<%= raw Carload.page.footer || t('carload.footer.message') %>
|
29
29
|
</footer>
|
30
30
|
</div>
|
31
31
|
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class CarloadEnableZhparserExtension < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
enable_extension :zhparser
|
4
|
+
execute 'create text search configuration zhparser (parser = zhparser);'
|
5
|
+
execute 'alter text search configuration zhparser add mapping for n,v,a,i,e,l with simple;'
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Carload
|
2
|
+
module AssociationPipelines
|
3
|
+
def association_pipelines
|
4
|
+
[ :pipeline_1, :pipeline_2, :pipeline_3 ]
|
5
|
+
end
|
6
|
+
|
7
|
+
# Find polymorphic instance models.
|
8
|
+
def pipeline_1 association
|
9
|
+
return unless association.options[:polymorphic]
|
10
|
+
associated_model = @associated_models[association.name]
|
11
|
+
ActiveRecord::Base.descendants.each do |model|
|
12
|
+
next if model.name == 'ApplicationRecord' or model.name.underscore == @name.to_s
|
13
|
+
model.reflect_on_all_associations.each do |model_association|
|
14
|
+
next unless model_association.options[:as] == association.name
|
15
|
+
associated_model[:instance_models] ||= []
|
16
|
+
associated_model[:instance_models] << model.name.underscore.to_sym
|
17
|
+
if not associated_model[:attributes]
|
18
|
+
associated_model[:attributes] = model.column_names - ModelSpec::SkippedAttributes
|
19
|
+
else
|
20
|
+
associated_model[:attributes] = associated_model[:attributes] & model.column_names
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Add possible attributes to let user choose.
|
27
|
+
def pipeline_2 association
|
28
|
+
return unless associated_model = @associated_models[association.name.to_s.singularize.to_sym]
|
29
|
+
model = association.name.to_s.singularize.camelize.constantize rescue return
|
30
|
+
associated_model[:attributes] ||= []
|
31
|
+
associated_model[:attributes] = model.column_names - ModelSpec::SkippedAttributes
|
32
|
+
associated_model[:attributes] = associated_model[:attributes] - [
|
33
|
+
"#{@name}_id",
|
34
|
+
"#{associated_model[:polymorphic]}_id",
|
35
|
+
"#{associated_model[:polymorphic]}_type"
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add has-many permitted attribute.
|
40
|
+
def pipeline_3 association
|
41
|
+
_association = (association.delegate_reflection rescue nil) || association
|
42
|
+
return unless _association.class == ActiveRecord::Reflection::HasManyReflection
|
43
|
+
# Exclude join table.
|
44
|
+
return unless @klass.reflect_on_all_associations.select { |x| x.options[:through] == _association.name }.empty?
|
45
|
+
@attributes[:permitted].each do |permitted|
|
46
|
+
next unless permitted.class == Hash
|
47
|
+
return if permitted.keys.first == :"#{_association.class_name.underscore}_ids"
|
48
|
+
end
|
49
|
+
@attributes[:permitted] << { :"#{_association.class_name.underscore}_ids" => [] }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/carload/dashboard.rb
CHANGED
@@ -1,112 +1,12 @@
|
|
1
1
|
module Carload
|
2
2
|
class Dashboard
|
3
|
-
class ModelSpec
|
4
|
-
attr_accessor :default, :attributes, :index_page, :associated_models
|
5
|
-
|
6
|
-
SkippedAttributes = [
|
7
|
-
'id', 'created_at', 'updated_at',
|
8
|
-
'encrypted_password', 'reset_password_token',
|
9
|
-
'reset_password_sent_at', 'remember_created_at',
|
10
|
-
'sign_in_count', 'current_sign_in_at',
|
11
|
-
'last_sign_in_at', 'current_sign_in_ip',
|
12
|
-
'last_sign_in_ip'
|
13
|
-
].freeze
|
14
|
-
|
15
|
-
def self.foreign_key? attribute
|
16
|
-
attribute =~ /_id$/
|
17
|
-
end
|
18
|
-
|
19
|
-
def foreign_key? attribute
|
20
|
-
ModelSpec.foreign_key? attribute
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.polymorphic? model_class, attribute
|
24
|
-
return false unless foreign_key? attribute
|
25
|
-
model_class.column_names.include? "#{attribute.to_s.gsub('_id', '')}_type"
|
26
|
-
end
|
27
|
-
|
28
|
-
def polymorphic? model_class, attribute
|
29
|
-
ModelSpec.polymorphic? model_class, attribute
|
30
|
-
end
|
31
|
-
|
32
|
-
def initialize model_class = nil
|
33
|
-
@default = false
|
34
|
-
@attributes = ExtendedHash.new
|
35
|
-
@index_page = ExtendedHash.new
|
36
|
-
@index_page[:shows] = ExtendedHash.new
|
37
|
-
@index_page[:searches] = ExtendedHash.new
|
38
|
-
@associated_models = {}
|
39
|
-
if model_class
|
40
|
-
@attributes[:permitted] = model_class.column_names - SkippedAttributes
|
41
|
-
@attributes[:permitted].each do |attribute|
|
42
|
-
@index_page[:shows][:attributes] ||= []
|
43
|
-
@index_page[:searches][:attributes] ||= []
|
44
|
-
if foreign_key? attribute
|
45
|
-
associated_model = attribute.gsub('_id', '')
|
46
|
-
@associated_models[associated_model] = {
|
47
|
-
choose_by: nil, # Wait for setting.
|
48
|
-
polymorphic: polymorphic?(model_class, attribute),
|
49
|
-
model: model_class.name.underscore.to_sym
|
50
|
-
}
|
51
|
-
else
|
52
|
-
@index_page[:shows][:attributes] << attribute
|
53
|
-
@index_page[:searches][:attributes] << { name: attribute.to_sym, term: :cont }
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def changed? spec
|
60
|
-
not @attributes[:permitted] == spec.attributes[:permitted] or
|
61
|
-
not @index_page[:searches][:attributes] == spec.index_page[:searches][:attributes]
|
62
|
-
end
|
63
|
-
|
64
|
-
def revise_stage_1!
|
65
|
-
# Handle polymorphic associated models if necessary.
|
66
|
-
@associated_models.each do |associated_model, options|
|
67
|
-
next if not options or not options[:polymorphic]
|
68
|
-
ActiveRecord::Base.descendants.each do |model|
|
69
|
-
next if model.name == 'ApplicationRecord'
|
70
|
-
if not model.reflect_on_all_associations.map(&:name).select { |x| x.to_s =~ /\b(#{options[:model]}|#{options[:model].to_s.pluralize})\b/ }.empty?
|
71
|
-
options[:available_models] ||= []
|
72
|
-
options[:available_models] << model.name.underscore
|
73
|
-
if not options[:common_attributes]
|
74
|
-
options[:common_attributes] = model.column_names - SkippedAttributes
|
75
|
-
else
|
76
|
-
options[:common_attributes] = options[:common_attributes] & model.column_names
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def revise_stage_2!
|
84
|
-
# Handle associated models if necessary.
|
85
|
-
@associated_models.each do |associated_model, options|
|
86
|
-
next if not options
|
87
|
-
if options[:polymorphic]
|
88
|
-
# There should be a <associated_model>_type already.
|
89
|
-
@index_page[:shows][:attributes] << "#{associated_model}.#{options[:choose_by]}"
|
90
|
-
@index_page[:searches][:attributes].each do |attribute|
|
91
|
-
next unless attribute[:name] == :"#{associated_model}_type"
|
92
|
-
attribute[:options] = options[:available_models]
|
93
|
-
end
|
94
|
-
else
|
95
|
-
@index_page[:searches][:attributes] << {
|
96
|
-
name: "#{associated_model}.#{options[:choose_by]}",
|
97
|
-
term: :cont
|
98
|
-
}
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
3
|
class << self
|
105
4
|
def model name, &block
|
106
5
|
name = name.to_sym
|
107
6
|
if block_given?
|
108
7
|
@@models ||= {}
|
109
8
|
spec = @@models[name] || ModelSpec.new
|
9
|
+
spec.name = name
|
110
10
|
yield spec
|
111
11
|
@@models[name] = spec
|
112
12
|
else
|
@@ -118,12 +18,9 @@ module Carload
|
|
118
18
|
model_a = options.keys.first
|
119
19
|
model_b = options.values.first
|
120
20
|
options.shift
|
21
|
+
options[:name] = model_b
|
121
22
|
@@models[model_a] ||= ModelSpec.new
|
122
23
|
@@models[model_a].associated_models[model_b] = options
|
123
|
-
unless options[:polymorphic]
|
124
|
-
@@models[model_b] ||= ModelSpec.new
|
125
|
-
@@models[model_b].associated_models[model_a] = nil
|
126
|
-
end
|
127
24
|
end
|
128
25
|
|
129
26
|
def models
|
@@ -153,10 +50,10 @@ module Carload
|
|
153
50
|
RUBY
|
154
51
|
default = false
|
155
52
|
next if spec.associated_models.empty?
|
156
|
-
spec.associated_models.
|
157
|
-
next
|
53
|
+
spec.associated_models.each_value do |associated_model|
|
54
|
+
next unless associated_model[:choose_by]
|
158
55
|
content << <<-RUBY
|
159
|
-
associate(#{{ name.to_sym => associated_model.to_sym }
|
56
|
+
associate(#{{ name.to_sym => associated_model[:name], choose_by: associated_model[:choose_by].to_sym }})
|
160
57
|
RUBY
|
161
58
|
end
|
162
59
|
end
|
data/lib/carload/engine.rb
CHANGED
@@ -8,5 +8,54 @@ module Carload
|
|
8
8
|
require_dependency file
|
9
9
|
end
|
10
10
|
end
|
11
|
+
|
12
|
+
config.after_initialize do
|
13
|
+
# Fill up associations of models.
|
14
|
+
Dashboard.models.each do |name, spec|
|
15
|
+
spec.klass = name.to_s.camelize.constantize
|
16
|
+
spec.klass.reflect_on_all_associations.each do |association|
|
17
|
+
spec.handle_association association, rescue: true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
# Reopen model classes to add pg text search.
|
21
|
+
Dictionaries = {
|
22
|
+
en: 'english',
|
23
|
+
:'zh-CN' => 'zhparser'
|
24
|
+
}.freeze
|
25
|
+
Dashboard.models.each do |name, spec|
|
26
|
+
# NOTE: Only direct columns are included.
|
27
|
+
attributes = spec.index_page.searches.attributes.select { |x| x[:name].class == Symbol }.map { |x| x[:name] }
|
28
|
+
spec.klass.class_eval do
|
29
|
+
include PgSearch
|
30
|
+
pg_search_scope :search,
|
31
|
+
against: attributes,
|
32
|
+
using: {
|
33
|
+
tsearch: {
|
34
|
+
prefix: true,
|
35
|
+
negation: true,
|
36
|
+
dictionary: Dictionaries[I18n.locale]
|
37
|
+
}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
# Reopen model classes to handle polymorphic association.
|
42
|
+
Dashboard.models.each do |name, spec|
|
43
|
+
spec.associated_models.values.select { |x| x[:polymorphic] == true }.each do |associated_model|
|
44
|
+
polymorphic_objects = []
|
45
|
+
associated_model[:instance_models].each do |instance_model|
|
46
|
+
Dashboard.model(instance_model).klass.all.each do |object|
|
47
|
+
polymorphic_objects << ["#{object.class} - #{object.send(associated_model[:choose_by])}", "#{object.id},#{object.class}"]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
spec.klass.class_eval do
|
51
|
+
class_eval <<-RUBY
|
52
|
+
def self.#{associated_model[:name].to_s.pluralize}
|
53
|
+
#{polymorphic_objects}
|
54
|
+
end
|
55
|
+
RUBY
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
11
60
|
end
|
12
61
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Carload
|
2
|
+
class ModelSpec
|
3
|
+
include AssociationPipelines
|
4
|
+
|
5
|
+
attr_accessor :name, :klass, :default, :attributes, :index_page, :associated_models
|
6
|
+
|
7
|
+
SkippedAttributes = [
|
8
|
+
'id', 'created_at', 'updated_at',
|
9
|
+
'encrypted_password', 'reset_password_token',
|
10
|
+
'reset_password_sent_at', 'remember_created_at',
|
11
|
+
'sign_in_count', 'current_sign_in_at',
|
12
|
+
'last_sign_in_at', 'current_sign_in_ip',
|
13
|
+
'last_sign_in_ip'
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
AssociationTypes = {
|
17
|
+
ActiveRecord::Reflection::BelongsToReflection => :belongs_to,
|
18
|
+
ActiveRecord::Reflection::HasOneReflection => :has_one,
|
19
|
+
ActiveRecord::Reflection::HasManyReflection => :has_many,
|
20
|
+
ActiveRecord::Reflection::ThroughReflection => :through
|
21
|
+
}
|
22
|
+
|
23
|
+
def initialize model_class = nil
|
24
|
+
@default = false
|
25
|
+
@attributes = ExtendedHash.new
|
26
|
+
@index_page = ExtendedHash.new
|
27
|
+
@index_page[:shows] = ExtendedHash.new
|
28
|
+
@index_page[:shows][:attributes] ||= []
|
29
|
+
@index_page[:searches] = ExtendedHash.new
|
30
|
+
@index_page[:searches][:attributes] ||= []
|
31
|
+
@associated_models = {}
|
32
|
+
if model_class
|
33
|
+
@name = model_class.name.underscore
|
34
|
+
@klass = model_class
|
35
|
+
@attributes[:permitted] = (model_class.column_names - SkippedAttributes).map(&:to_sym)
|
36
|
+
# Handle model associations.
|
37
|
+
model_class.reflect_on_all_associations.each do |association|
|
38
|
+
handle_association association
|
39
|
+
end
|
40
|
+
@attributes[:permitted].each do |attribute|
|
41
|
+
next if attribute.class == Hash
|
42
|
+
@index_page[:shows][:attributes] << attribute
|
43
|
+
@index_page[:searches][:attributes] << { name: attribute.to_sym, term: :cont }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def changed? spec
|
49
|
+
not @default == spec.default or
|
50
|
+
not @attributes[:permitted] == spec.attributes[:permitted] or
|
51
|
+
not @index_page[:shows][:attributes] == spec.index_page[:shows][:attributes] or
|
52
|
+
not @index_page[:searches][:attributes] == spec.index_page[:searches][:attributes]
|
53
|
+
end
|
54
|
+
|
55
|
+
def revise!
|
56
|
+
# Handle associated models if necessary.
|
57
|
+
@associated_models.each_value do |associated_model|
|
58
|
+
next unless associated_model[:choose_by]
|
59
|
+
if associated_model[:association_type] == :has_many
|
60
|
+
show_name = [:pluck, associated_model[:name].to_s.pluralize.to_sym, associated_model[:choose_by]]
|
61
|
+
else
|
62
|
+
show_name = "#{associated_model[:name]}.#{associated_model[:choose_by]}"
|
63
|
+
end
|
64
|
+
@index_page[:shows][:attributes].delete_if { |x| x == :"#{associated_model[:name]}_id" }
|
65
|
+
@index_page[:shows][:attributes] << show_name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_association association, options = {}
|
70
|
+
begin
|
71
|
+
_association = (association.delegate_reflection rescue nil) || association
|
72
|
+
name = (_association.klass.name.underscore.to_sym rescue nil) || _association.name
|
73
|
+
association_type = AssociationTypes[_association.class]
|
74
|
+
polymorphic = association.options[:polymorphic] || association.options[:as]
|
75
|
+
foreign_key = @klass.column_names.include?("#{(_association.klass.name.underscore rescue nil) || _association.name}_id")
|
76
|
+
join_table = association.options[:through].to_s.singularize.to_sym if association.options[:through]
|
77
|
+
@associated_models[name] = {
|
78
|
+
name: name,
|
79
|
+
association_type: association_type,
|
80
|
+
polymorphic: polymorphic,
|
81
|
+
foreign_key: foreign_key,
|
82
|
+
join_table: join_table,
|
83
|
+
choose_by: nil
|
84
|
+
}.merge @associated_models[name] || {}
|
85
|
+
association_pipelines.each { |pipeline| send pipeline, association }
|
86
|
+
# Delete join-table model!
|
87
|
+
if association.options[:through]
|
88
|
+
@associated_models.delete association.options[:through]
|
89
|
+
@associated_models.delete association.options[:through].to_s.singularize.to_sym
|
90
|
+
end
|
91
|
+
rescue => e
|
92
|
+
raise e unless options[:rescue]
|
93
|
+
raise e if not e&.original_exception&.class == PG::UndefinedTable and
|
94
|
+
not e.class == ActiveRecord::NoDatabaseError
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/carload/version.rb
CHANGED
data/lib/carload.rb
CHANGED
@@ -4,13 +4,17 @@ Gem.loaded_specs['carload'].dependencies.each do |dependency|
|
|
4
4
|
end
|
5
5
|
|
6
6
|
require 'carload/extended_hash'
|
7
|
+
require 'carload/association_pipelines'
|
8
|
+
require 'carload/model_spec'
|
7
9
|
require 'carload/dashboard'
|
8
10
|
require 'carload/engine'
|
9
11
|
require 'carload/exceptions'
|
10
12
|
|
11
13
|
module Carload
|
12
14
|
def self.setup &block
|
15
|
+
# Read in configuration.
|
13
16
|
@@config = ExtendedHash.new
|
17
|
+
@@config[:page] = ExtendedHash.new
|
14
18
|
@@config[:dashboard] = ExtendedHash.new
|
15
19
|
@@config[:dashboard][:permits_user] = ExtendedHash.new
|
16
20
|
yield @@config
|
@@ -20,7 +24,7 @@ module Carload
|
|
20
24
|
if not [:carrierwave].include? @@config[:upload_solution]
|
21
25
|
raise UnsupportedError.new("upload solution #{@@config[:upload_solution]}")
|
22
26
|
end
|
23
|
-
|
27
|
+
# Define configuation helpers.
|
24
28
|
@@config.each do |key, value|
|
25
29
|
define_singleton_method key do
|
26
30
|
value
|
@@ -8,34 +8,35 @@ module Carload
|
|
8
8
|
model_specs = {}
|
9
9
|
Rails.application.eager_load! # It is necessary to load models manually.
|
10
10
|
ActiveRecord::Base.descendants.each do |model| # Rails 5 can use ApplicationRecord.
|
11
|
-
next if model.name == 'ApplicationRecord'
|
11
|
+
next if model.name == 'ApplicationRecord' or model.name == 'PgSearch::Document'
|
12
12
|
name = model.name.underscore
|
13
|
-
model_specs[name] =
|
13
|
+
model_specs[name] = ModelSpec.new model
|
14
14
|
end
|
15
15
|
spec = model_specs[model]
|
16
16
|
if not spec.associated_models.empty?
|
17
|
-
spec.revise_stage_1!
|
18
17
|
cli = HighLine.new
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
attributes.
|
25
|
-
|
26
|
-
|
18
|
+
spec.associated_models.each_value do |associated_model|
|
19
|
+
next if associated_model[:attributes].empty?
|
20
|
+
if associated_model[:polymorphic]
|
21
|
+
attributes = associated_model[:attributes]
|
22
|
+
else
|
23
|
+
attributes = model_specs[associated_model[:name].to_s].attributes.permitted.select { |x| x.class != Hash }
|
24
|
+
end
|
25
|
+
if attributes.size == 1
|
26
|
+
associated_model[:choose_by] = attributes.first
|
27
|
+
else
|
28
|
+
associated_model[:choose_by] = cli.choose do |menu|
|
29
|
+
menu.prompt = "Choose the attribute of model #{associated_model[:name]} for choosing in #{model}? "
|
30
|
+
attributes.each do |attribute|
|
31
|
+
next if attribute.to_s =~ /_id$/
|
32
|
+
menu.choice attribute
|
33
|
+
end
|
27
34
|
end
|
28
35
|
end
|
29
36
|
end
|
30
|
-
spec.
|
37
|
+
spec.revise!
|
31
38
|
end
|
32
39
|
# Check if model exists in dashboard file, but it may be changed.
|
33
|
-
begin
|
34
|
-
load 'app/carload/dashboard.rb'
|
35
|
-
rescue LoadError
|
36
|
-
Dashboard.models[model.to_sym] = spec
|
37
|
-
Dashboard.write 'app/carload/dashboard.rb'
|
38
|
-
end
|
39
40
|
if not Dashboard.models.keys.include? model.to_sym or Dashboard.model(model).changed? spec
|
40
41
|
Dashboard.models[model.to_sym] = spec
|
41
42
|
Dashboard.write 'app/carload/dashboard.rb'
|
@@ -26,5 +26,15 @@ require 'carload'
|
|
26
26
|
def copy_dashboard_file
|
27
27
|
copy_file 'dashboard.rb', 'app/carload/dashboard.rb'
|
28
28
|
end
|
29
|
+
|
30
|
+
def copy_migration_files
|
31
|
+
# Copy migrations if necessary.
|
32
|
+
case I18n.locale
|
33
|
+
when :'zh-CN'
|
34
|
+
['20161030074822_carload_enable_zhparser_extension.rb'].each do |file|
|
35
|
+
copy_file "#{Carload::Engine.root}/db/migrate/#{file}", "db/migrate/#{file}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
@@ -1,8 +1,20 @@
|
|
1
1
|
Carload.setup do |config|
|
2
|
-
#
|
2
|
+
# Set the title that will be displayed on the browser tab area.
|
3
|
+
config.page.title = nil
|
4
|
+
|
5
|
+
# Set the footer text that will be displayed on each page.
|
6
|
+
config.page.footer = nil
|
7
|
+
|
8
|
+
# Set the colors of page elements.
|
9
|
+
config.page.main_color = nil
|
10
|
+
config.page.text_color = 'black'
|
11
|
+
config.page.button_color = nil
|
12
|
+
config.page.button_text_color = nil
|
13
|
+
|
14
|
+
# Set which authentication solution is used. Currently, we only support Devise.
|
3
15
|
config.auth_solution = :devise
|
4
16
|
|
5
|
-
#
|
17
|
+
# Set which file upload solution is used. Currently, we only support Carrierwave.
|
6
18
|
config.upload_solution = :carrierwave
|
7
19
|
|
8
20
|
# Set the actions used to discern user's permission to access dashboard.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: carload
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Li Dong
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -129,19 +129,19 @@ dependencies:
|
|
129
129
|
- !ruby/object:Gem::Version
|
130
130
|
version: '3.3'
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
|
-
name:
|
132
|
+
name: pg_search
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
134
134
|
requirements:
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: '1.
|
137
|
+
version: '1.0'
|
138
138
|
type: :runtime
|
139
139
|
prerelease: false
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: '1.
|
144
|
+
version: '1.0'
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
146
|
name: kaminari
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -227,7 +227,7 @@ files:
|
|
227
227
|
- app/assets/javascripts/carload/dashboard.js
|
228
228
|
- app/assets/stylesheets/carload/_select2.scss
|
229
229
|
- app/assets/stylesheets/carload/application.scss
|
230
|
-
- app/assets/stylesheets/carload/colors.scss
|
230
|
+
- app/assets/stylesheets/carload/colors.scss.erb
|
231
231
|
- app/assets/stylesheets/carload/common.scss
|
232
232
|
- app/assets/stylesheets/carload/dashboard.scss
|
233
233
|
- app/assets/stylesheets/carload/error.scss
|
@@ -264,11 +264,14 @@ files:
|
|
264
264
|
- config/locales/simple_form.en.yml
|
265
265
|
- config/locales/zh-CN.yml
|
266
266
|
- config/routes.rb
|
267
|
+
- db/migrate/20161030074822_carload_enable_zhparser_extension.rb
|
267
268
|
- lib/carload.rb
|
269
|
+
- lib/carload/association_pipelines.rb
|
268
270
|
- lib/carload/dashboard.rb
|
269
271
|
- lib/carload/engine.rb
|
270
272
|
- lib/carload/exceptions.rb
|
271
273
|
- lib/carload/extended_hash.rb
|
274
|
+
- lib/carload/model_spec.rb
|
272
275
|
- lib/carload/version.rb
|
273
276
|
- lib/generators/carload/USAGE
|
274
277
|
- lib/generators/carload/dash_generator.rb
|