passionview 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|