sofav 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +35 -0
- data/Rakefile +34 -0
- data/app/builders/admin/form_builder.rb +250 -0
- data/app/controllers/application_controller.rb +23 -0
- data/app/decorators/base_decorator.rb +172 -0
- data/app/helpers/admin/core_helper.rb +237 -0
- data/app/helpers/admin/show_view_helper.rb +69 -0
- data/app/helpers/admin/tags_helper.rb +104 -0
- data/app/services/active_record/search_service.rb +121 -0
- data/app/views/admin/application/_breadcrumb_nav.html.erb +10 -0
- data/app/views/admin/application/_collection.html.erb +79 -0
- data/app/views/admin/application/_save_js.js.erb +20 -0
- data/app/views/admin/application/_sidebar.html.erb +62 -0
- data/app/views/admin/application/_topbar.html.erb +32 -0
- data/app/views/admin/application/actions/charge.html.erb +3 -0
- data/app/views/admin/application/actions/destroy.html.erb +5 -0
- data/app/views/admin/application/actions/edit.html.erb +1 -0
- data/app/views/admin/application/actions/edit_sights.html.erb +1 -0
- data/app/views/admin/application/actions/feedback.html.erb +4 -0
- data/app/views/admin/application/actions/index_charge.html.erb +1 -0
- data/app/views/admin/application/actions/index_destroy.html.erb +1 -0
- data/app/views/admin/application/actions/index_edit.html.erb +1 -0
- data/app/views/admin/application/actions/index_edit_sights.html.erb +1 -0
- data/app/views/admin/application/actions/index_show.html.erb +1 -0
- data/app/views/admin/application/actions/new.html.erb +1 -0
- data/app/views/admin/application/create.js.erb +1 -0
- data/app/views/admin/application/destroy.js.erb +6 -0
- data/app/views/admin/application/edit.html.erb +18 -0
- data/app/views/admin/application/index.html.erb +23 -0
- data/app/views/admin/application/new.html.erb +18 -0
- data/app/views/admin/application/partials/_map_pos_picker.html.erb +101 -0
- data/app/views/admin/application/partials/_sort_script.html.erb +32 -0
- data/app/views/admin/application/show.html.erb +16 -0
- data/app/views/admin/application/update.js.erb +1 -0
- data/app/views/admin/dashboard/index.html.erb +8 -0
- data/app/views/layouts/admin.html.erb +30 -0
- data/config/locales/devise.en.yml +64 -0
- data/config/locales/devise.zh-CN.yml +120 -0
- data/config/locales/doorkeeper.en.yml +124 -0
- data/config/locales/doorkeeper.zh-CN.yml +132 -0
- data/config/locales/en.yml +23 -0
- data/config/locales/enumerize/defaults.zh-CN.yml +7 -0
- data/config/locales/kaminari.zh-CN.yml +17 -0
- data/config/locales/rails.zh-CN.yml +204 -0
- data/config/locales/views/actions.zh-CN.yml +21 -0
- data/config/locales/views/attributes.zh-CN.yml +15 -0
- data/config/locales/views/breadcrumb.zh-CN.yml +5 -0
- data/config/locales/views/common.zh-CN.yml +7 -0
- data/config/locales/views/enums.zh-CN.yml +13 -0
- data/config/locales/views/profiles.zh-CN.yml +3 -0
- data/lib/generators/sofav/USAGE +0 -0
- data/lib/generators/sofav/sofav_generator.rb +50 -0
- data/lib/generators/sofav/templates/activerecord.zh-CN.yml +4 -0
- data/lib/generators/sofav/templates/attribute_types.zh-CN.yml +2 -0
- data/lib/sofav.rb +6 -0
- data/lib/sofav/decorator.rb +47 -0
- data/lib/sofav/local.rb +52 -0
- data/lib/sofav/version.rb +3 -0
- data/lib/tasks/sofa_tasks.rake +4 -0
- data/vendor/assets/javascripts/admin/here.js +0 -0
- data/vendor/assets/javascripts/bootbox.js +1020 -0
- data/vendor/assets/javascripts/bootstrap.js +2377 -0
- data/vendor/assets/javascripts/jquery-ui.js +5169 -0
- data/vendor/assets/javascripts/select2.js +5725 -0
- data/vendor/assets/stylesheets/bootstrap.css +6800 -0
- data/vendor/assets/stylesheets/bootstrap.css.map +1 -0
- data/vendor/assets/stylesheets/jquery-ui.css +453 -0
- data/vendor/assets/stylesheets/select2.css +484 -0
- metadata +211 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e60d0d8ba1013b0ea2c8d3cfa78c2d6243568a89
|
4
|
+
data.tar.gz: f7fd74fee86b39adbbda912402fdba14bacc4f6a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2bebb18fb8e41a8ed3dc0a41e8a37b0c18791f0339366d3652678ab8ff303b45ae2cc27072c257bd90295d2c9e5d4895858032591ccf09cd49daa03e99fff12f
|
7
|
+
data.tar.gz: 8ce5c387a0096a5b9a97d4c333644d6e30ab542a6de1ac77c051c4cce34cd4b6d7be963e61a4982a6db2902e067808330df48bd3a4929a30576e4b7ba8b6e7b2
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 yangdiei
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Sofa
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
rails generate sofav NAME [field:type field:type] [options]
|
6
|
+
```
|
7
|
+
rails g sofav User name:string age:integer
|
8
|
+
```
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'sofav'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
```bash
|
19
|
+
$ bundle
|
20
|
+
```
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
```bash
|
24
|
+
$ gem install sofa
|
25
|
+
```
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
|
34
|
+
## License
|
35
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Sofav'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,250 @@
|
|
1
|
+
module Admin
|
2
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
3
|
+
delegate :model, :decorator, :decorate, :record, :records, :concat, to: :@template
|
4
|
+
|
5
|
+
def label_tag(method, options = {})
|
6
|
+
label_class = Array(options[:label_class]) || []
|
7
|
+
label_class << ['control-label', options[:block] ? 'col-sm-2' : 'col-sm-4'] if label_class.blank?
|
8
|
+
label_class << 'required' if options[:required]
|
9
|
+
@template.content_tag(:label, model.human_attribute_name(method), class: label_class.flatten)
|
10
|
+
end
|
11
|
+
|
12
|
+
def input_dom_id(field_name)
|
13
|
+
'_form_id_'.concat(field_name.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def field_wrapper(method, options = {}, &block)
|
17
|
+
label_options = options.extract!(:label_class, :required, :block)
|
18
|
+
is_block = label_options[:block]
|
19
|
+
show_label = options.delete(:show_label)
|
20
|
+
options[:class] ||= 'form-control'
|
21
|
+
options.merge!(id: input_dom_id(method))
|
22
|
+
|
23
|
+
@template.content_tag(:li, class: is_block ? 'col-sm-12' : 'col-sm-6') do
|
24
|
+
@template.content_tag(:div, class: 'form-group') do
|
25
|
+
content = (show_label.nil? || show_label) ? [label_tag(method, label_options)] : [@template.content_tag(:div, class: 'col-sm-2'){}]
|
26
|
+
content << @template.content_tag(:div, yield(block), class: is_block ? 'col-sm-10' : 'col-sm-8')
|
27
|
+
content.join.html_safe
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def admin_nested_fields(method, options = {})
|
33
|
+
field_wrapper(method, options) do
|
34
|
+
fields_content = []
|
35
|
+
fields_content << @template.content_tag(:div, id: "#{method}_nested_form") do
|
36
|
+
nested_content = []
|
37
|
+
nested_content << self.fields_for(method.to_sym) do |ff|
|
38
|
+
@template.render("#{method.to_s.singularize}_fields", f: ff)
|
39
|
+
end
|
40
|
+
|
41
|
+
nested_content << @template.content_tag(:div, class: 'col-sm-12') do
|
42
|
+
btns_content = [@template.link_to_add_association('添加', self, method.to_sym, class: 'btn btn-sm btn-success'), " "]
|
43
|
+
btns_content << @template.button_tag(:input, class: "sort-btn btn btn-sm btn-success", type: "button", id: "#{method}-sort-btn") { "排序" }
|
44
|
+
btns_content.join.html_safe
|
45
|
+
end
|
46
|
+
|
47
|
+
nested_content.join.html_safe
|
48
|
+
end
|
49
|
+
|
50
|
+
fields_content << @template.render("admin/application/partials/sort_script",
|
51
|
+
sort_btn_id: "##{method}-sort-btn",
|
52
|
+
container_id: "##{method}_nested_form",
|
53
|
+
resource_parts: method.pluralize)
|
54
|
+
|
55
|
+
fields_content << @template.content_tag(:script) do
|
56
|
+
%(
|
57
|
+
$(document).on('turbolinks:load', function () {
|
58
|
+
Cocoon();
|
59
|
+
});
|
60
|
+
).html_safe
|
61
|
+
end
|
62
|
+
fields_content.join.html_safe
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def admin_render_field(method, options = {})
|
67
|
+
field_wrapper(method, options) do
|
68
|
+
@template.render("#{method.to_s.singularize}", f: self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def admin_display_field(method, options = {})
|
73
|
+
field_wrapper(method, options) do
|
74
|
+
@template.content_tag(:span, record.try(method))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def admin_text_field(method, options = {})
|
79
|
+
field_wrapper(method, options) do
|
80
|
+
@template.text_field(@object_name, method, objectify_options(options))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def admin_password_field(method, options = {})
|
85
|
+
field_wrapper(method, options) do
|
86
|
+
@template.password_field(@object_name, method, objectify_options(options))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def admin_number_field(method, options = {})
|
91
|
+
field_wrapper(method, options) do
|
92
|
+
@template.number_field(@object_name, method, objectify_options(options))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def admin_generic_field(type, method, options = {})
|
97
|
+
field_wrapper(method, options) do
|
98
|
+
send type, @object_name, method, objectify_options(options)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def admin_file_field(method, options = {})
|
103
|
+
file_url = record.try(method)
|
104
|
+
file_url = file_url.class.superclass == CarrierWave::Uploader::Base ? file_url.try('url') : file_url
|
105
|
+
|
106
|
+
field_wrapper(method, options) do
|
107
|
+
content = [@template.file_field(@object_name, method, objectify_options(options))]
|
108
|
+
content << @template.link_to(file_url, target: '_blank') {"点击查看"}
|
109
|
+
content.join.html_safe
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def admin_image_field(method, options = {})
|
114
|
+
field_wrapper(method, options) do
|
115
|
+
img_url = record.try(method)
|
116
|
+
img_url = img_url.class.superclass == CarrierWave::Uploader::Base ? img_url.try('url') : img_url
|
117
|
+
content = [@template.link_to(img_url, target: '_blank') do
|
118
|
+
decorate.respond_to?("human_#{method}") ? decorate.human(method) : @template.image_tag(img_url)
|
119
|
+
end]
|
120
|
+
content << @template.file_field(@object_name, method, objectify_options(options))
|
121
|
+
content.join.html_safe
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def admin_collection_check_boxes(method, options = {})
|
126
|
+
collection, value_method = options.delete(:collection), options.delete(:value_method)
|
127
|
+
text_method, html_options = options.delete(:text_method), options.delete(:html_options) || {}
|
128
|
+
field_wrapper(method, options) do
|
129
|
+
@template.collection_check_boxes(@object_name, method, eval(collection), value_method, text_method, objectify_options(options), @default_options.merge(html_options)) do |b|
|
130
|
+
@template.content_tag(:span) { [b.check_box, b.text].join(' ').html_safe }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def admin_collection_select2(method, options = {})
|
136
|
+
collection, value_method = options.delete(:collection), options.delete(:value_method)
|
137
|
+
text_method, html_options = options.delete(:text_method), options.delete(:html_options) || {}
|
138
|
+
is_array = options.delete(:is_array)
|
139
|
+
select2_options = options.delete(:select2_options)
|
140
|
+
options.merge!(prompt: true)
|
141
|
+
html_options.merge!(options.extract!(:label_class, :required, :block))
|
142
|
+
html_options.merge!(name: "#{@object_name}[#{method}][]") if is_array
|
143
|
+
sel_scripts = field_wrapper(method, html_options) do
|
144
|
+
@template.collection_select(@object_name, method, eval(collection), value_method, text_method, objectify_options(options), @default_options.merge(html_options))
|
145
|
+
end
|
146
|
+
select2_scripts = @template.content_tag(:script) { "$('##{input_dom_id(method)}').select2(#{select2_options.to_json});".html_safe }
|
147
|
+
sel_scripts.concat(select2_scripts)
|
148
|
+
end
|
149
|
+
|
150
|
+
def admin_collection_select(method, options = {})
|
151
|
+
collection, value_method = options.delete(:collection), options.delete(:value_method)
|
152
|
+
text_method, html_options = options.delete(:text_method), options.delete(:html_options) || {}
|
153
|
+
is_array = options.delete(:is_array)
|
154
|
+
options.merge!(prompt: true)
|
155
|
+
html_options.merge!(options.extract!(:label_class, :required, :block))
|
156
|
+
html_options.merge!(name: "#{@object_name}[#{method}][]") if is_array
|
157
|
+
field_wrapper(method, html_options) do
|
158
|
+
@template.collection_select(@object_name, method, eval(collection), value_method, text_method, objectify_options(options), @default_options.merge(html_options))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def admin_text_area(method, options = {})
|
163
|
+
field_wrapper(method, options) do
|
164
|
+
@template.text_area(@object_name, method, objectify_options(options))
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def admin_date_select(method, options = {})
|
169
|
+
field_wrapper(method, options) do
|
170
|
+
options[:class] = [options[:class], 'datepicker'].flatten
|
171
|
+
@template.text_field(@object_name, method, objectify_options(options))
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def admin_datetime_select(method, options = {})
|
176
|
+
field_wrapper(method, options) do
|
177
|
+
options[:class] = [options[:class], 'datetimepicker'].flatten
|
178
|
+
@template.text_field(@object_name, method, objectify_options(options))
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def admin_collection_radio_buttons(method, options = {})
|
183
|
+
collection, value_method = options.delete(:collection), options.delete(:value_method)
|
184
|
+
text_method, html_options = options.delete(:text_method), options.delete(:html_options) || {}
|
185
|
+
field_wrapper(method, options) do
|
186
|
+
@template.collection_radio_buttons(@object_name, method, eval(collection), value_method, text_method, objectify_options(options), @default_options.merge(html_options)) do |b|
|
187
|
+
@template.content_tag(:span, style: 'height: 34px;display: inline-block;') { [b.radio_button, b.text].join(' ').html_safe }
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def build_form_for(decorator, *attribute_name_list)
|
193
|
+
fields_hash = []
|
194
|
+
attribute_name_list.each do |attribute_name|
|
195
|
+
fields_hash = decorator.try(attribute_name)
|
196
|
+
break if !fields_hash.empty?
|
197
|
+
end
|
198
|
+
if fields_hash.empty?
|
199
|
+
fields_hash = decorator.try(:new_attributes)
|
200
|
+
end
|
201
|
+
profiles = fields_hash.fetch(:profiles, [])
|
202
|
+
|
203
|
+
form_fields = if profiles.present?
|
204
|
+
profiles.collect { |profile|
|
205
|
+
fields = fields_hash.fetch(profile.to_sym, {})
|
206
|
+
content = []
|
207
|
+
content << @template.content_tag(:h3, I18n.t("profiles.#{profile}", default: profile), class: 'lead clearfix')
|
208
|
+
content << build_fields_for(decorator, fields)
|
209
|
+
content.join.html_safe
|
210
|
+
}.join.html_safe
|
211
|
+
else
|
212
|
+
build_fields_for(decorator, fields_hash)
|
213
|
+
end
|
214
|
+
[form_fields, build_form_buttons].join.html_safe
|
215
|
+
end
|
216
|
+
|
217
|
+
def build_fields_for(decorator, fields)
|
218
|
+
@template.content_tag(:ul, class: 'row list-unstyled') do
|
219
|
+
fields.each do |field_name|
|
220
|
+
options = decorator.try(:attribute_type_for, field_name, true) || {type: 'text_field', required: false}
|
221
|
+
type = options.delete(:type)
|
222
|
+
|
223
|
+
if self.respond_to?('admin_'.concat(type))
|
224
|
+
concat(send('admin_'.concat(type), field_name, options))
|
225
|
+
else
|
226
|
+
concat admin_generic_field(type, field_name, options)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def build_form_buttons
|
233
|
+
lead = @template.content_tag(:h3, nil, class: 'lead clearfix')
|
234
|
+
tags = @template.content_tag(:div, [submit_tag, ' ', reset_tag].join.html_safe, class: 'col-sm-12')
|
235
|
+
lead.concat(@template.content_tag(:div, tags, class: 'row'))
|
236
|
+
end
|
237
|
+
|
238
|
+
def submit_tag
|
239
|
+
tag_text = @template.content_tag(:i, nil, class: 'glyphicon glyphicon-check')
|
240
|
+
tag_text.concat(@template.content_tag(:span, '提交'))
|
241
|
+
@template.content_tag(:button, tag_text, class: 'btn btn-icon btn-info', type: 'submit')
|
242
|
+
end
|
243
|
+
|
244
|
+
def reset_tag
|
245
|
+
tag_text = @template.content_tag(:i, nil, class: 'glyphicon glyphicon-repeat')
|
246
|
+
tag_text.concat(@template.content_tag(:span, '重置'))
|
247
|
+
@template.content_tag(:button, tag_text, class: 'btn btn-icon btn-warning', type: 'reset')
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Admin
|
2
|
+
class ApplicationController < ::ApplicationController
|
3
|
+
layout "admin"
|
4
|
+
|
5
|
+
include Admin::ActionController
|
6
|
+
include Admin::CommonActions
|
7
|
+
include ApplicationHelper
|
8
|
+
|
9
|
+
helper_method :scope, :model, :decorator, :record, :decorate, :records, :render_to_string
|
10
|
+
|
11
|
+
before_action :authenticate_user!
|
12
|
+
before_action do
|
13
|
+
if current_user.user? or current_user.role.blank?
|
14
|
+
render :file => "public/403.html", :status => :unauthorized, :layout => false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
before_action do
|
19
|
+
prepend_view_path 'app/views/admin/application'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
class BaseDecorator < Draper::Decorator
|
2
|
+
extend Dry::Configurable
|
3
|
+
|
4
|
+
# 对应的model class
|
5
|
+
setting :model
|
6
|
+
|
7
|
+
DEFINITIONS = {
|
8
|
+
# 属性定义列表
|
9
|
+
attribute_types: {},
|
10
|
+
|
11
|
+
# index page用到的字段/筛选/搜索
|
12
|
+
collection_filters: [],
|
13
|
+
collection_search: {},
|
14
|
+
collection_attributes: [],
|
15
|
+
|
16
|
+
# new/edit page用到的字段列表
|
17
|
+
form_attributes: [],
|
18
|
+
edit_form_attributes: [],
|
19
|
+
|
20
|
+
# show page 用到的字段列表
|
21
|
+
show_page_attributes: [],
|
22
|
+
|
23
|
+
# class/instance 操作列表
|
24
|
+
permitted_class_methods: {},
|
25
|
+
permitted_instance_methods: {},
|
26
|
+
|
27
|
+
extra_permitted_attributes: {}
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
DEFINITIONS.each do |key, default_value|
|
31
|
+
setting key, default_value
|
32
|
+
delegate key, to: :class
|
33
|
+
end
|
34
|
+
|
35
|
+
def human(field)
|
36
|
+
value = try("human_#{field}") || try(field) || object.try("#{field}_text") || object.try(field)
|
37
|
+
value = field.split('.').inject(object) {
|
38
|
+
|result, method| result.try(method)
|
39
|
+
} if field.include?('.')
|
40
|
+
|
41
|
+
case value
|
42
|
+
when ActiveSupport::TimeWithZone
|
43
|
+
value.strftime('%Y-%m-%d %H:%M:%S')
|
44
|
+
when TrueClass
|
45
|
+
I18n.t("#{field}.true_html", default: '-').html_safe
|
46
|
+
when FalseClass
|
47
|
+
I18n.t("#{field}.false_html", default: '-').html_safe
|
48
|
+
else
|
49
|
+
value.presence || '-'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
delegate :model, to: :config
|
55
|
+
|
56
|
+
def inherited(subclass)
|
57
|
+
super
|
58
|
+
|
59
|
+
subclass.configure do |config|
|
60
|
+
config.model = subclass.name.remove(/Decorator\Z/).singularize.safe_constantize
|
61
|
+
|
62
|
+
scope = config.model.model_name.singular
|
63
|
+
config.attribute_types = I18n.t(['attribute_types', scope].join('.'), default: {}).deep_dup
|
64
|
+
I18n.t(['decorator', scope].join('.'), default: {}).map { |key, value| config.send("#{key}=", value) }
|
65
|
+
|
66
|
+
config.model.columns_hash.each do |name, col|
|
67
|
+
name = name.to_sym
|
68
|
+
if config.attribute_types[name].nil?
|
69
|
+
if col.array
|
70
|
+
config.attribute_types[name] = {
|
71
|
+
type: 'collection_select',
|
72
|
+
collection: '[]',
|
73
|
+
text_method: :last,
|
74
|
+
value_method: :first,
|
75
|
+
}
|
76
|
+
else
|
77
|
+
case col.type
|
78
|
+
when 'integer', 'float'
|
79
|
+
config.attribute_types[name] = {
|
80
|
+
type: 'number_field',
|
81
|
+
required: false
|
82
|
+
}
|
83
|
+
when 'datetime'
|
84
|
+
config.attribute_types[name] = {
|
85
|
+
type: 'datetime_select',
|
86
|
+
required: false
|
87
|
+
}
|
88
|
+
when 'boolean'
|
89
|
+
config.attribute_types[name] = {
|
90
|
+
type: 'collection_check_boxes',
|
91
|
+
required: false
|
92
|
+
}
|
93
|
+
when 'date'
|
94
|
+
config.attribute_types[name] = {
|
95
|
+
type: 'date_select',
|
96
|
+
required: false
|
97
|
+
}
|
98
|
+
else
|
99
|
+
config.attribute_types[name] = {
|
100
|
+
type: 'text_field',
|
101
|
+
required: false
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# 设置对应的class
|
113
|
+
def decorate_to(klass)
|
114
|
+
config.model = klass
|
115
|
+
end
|
116
|
+
|
117
|
+
DEFINITIONS.keys.each do |method_name|
|
118
|
+
define_method(method_name) { |&block|
|
119
|
+
if block
|
120
|
+
config.send("#{method_name}=", block.call)
|
121
|
+
else
|
122
|
+
config.send(method_name)
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
# 获取某个属性的定义
|
128
|
+
def attribute_type_for(attribute_name, quiet = false)
|
129
|
+
config.attribute_types.fetch(attribute_name.to_sym) do
|
130
|
+
fail attribute_not_found_message(attribute_name) unless quiet
|
131
|
+
end.deep_dup
|
132
|
+
end
|
133
|
+
|
134
|
+
# 表单必填字段列表
|
135
|
+
def required_attributes
|
136
|
+
config.attribute_types.collect { |name, field| name if field[:required] }.compact
|
137
|
+
end
|
138
|
+
|
139
|
+
# 表单允许提交的参数
|
140
|
+
def permitted_attributes
|
141
|
+
_attributes = config.form_attributes
|
142
|
+
|
143
|
+
if _attributes.is_a?(Hash)
|
144
|
+
profiles = _attributes.fetch(:profiles, [])
|
145
|
+
_attributes = profiles.collect { |profile| _attributes.fetch(profile.to_sym, []) }.flatten.uniq
|
146
|
+
# _attributes = _attributes.each {|attr| _attributes << "#{attr}_attributes"}
|
147
|
+
end
|
148
|
+
|
149
|
+
array, hash = [], {}
|
150
|
+
_attributes.each do |field_name|
|
151
|
+
attribute_type_for(field_name)[:is_array] ? hash.merge!(field_name => []) : array.push(field_name)
|
152
|
+
end
|
153
|
+
|
154
|
+
[array, hash].append(config.extra_permitted_attributes)
|
155
|
+
#.append(filters: config.collection_filters.keys)
|
156
|
+
end
|
157
|
+
|
158
|
+
def permitted_class_methods_for(action_name)
|
159
|
+
config.permitted_class_methods.fetch(action_name.to_sym, [])
|
160
|
+
end
|
161
|
+
|
162
|
+
def permitted_instance_methods_for(action_name)
|
163
|
+
config.permitted_instance_methods.fetch(action_name.to_sym, [])
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def attribute_not_found_message(attribute_name)
|
169
|
+
"Attribute #{attribute_name} could not be found in #{self}.attribute_types"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|