carload 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d65cff581f4e29c73cf1e4fe384de789b4b48bd8
4
- data.tar.gz: e080ef8a8a7118df7553ac7b565d80880a562ff7
3
+ metadata.gz: 8cd7d8e37f5576e78d936449c1e5037208db1789
4
+ data.tar.gz: feed52ae809a23d8cdc1f591e8ea76760e387945
5
5
  SHA512:
6
- metadata.gz: 7d565a9cd04ac9955051d1e41e5b789886345c8d3b7baefcd78cfc37728f90ebb816fda1f141edf21458b76c5772227a17b64faf66f7b1a54c6bb7984a821beb
7
- data.tar.gz: f1f945a17a018ae47f1780e8cbd6047449ac1580defef761aa3404f858b0cd15ea4fea6f8c73819ee18004b1f00c15c585a0d2206129a09be885e9f75e5a32b8
6
+ metadata.gz: cbc8dcf229e5789e2f7517b8b8c7e1b717abfc63372fd76bc537571969072a0f260d8cd5b90b56aba35ea5d5780f20a6b3a2cb7222552eb1828548ba528100d2
7
+ data.tar.gz: 4faa773c891477b8f1b72c9b54e33ecebdd55d8c9e333ced189a8acec895c24309d54bf4ae4ca14fd4133ef4075c5d74e92cf4d1aa78b52ce68ead454f862df8
@@ -11,3 +11,7 @@
11
11
  .select2-results__option--highlighted {
12
12
  background-color: $main-color !important;
13
13
  }
14
+
15
+ .select2-container {
16
+ min-width: 174px;
17
+ }
@@ -4,6 +4,7 @@
4
4
  //= require upload-image
5
5
 
6
6
  $spacing: 16px;
7
+ $half-spacing: 8px;
7
8
 
8
9
  @import 'bootstrap-sprockets';
9
10
  @import 'bootstrap';
@@ -42,17 +43,30 @@ $spacing: 16px;
42
43
 
43
44
  #main-container {
44
45
  width: 100%;
45
- padding-left: $spacing;
46
- padding-right: $spacing;
47
46
  #main-content {
48
47
  margin-top: 50px;
48
+ padding-left: $spacing;
49
+ padding-right: $spacing;
49
50
  }
50
51
  }
51
52
 
52
53
  .model-index-panel {
53
54
  width: 100%;
55
+ .panel-body {
56
+ overflow: scroll;
57
+ padding-left: 0;
58
+ padding-right: 0;
59
+ }
54
60
  table {
55
61
  margin-bottom: 0;
62
+ table-layout: fixed;
63
+ width: 100%;
64
+ td {
65
+ overflow: scroll;
66
+ }
67
+ th:last-child {
68
+ width: 50px;
69
+ }
56
70
  }
57
71
  }
58
72
  .model-new-panel, .model-edit-panel {
@@ -11,7 +11,7 @@ module Carload
11
11
  include Croppable
12
12
 
13
13
  def index
14
- authorize :dashboard, :index? unless Carload.auth_solution == :none
14
+ authorize :carload_dashboard, :index? unless Carload.auth_solution == :none
15
15
  @search = @model_class.search(params[:q])
16
16
  @objects = @search.result.page(params[:page])
17
17
  @show_attributes = Dashboard.model(@model_name).index_page[:shows][:attributes] + [:created_at, :updated_at]
@@ -20,16 +20,16 @@ module Carload
20
20
  end
21
21
 
22
22
  def new
23
- authorize :dashboard, :new? unless Carload.auth_solution == :none
23
+ authorize :carload_dashboard, :new? unless Carload.auth_solution == :none
24
24
  @object = @model_class.new
25
25
  end
26
26
 
27
27
  def edit
28
- authorize :dashboard, :edit? unless Carload.auth_solution == :none
28
+ authorize :carload_dashboard, :edit? unless Carload.auth_solution == :none
29
29
  end
30
30
 
31
31
  def create
32
- authorize :dashboard, :create? unless Carload.auth_solution == :none
32
+ authorize :carload_dashboard, :create? unless Carload.auth_solution == :none
33
33
  @object = @model_class.create model_params
34
34
  if @object.save
35
35
  redirect_to action: :index, model: @model_names # TODO: To show page.
@@ -39,7 +39,7 @@ module Carload
39
39
  end
40
40
 
41
41
  def update
42
- authorize :dashboard, :update? unless Carload.auth_solution == :none
42
+ authorize :carload_dashboard, :update? unless Carload.auth_solution == :none
43
43
  if @object.update model_params
44
44
  redirect_to action: :index, model: @model_names # TODO: To show page.
45
45
  else
@@ -48,7 +48,7 @@ module Carload
48
48
  end
49
49
 
50
50
  def destroy
51
- authorize :dashboard, :destroy? unless Carload.auth_solution == :none
51
+ authorize :carload_dashboard, :destroy? unless Carload.auth_solution == :none
52
52
  @object.destroy
53
53
  redirect_to action: :index, model: @model_names
54
54
  end
@@ -1,16 +1,63 @@
1
1
  module Carload
2
2
  module DashboardHelper
3
3
  def generate_input form, model_name, attribute_name, column
4
- if column.type == :integer and attribute_name =~ /_id/
5
- associated_model = attribute_name.sub('_id', '')
6
- label_attribute = Dashboard.model(model_name).associated_models[associated_model.to_sym]
7
- form.association associated_model,
8
- label_method: label_attribute,
9
- label: t("activerecord.models.#{associated_model}"),
4
+ if Dashboard::ModelSpec.foreign_key? attribute_name
5
+ associated_model = attribute_name.gsub(/_id$/, '').to_sym
6
+ options = Dashboard.model(model_name).associated_models[associated_model]
7
+ label_attribute = options[:choose_by]
8
+ if options[:polymorphic]
9
+ forms = ''
10
+ options[:available_models].each_with_index do |real_model, i|
11
+ forms << form.input(attribute_name,
12
+ label: t("activerecord.attributes.#{model_name}.#{real_model}.#{label_attribute}"),
13
+ collection: real_model.camelize.constantize.all,
14
+ label_method: label_attribute,
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
+ }
51
+ }
52
+ end
53
+ elsif attribute_name =~ /_type$/
54
+ associated_model = attribute_name.gsub(/_type$/, '').to_sym
55
+ options = Dashboard.model(model_name).associated_models[associated_model]
56
+ form.input attribute_name, collection: options[:available_models].map(&:camelize),
10
57
  input_html: {
11
58
  class: 'use-select2',
12
59
  data: {
13
- placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{associated_model}.#{label_attribute}"))
60
+ placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{model_name}.#{attribute_name}"))
14
61
  }
15
62
  }
16
63
  elsif needs_upload?(model_name, attribute_name) and image?(attribute_name)
@@ -20,6 +67,23 @@ module Carload
20
67
  end
21
68
  end
22
69
 
70
+ def generate_search_input form, model_name, attribute
71
+ if attribute[:options]
72
+ form.input "#{attribute[:name].to_s.gsub('.', '_')}_#{attribute[:term]}",
73
+ required: false, label: false, collection: attribute[:options],
74
+ input_html: {
75
+ class: 'use-select2',
76
+ data: {
77
+ placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{model_name}.#{attribute[:name]}"))
78
+ }
79
+ }
80
+ else
81
+ form.input "#{attribute[:name].to_s.gsub('.', '_')}_#{attribute[:term]}",
82
+ placeholder: t("activerecord.attributes.#{@model_name}.#{attribute[:name]}"),
83
+ required: false, label: false
84
+ end
85
+ end
86
+
23
87
  def generate_show object, attribute
24
88
  case attribute
25
89
  when Symbol
@@ -1,4 +1,4 @@
1
- class DashboardPolicy < Struct.new(:user, :dashboard)
1
+ class CarloadDashboardPolicy < Struct.new(:user, :dashboard)
2
2
  [:index, :new, :edit, :destroy].each do |action|
3
3
  define_method :"#{action}?" do
4
4
  return true if Carload.auth_solution == :none
@@ -1,7 +1,7 @@
1
1
  <%= simple_form_for @object, url: "/carload/dashboard/#{@model_names}/#{@object.id}" do |f| %>
2
2
  <% @model_class.columns_hash.each do |name, column| %>
3
- <% next if ['id', 'created_at', 'updated_at'].include? name %>
4
- <%= generate_input f, @model_name, name, column %>
3
+ <% next if Dashboard::ModelSpec::SkippedAttributes.include? name %>
4
+ <%= generate_input(f, @model_name, name, column) rescue nil %>
5
5
  <% end %>
6
6
  <%= f.button :submit, t('carload.action.submit'), class: 'btn btn-primary' %>
7
7
  <% end %>
@@ -1,8 +1,7 @@
1
1
  <%= simple_form_for @search, url: dashboard_search_path(@model_names), method: :post do |f| %>
2
2
  <div class='search-fields'>
3
3
  <% @search_attributes.each do |attribute| %>
4
- <%= f.input "#{attribute[:name].to_s.gsub('.', '_')}_#{attribute[:term]}", required: false, label: false,
5
- placeholder: t("activerecord.attributes.#{@model_name}.#{attribute[:name]}") %>
4
+ <%= generate_search_input f, @model_name, attribute %>
6
5
  <% end %>
7
6
  </div>
8
7
  <div class='search-buttons'>
@@ -2,6 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>Carload</title>
5
+ <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0'/>
5
6
  <%= stylesheet_link_tag 'carload/dashboard', media: 'all' %>
6
7
  <%= javascript_include_tag 'carload/dashboard' %>
7
8
  <%= csrf_meta_tags %>
@@ -1,4 +1,12 @@
1
1
  en:
2
+ date:
3
+ order:
4
+ - :year
5
+ - :month
6
+ - :day
7
+ month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
8
+ abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
9
+
2
10
  carload:
3
11
  actions: Actions
4
12
  action:
@@ -1,4 +1,12 @@
1
1
  zh-CN:
2
+ date:
3
+ order:
4
+ - :year
5
+ - :month
6
+ - :day
7
+ month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]
8
+ abbr_month_names: [~, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
9
+
2
10
  carload:
3
11
  actions: 操作
4
12
  action:
@@ -3,21 +3,110 @@ module Carload
3
3
  class ModelSpec
4
4
  attr_accessor :default, :attributes, :index_page, :associated_models
5
5
 
6
- def initialize
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
7
33
  @default = false
8
34
  @attributes = ExtendedHash.new
9
35
  @index_page = ExtendedHash.new
10
36
  @index_page[:shows] = ExtendedHash.new
11
37
  @index_page[:searches] = ExtendedHash.new
12
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
13
101
  end
14
102
  end
15
103
 
16
104
  class << self
17
105
  def model name, &block
106
+ name = name.to_sym
18
107
  if block_given?
19
108
  @@models ||= {}
20
- spec = ModelSpec.new
109
+ spec = @@models[name] || ModelSpec.new
21
110
  yield spec
22
111
  @@models[name] = spec
23
112
  else
@@ -25,11 +114,16 @@ module Carload
25
114
  end
26
115
  end
27
116
 
28
- def associate args
29
- model_a = args.keys.first
30
- model_b = args.values.first
31
- @@models[model_a].associated_models[model_b] = args[:choose_by]
32
- @@models[model_b].associated_models[model_a] = nil
117
+ def associate options
118
+ model_a = options.keys.first
119
+ model_b = options.values.first
120
+ options.shift
121
+ @@models[model_a] ||= ModelSpec.new
122
+ @@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
33
127
  end
34
128
 
35
129
  def models
@@ -41,6 +135,36 @@ module Carload
41
135
  @@models.each do |name, spec|
42
136
  return @@default_model = name if spec.default
43
137
  end
138
+ @@default_model = @@models.keys.first
139
+ end
140
+
141
+ def write file_path
142
+ content = File.read("#{Carload::Engine.root}/lib/generators/carload/templates/dashboard.rb")
143
+ content.gsub!(/^end$/, '')
144
+ default = true
145
+ models.each do |name, spec|
146
+ content << <<-RUBY
147
+ model :#{name} do |spec|
148
+ spec.default = #{default}
149
+ spec.attributes.permitted = #{spec.attributes.permitted}
150
+ spec.index_page.shows.attributes = #{spec.index_page.shows.attributes}
151
+ spec.index_page.searches.attributes = #{spec.index_page.searches.attributes}
152
+ end
153
+ RUBY
154
+ default = false
155
+ next if spec.associated_models.empty?
156
+ spec.associated_models.each do |associated_model, options|
157
+ next if not options.class == Hash or not options[:choose_by]
158
+ content << <<-RUBY
159
+ associate(#{{ name.to_sym => associated_model.to_sym }.merge options})
160
+ RUBY
161
+ end
162
+ end
163
+ content << "end\n"
164
+ File.open('app/carload/dashboard.rb', 'w') do |file|
165
+ file.write content
166
+ file.close
167
+ end
44
168
  end
45
169
  end
46
170
  end
@@ -1,3 +1,3 @@
1
1
  module Carload
2
- VERSION = '0.2.2'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,45 @@
1
+ module Carload
2
+ class DashGenerator < Rails::Generators::NamedBase
3
+ source_root File.expand_path('../templates', __FILE__)
4
+
5
+ def change_dashboard_file
6
+ # Process model once atime.
7
+ model = file_name
8
+ model_specs = {}
9
+ Rails.application.eager_load! # It is necessary to load models manually.
10
+ ActiveRecord::Base.descendants.each do |model| # Rails 5 can use ApplicationRecord.
11
+ next if model.name == 'ApplicationRecord'
12
+ name = model.name.underscore
13
+ model_specs[name] = Dashboard::ModelSpec.new model
14
+ end
15
+ spec = model_specs[model]
16
+ if not spec.associated_models.empty?
17
+ spec.revise_stage_1!
18
+ cli = HighLine.new
19
+ cli.say "\nModel #{model} has associated with other models."
20
+ spec.associated_models.each do |associated_model, options|
21
+ spec.associated_models[associated_model][:choose_by] = cli.choose do |menu|
22
+ menu.prompt = "Choose the attribute of model #{associated_model} for choosing in #{model}? "
23
+ attributes = options[:polymorphic] ? options[:common_attributes] : model_specs[associated_model].attributes.permitted
24
+ attributes.each do |attribute|
25
+ next if attribute =~ /_id$/
26
+ menu.choice attribute.to_sym
27
+ end
28
+ end
29
+ end
30
+ spec.revise_stage_2!
31
+ end
32
+ # 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
+ if not Dashboard.models.keys.include? model.to_sym or Dashboard.model(model).changed? spec
40
+ Dashboard.models[model.to_sym] = spec
41
+ Dashboard.write 'app/carload/dashboard.rb'
42
+ end
43
+ end
44
+ end
45
+ end
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.2.2
4
+ version: 0.3.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-10-01 00:00:00.000000000 Z
11
+ date: 2016-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -198,6 +198,20 @@ dependencies:
198
198
  - - "~>"
199
199
  - !ruby/object:Gem::Version
200
200
  version: '0.1'
201
+ - !ruby/object:Gem::Dependency
202
+ name: highline
203
+ requirement: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - "~>"
206
+ - !ruby/object:Gem::Version
207
+ version: '1.7'
208
+ type: :runtime
209
+ prerelease: false
210
+ version_requirements: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - "~>"
213
+ - !ruby/object:Gem::Version
214
+ version: '1.7'
201
215
  description: Carload is built with taste, and tries to be just right!
202
216
  email:
203
217
  - dongli.init@gmail.com
@@ -225,8 +239,7 @@ files:
225
239
  - app/jobs/carload/application_job.rb
226
240
  - app/mailers/carload/application_mailer.rb
227
241
  - app/models/carload/application_record.rb
228
- - app/policies/application_policy.rb
229
- - app/policies/dashboard_policy.rb
242
+ - app/policies/carload_dashboard_policy.rb
230
243
  - app/views/carload/dashboard/_form.html.erb
231
244
  - app/views/carload/dashboard/edit.html.erb
232
245
  - app/views/carload/dashboard/index.html.erb
@@ -258,6 +271,7 @@ files:
258
271
  - lib/carload/extended_hash.rb
259
272
  - lib/carload/version.rb
260
273
  - lib/generators/carload/USAGE
274
+ - lib/generators/carload/dash_generator.rb
261
275
  - lib/generators/carload/install_generator.rb
262
276
  - lib/generators/carload/templates/carload.rb
263
277
  - lib/generators/carload/templates/dashboard.rb
@@ -1,53 +0,0 @@
1
- class ApplicationPolicy
2
- attr_reader :user, :record
3
-
4
- def initialize(user, record)
5
- @user = user
6
- @record = record
7
- end
8
-
9
- def index?
10
- false
11
- end
12
-
13
- def show?
14
- scope.where(:id => record.id).exists?
15
- end
16
-
17
- def create?
18
- false
19
- end
20
-
21
- def new?
22
- create?
23
- end
24
-
25
- def update?
26
- false
27
- end
28
-
29
- def edit?
30
- update?
31
- end
32
-
33
- def destroy?
34
- false
35
- end
36
-
37
- def scope
38
- Pundit.policy_scope!(user, record.class)
39
- end
40
-
41
- class Scope
42
- attr_reader :user, :scope
43
-
44
- def initialize(user, scope)
45
- @user = user
46
- @scope = scope
47
- end
48
-
49
- def resolve
50
- scope
51
- end
52
- end
53
- end