platanus 0.0.19 → 0.0.21
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.
- data/lib/platanus/stacked.rb +92 -46
- data/lib/platanus/version.rb +1 -1
- data/lib/platanus.rb +0 -1
- metadata +2 -3
- data/lib/platanus/cmap.rb +0 -80
data/lib/platanus/stacked.rb
CHANGED
@@ -4,7 +4,9 @@
|
|
4
4
|
|
5
5
|
module Platanus
|
6
6
|
|
7
|
-
|
7
|
+
## Adds the has_stacked association to an ActiveRecord model.
|
8
|
+
#
|
9
|
+
# TODO
|
8
10
|
#
|
9
11
|
module StackedAttr
|
10
12
|
|
@@ -17,61 +19,94 @@ module Platanus
|
|
17
19
|
module ClassMethods
|
18
20
|
|
19
21
|
# Adds an stacked attribute to the model.
|
20
|
-
def has_stacked(_name, _options={}
|
22
|
+
def has_stacked(_name, _options={})
|
23
|
+
|
24
|
+
# check option support
|
25
|
+
raise NotSupportedError.new('Only autosave mode is supported') if _options[:autosave] == false
|
26
|
+
raise NotSupportedError.new('has_many_through is not supported yet') if _options.has_key? :through
|
21
27
|
|
28
|
+
# prepare names
|
22
29
|
tname = _name.to_s
|
23
30
|
tname_single = tname.singularize
|
31
|
+
tname_class = _options.fetch(:class_name, tname_single.camelize)
|
32
|
+
|
33
|
+
# generate top_value property
|
34
|
+
top_value_prop = "top_#{tname_single}"
|
35
|
+
if _options.has_key? :top_value_key
|
36
|
+
belongs_to top_value_prop.to_sym, class_name: tname_class, foreign_key: _options.delete(:top_value_key)
|
37
|
+
elsif self.column_names.include? "#{top_value_prop}_id"
|
38
|
+
belongs_to top_value_prop.to_sym, class_name: tname_class
|
39
|
+
else
|
40
|
+
send :define_method, top_value_prop do
|
41
|
+
# Storing the last stacked value will not prevent race conditions
|
42
|
+
# when simultaneous updates occur.
|
43
|
+
return @_stacked_last unless @_stacked_last.nil?
|
44
|
+
@_stacked_last = self.send(tname).first
|
45
|
+
end
|
46
|
+
send :define_method, "#{top_value_prop}=" do |_top|
|
47
|
+
@_stacked_last = _top
|
48
|
+
end
|
49
|
+
end
|
50
|
+
send :private, "#{top_value_prop}="
|
24
51
|
|
52
|
+
# prepare cached attributes
|
25
53
|
to_cache = _options.delete(:cached)
|
26
|
-
to_cache_prf = if _options[:cache_prf].nil? then 'last_' else _options.delete(:cache_prf) end
|
54
|
+
to_cache_prf = if _options[:cache_prf].nil? then 'last_' else _options.delete(:cache_prf) end # TODO: deprecate
|
27
55
|
|
28
|
-
# Retrieve callbacks
|
29
|
-
before_push = _options.delete(:before_push)
|
30
|
-
after_push = _options.delete(:after_push)
|
31
|
-
|
32
|
-
raise NotSupportedError.new('Only autosave mode is supported') if _options[:autosave] == false
|
33
|
-
|
34
|
-
_options[:order] = 'created_at DESC, id DESC'
|
35
|
-
_options[:limit] = 10 if _options[:limit].nil?
|
36
|
-
|
37
|
-
# Prepare cached attributes, generate read-only aliases without prefix.
|
38
56
|
unless to_cache.nil?
|
39
|
-
to_cache = to_cache.map do |
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
57
|
+
to_cache = to_cache.map do |cache_attr|
|
58
|
+
unless cache_attr.is_a? Hash
|
59
|
+
name = cache_attr.to_s
|
60
|
+
# attr_protected(to_cache_prf + name)
|
61
|
+
send :define_method, name do self.send(to_cache_prf + name) end # generate read-only aliases without prefix. TODO: deprecate
|
62
|
+
{ to: to_cache_prf + name, from: name }
|
63
|
+
else
|
64
|
+
# TODO: Test whether options are valid.
|
65
|
+
cache_attr
|
66
|
+
end
|
44
67
|
end
|
45
68
|
end
|
46
69
|
|
47
|
-
|
48
|
-
|
49
|
-
public
|
70
|
+
# callbacks
|
71
|
+
on_stack = _options.delete(:on_stack)
|
50
72
|
|
51
|
-
|
52
|
-
|
73
|
+
# limits and ordering
|
74
|
+
# TODO: Support other kind of ordering, this would require to reevaluate top on every push
|
75
|
+
_options[:order] = 'created_at DESC, id DESC'
|
76
|
+
_options[:limit] = 10 if _options[:limit].nil?
|
53
77
|
|
54
|
-
|
55
|
-
|
56
|
-
block.call(self, obj) unless block.nil?
|
78
|
+
# setup main association
|
79
|
+
has_many _name, _options
|
57
80
|
|
58
|
-
|
59
|
-
|
60
|
-
|
81
|
+
cache_step = ->(_ctx, _top) {
|
82
|
+
# cache required fields
|
83
|
+
return if to_cache.nil?
|
84
|
+
to_cache.each do |cache_attr|
|
85
|
+
value = if cache_attr.has_key? :from
|
86
|
+
_top.nil? ? _top : _top.send(cache_attr[:from])
|
87
|
+
else
|
88
|
+
_ctx.send(cache_attr[:virtual], _top)
|
61
89
|
end
|
90
|
+
_ctx.send(cache_attr[:to].to_s + '=', value)
|
91
|
+
end
|
92
|
+
}
|
62
93
|
|
63
|
-
|
64
|
-
|
94
|
+
after_step = ->(_ctx, _top) {
|
95
|
+
# update top value property
|
96
|
+
_ctx.send("#{top_value_prop}=", _top)
|
65
97
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
else
|
70
|
-
@_stacked_last = obj
|
71
|
-
end
|
98
|
+
# execute after callback
|
99
|
+
_ctx.send(on_stack, _top) unless on_stack.nil?
|
100
|
+
}
|
72
101
|
|
73
|
-
|
74
|
-
|
102
|
+
send :define_method, "push_#{tname_single}!" do |obj|
|
103
|
+
self.class.transaction do
|
104
|
+
|
105
|
+
# cache, then save if new, then push and finally process state
|
106
|
+
cache_step.call(self, obj)
|
107
|
+
self.save! if self.new_record? # make sure there is an id BEFORE pushing
|
108
|
+
raise ActiveRecord::RecordInvalid.new(obj) unless send(tname).send('<<',obj)
|
109
|
+
after_step.call(self, obj)
|
75
110
|
|
76
111
|
self.save! if self.changed? # Must save again, no other way...
|
77
112
|
end
|
@@ -79,18 +114,29 @@ module Platanus
|
|
79
114
|
|
80
115
|
send :define_method, "push_#{tname_single}" do |obj|
|
81
116
|
begin
|
82
|
-
return
|
117
|
+
return send("push_#{tname_single}!", obj)
|
83
118
|
rescue ActiveRecord::RecordInvalid
|
84
119
|
return false
|
85
120
|
end
|
86
121
|
end
|
87
122
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
123
|
+
send :define_method, "restore_#{tname}!" do
|
124
|
+
self.class.transaction do
|
125
|
+
|
126
|
+
# find current top, then restore stack state
|
127
|
+
top = self.send(_name).first
|
128
|
+
cache_step.call(self, top)
|
129
|
+
after_step.call(self, top)
|
130
|
+
|
131
|
+
self.save! if self.changed?
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
send :define_method, "restore_#{tname}" do
|
136
|
+
begin
|
137
|
+
return send("restore_#{tname}!")
|
138
|
+
rescue ActiveRecord::RecordInvalid
|
139
|
+
return false
|
94
140
|
end
|
95
141
|
end
|
96
142
|
end
|
data/lib/platanus/version.rb
CHANGED
data/lib/platanus.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: platanus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.21
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-08 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Platan.us utility gem
|
15
15
|
email:
|
@@ -28,7 +28,6 @@ files:
|
|
28
28
|
- lib/platanus/activable.rb
|
29
29
|
- lib/platanus/api_base.rb
|
30
30
|
- lib/platanus/canned.rb
|
31
|
-
- lib/platanus/cmap.rb
|
32
31
|
- lib/platanus/enum.rb
|
33
32
|
- lib/platanus/gcontroller.rb
|
34
33
|
- lib/platanus/http_helpers.rb
|
data/lib/platanus/cmap.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
module Platanus
|
2
|
-
|
3
|
-
module Cmap
|
4
|
-
|
5
|
-
class InvalidName < Exception; end
|
6
|
-
|
7
|
-
def self.included(base)
|
8
|
-
base.cattr_accessor :cmap
|
9
|
-
base.cmap = Hash.new { |hash, key| hash[key] = {} }
|
10
|
-
base.extend ClassMethods
|
11
|
-
end
|
12
|
-
|
13
|
-
module ClassMethods
|
14
|
-
|
15
|
-
def cmap_register(_key, _value, _cat=nil)
|
16
|
-
if _cat.nil?; self.cmap[_key] = _value
|
17
|
-
else; self.cmap[_cat.to_s][_key] = _value
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def cmap_convert(_key, _cat=nil)
|
22
|
-
if _cat.nil?; return self.cmap[_key]
|
23
|
-
else; return self.cmap[_cat.to_s][_key]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def cmap_convert_back(_value, _cat=nil)
|
28
|
-
if _cat.nil?; return self.cmap.key(_value)
|
29
|
-
else; return self.cmap[_cat.to_s].key(_value)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def str_attr_accessor(_target, _options={})
|
34
|
-
str_attr_reader(_target,_options)
|
35
|
-
str_attr_writer(_target,_options)
|
36
|
-
end
|
37
|
-
|
38
|
-
def str_attr_writer(_target, _options={})
|
39
|
-
_target = _target.to_s
|
40
|
-
_self = _options.fetch(:extend, self)
|
41
|
-
_cat = _options[:cat]
|
42
|
-
_cmap = self.cmap
|
43
|
-
|
44
|
-
_self.send(:define_method, _target + '_str=') do |value|
|
45
|
-
if _cat.nil?; self.send(_target + '=', _cmap.fetch(value))
|
46
|
-
else; self.send(_target + '=', _cmap[_cat.to_s].fetch(value)); end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def str_attr_reader(_target, _options={})
|
51
|
-
_target = _target.to_s
|
52
|
-
_self = _options.fetch(:extend, self)
|
53
|
-
_cat = _options[:cat]
|
54
|
-
_cmap = self.cmap
|
55
|
-
|
56
|
-
_self.send(:define_method, _target + '_str') do
|
57
|
-
if _cat.nil?; _cmap.key(self.send(_target))
|
58
|
-
else; _cmap[_cat.to_s].key(self.send(_target)); end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# private
|
63
|
-
# def cmap_converters(_cat_or_name=nil)
|
64
|
-
# # Define new class methods.
|
65
|
-
# klass = (class << self; self; end)
|
66
|
-
# klass.send(:define_method, _target + '_to_str') do |value|
|
67
|
-
# if _cat.nil?; _cmap.index(value)
|
68
|
-
# else; _cmap[_cat].index(value); end
|
69
|
-
# end
|
70
|
-
#
|
71
|
-
# klass.send(:define_method, 'str_to_' + _target) do |value|
|
72
|
-
# if _cat.nil?; _cmap.fetch(value)
|
73
|
-
# else; _cmap[_cat].fetch(value); end
|
74
|
-
# end
|
75
|
-
# end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
|