passionview 0.0.0 → 0.1.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 +4 -4
- data/lib/passion_view/actionable.rb +61 -0
- data/lib/passion_view/authorization.rb +8 -0
- data/lib/passion_view/card/base.rb +20 -0
- data/lib/passion_view/card/collection.rb +9 -0
- data/lib/passion_view/card.rb +4 -0
- data/lib/passion_view/filter/base.rb +13 -0
- data/lib/passion_view/filter/simple_filter.rb +14 -0
- data/lib/passion_view/filter.rb +4 -0
- data/lib/passion_view/filterable.rb +56 -0
- data/lib/passion_view/form/base.rb +134 -0
- data/lib/passion_view/form.rb +3 -0
- data/lib/passion_view/paginable.rb +136 -0
- data/lib/passion_view/pathable.rb +5 -0
- data/lib/passion_view/relation.rb +58 -0
- data/lib/passion_view/resourceful.rb +34 -0
- data/lib/passion_view/routable.rb +42 -0
- data/lib/passion_view/sortable.rb +103 -0
- data/lib/passion_view/view_model/base.rb +20 -0
- data/lib/passion_view/view_model/list.rb +28 -0
- data/lib/passion_view/view_model.rb +4 -0
- data/lib/passion_view.rb +15 -0
- data/lib/passionview.rb +1 -0
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b45e27b2a47a1f99c639a07dc1a6fc5fddc4bb9
|
4
|
+
data.tar.gz: 2fbdbbd3d7571d6f854de5c383ada31c25e00694
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16d514d7f8221eea35a6963bd4671995fafacc8ca6f60a6a17acf9f0a9ec2148be78e3c14a02ef920ff7e3918bd9fffeb663e176106398f928d37b10bd0b4256
|
7
|
+
data.tar.gz: 53656135764c204ffed1202df61a3ec91b234d52fb0aaf2ae30fed0530e5d0faf23318e818e9c4d7db73a374a4596980f4acc948116e880104f8763995f36953
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module PassionView::Actionable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
# well : menu en bas de page
|
5
|
+
# menu : menu sur l'entite
|
6
|
+
# nav : menu de navigation
|
7
|
+
# item : menu dans une liste
|
8
|
+
CONTEXTS = [:well, :menu, :nav, :item].freeze
|
9
|
+
TYPES = [:nav, :action].freeze
|
10
|
+
|
11
|
+
class Action
|
12
|
+
KEYS = [
|
13
|
+
:name, :icon, :path,
|
14
|
+
:type, :context, :priority,
|
15
|
+
:options
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
attr_accessor(*KEYS)
|
19
|
+
|
20
|
+
def initialize(attributes)
|
21
|
+
attributes.each do |key, value|
|
22
|
+
raise unless respond_to?(key)
|
23
|
+
send("#{key}=", value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](key)
|
28
|
+
return unless KEYS.include?(key)
|
29
|
+
|
30
|
+
send(key)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ActionList < Array
|
35
|
+
def context(context)
|
36
|
+
select do |a|
|
37
|
+
case a.context
|
38
|
+
when Symbol
|
39
|
+
a.context == context
|
40
|
+
when Array
|
41
|
+
a.context.include?(context)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def type(type)
|
47
|
+
select do |a|
|
48
|
+
case a.type
|
49
|
+
when Symbol
|
50
|
+
a.type == type
|
51
|
+
when Array
|
52
|
+
a.type.include?(type)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module ClassMethods
|
59
|
+
def action(name, path = nil, icon: nil); end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class PassionView::Card::Base < PassionView::ViewModel::Base
|
2
|
+
include PassionView::Routable
|
3
|
+
include PassionView::Actionable
|
4
|
+
include PassionView::Resourceful
|
5
|
+
include PassionView::Authorization
|
6
|
+
|
7
|
+
def to_key
|
8
|
+
[resource.id]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_model
|
12
|
+
resource
|
13
|
+
end
|
14
|
+
|
15
|
+
delegate :to_param, to: :resource
|
16
|
+
|
17
|
+
def self.i18n_scope
|
18
|
+
:cards
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module PassionView::Filterable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def initialize(items, options = {})
|
5
|
+
super
|
6
|
+
filters = options.delete(:filters) || {}
|
7
|
+
|
8
|
+
filter_with(filters)
|
9
|
+
end
|
10
|
+
|
11
|
+
def items
|
12
|
+
filters.reduce(super) { |items, filter| filter.skip? ? items : filter.apply(items) }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def url_options
|
18
|
+
super.merge(filterable_params)
|
19
|
+
end
|
20
|
+
|
21
|
+
def filterable_params
|
22
|
+
filters.each.with_object({}) { |filter, params| params[filter.name] = filter.value unless filter.skip? }
|
23
|
+
end
|
24
|
+
|
25
|
+
def filter_with(_)
|
26
|
+
@filters ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :filters
|
30
|
+
|
31
|
+
module Controller
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
|
34
|
+
def filter_params
|
35
|
+
action = params[:action]&.to_sym
|
36
|
+
|
37
|
+
raise "Unfilterable action: #{action.inspect}" unless self.class.filter?(action)
|
38
|
+
|
39
|
+
params[:filters]&.to_unsafe_h || {}
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def filter(*actions)
|
44
|
+
filtered_actions.push(*actions)
|
45
|
+
end
|
46
|
+
|
47
|
+
def filter?(action)
|
48
|
+
filtered_actions.include?(action)
|
49
|
+
end
|
50
|
+
|
51
|
+
def filtered_actions
|
52
|
+
@filtered_actions ||= []
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
class PassionView::Form::Base < PassionView::ViewModel::Base
|
2
|
+
include ActiveModel::Conversion
|
3
|
+
include ActiveModel::Validations
|
4
|
+
|
5
|
+
Delegation = Struct.new(
|
6
|
+
:to,
|
7
|
+
:prefix,
|
8
|
+
:methods,
|
9
|
+
:cast,
|
10
|
+
:accessor,
|
11
|
+
)
|
12
|
+
|
13
|
+
def persisted?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid?
|
18
|
+
[super].concat(self.class.delegations(inherited: true).map do |delegation|
|
19
|
+
item = instance_eval("self.#{delegation.to} rescue nil")
|
20
|
+
next if item.nil?
|
21
|
+
result = item.respond_to?(:valid?) ? item.valid? : true
|
22
|
+
|
23
|
+
# TODO: tester les validations des models
|
24
|
+
# du type "validates :qqch(sans _id), presence: true"
|
25
|
+
if item.respond_to?(:errors)
|
26
|
+
item.errors.messages.each do |method, messages|
|
27
|
+
messages.each do |message|
|
28
|
+
errors.add(:"#{delegation.prefix}#{method}", message) unless errors[:"#{delegation.prefix}#{method}"].include?(message)
|
29
|
+
end if item.methods.include?(method)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
result
|
34
|
+
end).compact.reduce(:&)
|
35
|
+
end
|
36
|
+
|
37
|
+
def assign_attributes(new_attributes)
|
38
|
+
Rails.logger.debug("Permitting parameters: #{permitted_params.map(&:inspect).join(', ')}")
|
39
|
+
new_attributes.permit(*permitted_params)
|
40
|
+
|
41
|
+
new_attributes.each do |k, v|
|
42
|
+
send("#{k}=", v) if respond_to?("#{k}=")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def update_attributes(new_attributes)
|
47
|
+
assign_attributes(new_attributes)
|
48
|
+
valid? && save
|
49
|
+
end
|
50
|
+
|
51
|
+
def permitted_params
|
52
|
+
self.class.permitted_params
|
53
|
+
end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
def permit(*methods)
|
57
|
+
permitted_params.concat(methods)
|
58
|
+
end
|
59
|
+
|
60
|
+
def delegate(*methods)
|
61
|
+
options = methods.pop
|
62
|
+
unless options.is_a?(Hash) && (to = options[:to])
|
63
|
+
raise ArgumentError, 'Delegation needs a target'
|
64
|
+
end
|
65
|
+
|
66
|
+
prefix, = options.values_at(:prefix, :allow_nil)
|
67
|
+
cast = options[:cast]
|
68
|
+
writer = options[:accessor] || !cast.nil?
|
69
|
+
|
70
|
+
method_prefix = "#{prefix == true ? to : prefix}_" if prefix
|
71
|
+
|
72
|
+
delegations << Delegation.new(
|
73
|
+
to,
|
74
|
+
method_prefix,
|
75
|
+
methods,
|
76
|
+
cast,
|
77
|
+
writer,
|
78
|
+
)
|
79
|
+
|
80
|
+
caster = caster_for(cast)
|
81
|
+
|
82
|
+
raise ArgumentError, ':errors not allowed' if methods.include?(:errors)
|
83
|
+
|
84
|
+
methods.each do |method|
|
85
|
+
permit("#{method_prefix}#{method}")
|
86
|
+
end if writer
|
87
|
+
methods += methods.map { |method| "#{method}=" } if writer
|
88
|
+
|
89
|
+
methods.each do |method|
|
90
|
+
if method =~ /[^\]]=$/
|
91
|
+
define_method("#{method_prefix}#{method}") do |arg|
|
92
|
+
instance_variable_set("@#{method_prefix}#{method.gsub(/=$/, '')}_before_type_cast", arg)
|
93
|
+
arg = caster.call(arg) unless caster.nil?
|
94
|
+
instance_eval("self.#{to}").send(method, arg)
|
95
|
+
end
|
96
|
+
|
97
|
+
define_method("#{method_prefix}#{method.to_s.gsub(/=$/, '')}_before_type_cast") do
|
98
|
+
instance_variable_get("@#{method_prefix}#{method.gsub(/=$/, '')}_before_type_cast") || send("#{method_prefix}#{method.gsub(/=$/, '')}")
|
99
|
+
end
|
100
|
+
else
|
101
|
+
define_method("#{method_prefix}#{method}") do |*args, &block|
|
102
|
+
instance_eval("self.#{to}").try(:send, method, *args, &block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def delegations(inherited: false)
|
109
|
+
@delegations ||= []
|
110
|
+
return @delegations unless inherited && superclass.respond_to?(:delegations)
|
111
|
+
@delegations + superclass.delegations(inherited: true)
|
112
|
+
end
|
113
|
+
|
114
|
+
def permitted_params
|
115
|
+
@permitted_params ||= []
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def caster_for(cast_type)
|
121
|
+
case cast_type
|
122
|
+
when :date then ->(v) { Date.parse(v) if v =~ /\A\d\d\d\d-\d\d-\d\d\z/ }
|
123
|
+
when :datetime then ->(v) { Time.zone.parse(v) if v =~ /\A\d\d\d\d-\d\d-\d\d \d\d:\d\d\z/ }
|
124
|
+
when :boolean then ->(v) { (%w(1 true yes) + %w(0 false no)).include?(v) ? %w(1 true yes).include?(v) : nil }
|
125
|
+
when :integer then ->(v) { v.to_i if v =~ /\A[-+]?\d+\z/ }
|
126
|
+
when :float then ->(v) { v.to_f if v =~ /\A[-+]?\d+(?:\.\d+|)\z/ }
|
127
|
+
when :decimal then ->(v) { BigDecimal.new(v) if v =~ /\A[-+]?\d+(?:\.\d+|)\z/ }
|
128
|
+
when :blank then nil
|
129
|
+
when Proc then cast_type
|
130
|
+
else ->(v) { v unless v.blank? }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module PassionView::Paginable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
include PassionView::Pathable
|
5
|
+
|
6
|
+
attr_accessor :current_page, :current_per
|
7
|
+
|
8
|
+
DEFAULT_PER_PAGE = 10
|
9
|
+
|
10
|
+
def initialize(items, options = {})
|
11
|
+
super
|
12
|
+
pagination = options.delete(:pagination) || {}
|
13
|
+
|
14
|
+
paginate_with(pagination[:page], pagination[:per])
|
15
|
+
end
|
16
|
+
|
17
|
+
def items
|
18
|
+
super.page(current_page)
|
19
|
+
.per(current_per)
|
20
|
+
end
|
21
|
+
|
22
|
+
def next_pages(count = 2, &block)
|
23
|
+
(-count..count).each do |idx|
|
24
|
+
target = current_page + idx
|
25
|
+
yield target, next_page_path(idx) if block_given? && !out_of_range?(target)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def first_page?(page = nil)
|
30
|
+
page ||= current_page
|
31
|
+
page.to_i == first_page
|
32
|
+
end
|
33
|
+
|
34
|
+
def last_page?(page = nil)
|
35
|
+
page ||= current_page
|
36
|
+
page.to_i == last_page
|
37
|
+
end
|
38
|
+
|
39
|
+
def current_page?(page)
|
40
|
+
page.to_i == current_page
|
41
|
+
end
|
42
|
+
|
43
|
+
def first_page
|
44
|
+
1
|
45
|
+
end
|
46
|
+
|
47
|
+
def previous_page
|
48
|
+
next_page(-1)
|
49
|
+
end
|
50
|
+
|
51
|
+
def next_page(shift = 1)
|
52
|
+
target = current_page + shift
|
53
|
+
target = first_page if target < first_page
|
54
|
+
target = last_page if target > last_page
|
55
|
+
|
56
|
+
target
|
57
|
+
end
|
58
|
+
|
59
|
+
def last_page
|
60
|
+
[items.total_pages, 1].max
|
61
|
+
end
|
62
|
+
|
63
|
+
def out_of_range?(target)
|
64
|
+
(target < first_page) || (target > last_page)
|
65
|
+
end
|
66
|
+
|
67
|
+
def first_page_path
|
68
|
+
paginable_path(page: first_page)
|
69
|
+
end
|
70
|
+
|
71
|
+
def previous_page_path
|
72
|
+
paginable_path(page: previous_page)
|
73
|
+
end
|
74
|
+
|
75
|
+
def next_page_path(shift = 1)
|
76
|
+
paginable_path(page: next_page(shift))
|
77
|
+
end
|
78
|
+
|
79
|
+
def last_page_path
|
80
|
+
paginable_path(page: last_page)
|
81
|
+
end
|
82
|
+
|
83
|
+
def paginable_path(options)
|
84
|
+
page_path(url_options.merge(options))
|
85
|
+
end
|
86
|
+
|
87
|
+
def page_path(options = {})
|
88
|
+
resource_path(options)
|
89
|
+
end
|
90
|
+
|
91
|
+
def url_options
|
92
|
+
super.merge(paginable_params)
|
93
|
+
end
|
94
|
+
|
95
|
+
def paginable_params(page = nil, per = nil)
|
96
|
+
params = {}
|
97
|
+
page ||= current_page
|
98
|
+
per ||= current_per
|
99
|
+
|
100
|
+
params[:page] = page unless page == first_page
|
101
|
+
params[:per] = per unless per.nil? || per == DEFAULT_PER_PAGE
|
102
|
+
|
103
|
+
params
|
104
|
+
end
|
105
|
+
|
106
|
+
def paginate_with(page, per = nil)
|
107
|
+
self.current_page = page.nil? ? 1 : page.to_i
|
108
|
+
self.current_per = per.nil? ? DEFAULT_PER_PAGE : per.to_i
|
109
|
+
end
|
110
|
+
|
111
|
+
module Controller
|
112
|
+
extend ActiveSupport::Concern
|
113
|
+
|
114
|
+
def pagination_params
|
115
|
+
action = params[:action]&.to_sym
|
116
|
+
|
117
|
+
raise "Unpaginable action: #{action.inspect}" unless self.class.paginate?(action)
|
118
|
+
|
119
|
+
params.permit(:page, :per).to_h
|
120
|
+
end
|
121
|
+
|
122
|
+
module ClassMethods
|
123
|
+
def paginate(*actions)
|
124
|
+
paginated_actions.push(*actions)
|
125
|
+
end
|
126
|
+
|
127
|
+
def paginate?(action)
|
128
|
+
paginated_actions.include?(action)
|
129
|
+
end
|
130
|
+
|
131
|
+
def paginated_actions
|
132
|
+
@paginated_actions ||= []
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module PassionView::Relation
|
2
|
+
# delegate :to_sql,
|
3
|
+
# :offset_value,
|
4
|
+
# :total_count,
|
5
|
+
# :limit_value,
|
6
|
+
# to: :scoped
|
7
|
+
|
8
|
+
def items
|
9
|
+
case (items = super)
|
10
|
+
when ActiveRecord::Relation
|
11
|
+
items
|
12
|
+
when ActiveRecord::Base
|
13
|
+
items.where(nil)
|
14
|
+
else
|
15
|
+
raise TypeError, "not an ActiveRecord object: #{items.class}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def first
|
20
|
+
item_view_model(items.first)
|
21
|
+
end
|
22
|
+
|
23
|
+
def last
|
24
|
+
item_view_model(items.first)
|
25
|
+
end
|
26
|
+
|
27
|
+
def count
|
28
|
+
items.count
|
29
|
+
end
|
30
|
+
|
31
|
+
def size
|
32
|
+
items.size
|
33
|
+
end
|
34
|
+
|
35
|
+
def include?(obj)
|
36
|
+
items.include?(obj)
|
37
|
+
end
|
38
|
+
|
39
|
+
def empty?
|
40
|
+
items.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_sql
|
44
|
+
items.to_sql
|
45
|
+
end
|
46
|
+
|
47
|
+
def offset_value
|
48
|
+
items.offset_value
|
49
|
+
end
|
50
|
+
|
51
|
+
def total_count
|
52
|
+
items.total_count
|
53
|
+
end
|
54
|
+
|
55
|
+
def limit_value
|
56
|
+
items.limit_value
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Resourceful makes an instance a resource in terms of Rails routing
|
2
|
+
module PassionView::Resourceful
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
include PassionView::Routable
|
6
|
+
|
7
|
+
def initialize(resource, options = {})
|
8
|
+
super
|
9
|
+
@resource = resource
|
10
|
+
@parent_resource = options[:parent]
|
11
|
+
end
|
12
|
+
|
13
|
+
def resource_path(options = {})
|
14
|
+
routes.polymorphic_path(polymorphic_components, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def resource
|
20
|
+
@resource
|
21
|
+
end
|
22
|
+
|
23
|
+
def polymorphic_components
|
24
|
+
options[:polymorphic_path] || [@parent_resource, nested_resource_path].compact
|
25
|
+
end
|
26
|
+
|
27
|
+
def resource_class
|
28
|
+
@resource.klass #### @objects
|
29
|
+
end
|
30
|
+
|
31
|
+
def nested_resource_path
|
32
|
+
resource_class
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Routable exposes contextualized routes in an object
|
2
|
+
module PassionView::Routable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def contextualize_routes(obj)
|
6
|
+
routes.contextualize(obj)
|
7
|
+
end
|
8
|
+
|
9
|
+
def routes
|
10
|
+
@_routes_context ||= Class.new do
|
11
|
+
include Rails.application.routes.url_helpers
|
12
|
+
include Rails.application.routes.mounted_helpers
|
13
|
+
|
14
|
+
def default_url_options
|
15
|
+
@_context ? @_context.default_url_options : {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def _routes_context
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def _with_routes(routes)
|
23
|
+
old_routes, @_routes = @_routes, routes
|
24
|
+
yield
|
25
|
+
ensure
|
26
|
+
@_routes = old_routes
|
27
|
+
end
|
28
|
+
|
29
|
+
def contextualized?
|
30
|
+
!@_context.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def contextualize(obj)
|
34
|
+
@_context = obj
|
35
|
+
end
|
36
|
+
|
37
|
+
def without_context
|
38
|
+
self.class.new
|
39
|
+
end
|
40
|
+
end.new
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module PassionView::Sortable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
include PassionView::Pathable
|
5
|
+
|
6
|
+
attr_accessor :sort_column, :sort_direction
|
7
|
+
|
8
|
+
def initialize(items, options = {})
|
9
|
+
super
|
10
|
+
sort = options.delete(:sort) || {}
|
11
|
+
|
12
|
+
sort_with(sort[:sort], sort[:direction])
|
13
|
+
end
|
14
|
+
|
15
|
+
def items
|
16
|
+
super.order(order_arel[sort_column].send(sort_direction))
|
17
|
+
end
|
18
|
+
|
19
|
+
def sort_columns_available
|
20
|
+
order_arel.keys
|
21
|
+
end
|
22
|
+
|
23
|
+
def sort_column_available?(name)
|
24
|
+
sort_columns_available.include?(name.to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
def sort_directions_available
|
28
|
+
%w(asc desc)
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_sort_column
|
32
|
+
resource_class.primary_key
|
33
|
+
end
|
34
|
+
|
35
|
+
def default_sort_direction
|
36
|
+
sort_directions_available.first
|
37
|
+
end
|
38
|
+
|
39
|
+
def sort_by_path(column, direction = default_sort_direction)
|
40
|
+
sortable_path(sortable_params(column, direction))
|
41
|
+
end
|
42
|
+
|
43
|
+
def sortable_path(options)
|
44
|
+
resource_path(url_options.merge(options))
|
45
|
+
end
|
46
|
+
|
47
|
+
def url_options
|
48
|
+
super.merge(sortable_params)
|
49
|
+
end
|
50
|
+
|
51
|
+
def order_arel
|
52
|
+
table = resource_class.arel_table
|
53
|
+
pk = default_sort_column
|
54
|
+
|
55
|
+
{ pk => table[pk.to_sym] }
|
56
|
+
end
|
57
|
+
|
58
|
+
def sortable_params(column = nil, direction = nil)
|
59
|
+
params = {}
|
60
|
+
|
61
|
+
column ||= sort_column
|
62
|
+
direction ||= sort_direction
|
63
|
+
|
64
|
+
params[:sort] = column
|
65
|
+
params[:direction] = direction
|
66
|
+
|
67
|
+
params
|
68
|
+
end
|
69
|
+
|
70
|
+
def sort_with(column, direction)
|
71
|
+
self.sort_column = column || default_sort_column
|
72
|
+
self.sort_column = default_sort_column unless sort_columns_available.include?(sort_column)
|
73
|
+
|
74
|
+
self.sort_direction = direction || default_sort_direction
|
75
|
+
self.sort_direction = default_sort_direction unless %w(asc desc).include?(sort_direction)
|
76
|
+
end
|
77
|
+
|
78
|
+
module Controller
|
79
|
+
extend ActiveSupport::Concern
|
80
|
+
|
81
|
+
def sort_params
|
82
|
+
action = params[:action]&.to_sym
|
83
|
+
|
84
|
+
raise "Unsortable action: #{action.inspect}" unless self.class.sort?(action)
|
85
|
+
|
86
|
+
params.permit(:sort, :direction).to_h
|
87
|
+
end
|
88
|
+
|
89
|
+
module ClassMethods
|
90
|
+
def sort(*actions)
|
91
|
+
sorted_actions.push(*actions)
|
92
|
+
end
|
93
|
+
|
94
|
+
def sort?(action)
|
95
|
+
sorted_actions.include?(action)
|
96
|
+
end
|
97
|
+
|
98
|
+
def sorted_actions
|
99
|
+
@sorted_actions ||= []
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class PassionView::ViewModel::Base
|
2
|
+
extend ActiveModel::Naming
|
3
|
+
extend ActiveModel::Translation
|
4
|
+
|
5
|
+
def initialize(_, options = {})
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.i18n_scope
|
10
|
+
:viewmodel
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.lookup_ancestors
|
14
|
+
[self]
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :options
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# items: enumerable
|
2
|
+
module PassionView::ViewModel::List
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
extend Enumerable
|
5
|
+
|
6
|
+
def initialize(items, options = {})
|
7
|
+
super
|
8
|
+
@items = items
|
9
|
+
@item_view_model_class = options[:item_view_model_class]
|
10
|
+
end
|
11
|
+
|
12
|
+
def each
|
13
|
+
return enum_for(:each) unless block_given?
|
14
|
+
|
15
|
+
items.each do |e|
|
16
|
+
yield item_view_model(e)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def item_view_model(item)
|
21
|
+
item_view_model_class.new(item)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :items
|
27
|
+
attr_reader :item_view_model_class
|
28
|
+
end
|
data/lib/passion_view.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module PassionView; end
|
2
|
+
|
3
|
+
require 'passion_view/pathable'
|
4
|
+
require 'passion_view/routable'
|
5
|
+
require 'passion_view/actionable'
|
6
|
+
require 'passion_view/authorization'
|
7
|
+
require 'passion_view/relation'
|
8
|
+
require 'passion_view/resourceful'
|
9
|
+
require 'passion_view/filterable'
|
10
|
+
require 'passion_view/paginable'
|
11
|
+
require 'passion_view/sortable'
|
12
|
+
require 'passion_view/view_model'
|
13
|
+
require 'passion_view/card'
|
14
|
+
require 'passion_view/form'
|
15
|
+
require 'passion_view/filter'
|
data/lib/passionview.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
require 'passion_view'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passionview
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ADHOC-GTI
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-05-
|
11
|
+
date: 2018-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Get view logic out of persistence models and controllers and put it into
|
@@ -19,6 +19,27 @@ extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
21
|
- LICENSE
|
22
|
+
- lib/passion_view.rb
|
23
|
+
- lib/passion_view/actionable.rb
|
24
|
+
- lib/passion_view/authorization.rb
|
25
|
+
- lib/passion_view/card.rb
|
26
|
+
- lib/passion_view/card/base.rb
|
27
|
+
- lib/passion_view/card/collection.rb
|
28
|
+
- lib/passion_view/filter.rb
|
29
|
+
- lib/passion_view/filter/base.rb
|
30
|
+
- lib/passion_view/filter/simple_filter.rb
|
31
|
+
- lib/passion_view/filterable.rb
|
32
|
+
- lib/passion_view/form.rb
|
33
|
+
- lib/passion_view/form/base.rb
|
34
|
+
- lib/passion_view/paginable.rb
|
35
|
+
- lib/passion_view/pathable.rb
|
36
|
+
- lib/passion_view/relation.rb
|
37
|
+
- lib/passion_view/resourceful.rb
|
38
|
+
- lib/passion_view/routable.rb
|
39
|
+
- lib/passion_view/sortable.rb
|
40
|
+
- lib/passion_view/view_model.rb
|
41
|
+
- lib/passion_view/view_model/base.rb
|
42
|
+
- lib/passion_view/view_model/list.rb
|
22
43
|
- lib/passionview.rb
|
23
44
|
homepage: https://github.com/adhoc-gti/passionview
|
24
45
|
licenses:
|