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.
@@ -4,7 +4,9 @@
4
4
 
5
5
  module Platanus
6
6
 
7
- # Adds the has_stacked association to an ActiveRecord model.
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={}, &block)
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 |name|
40
- name = name.to_s
41
- # attr_protected(to_cache_prf + name)
42
- send :define_method, name do self.send(to_cache_prf + name) end
43
- name
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
- private
48
- has_many _name, _options
49
- public
70
+ # callbacks
71
+ on_stack = _options.delete(:on_stack)
50
72
 
51
- send :define_method, "push_#{tname_single}!" do |obj|
52
- self.class.transaction do
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
- # execute before callbacks
55
- self.send(before_push, obj) unless before_push.nil?
56
- block.call(self, obj) unless block.nil?
78
+ # setup main association
79
+ has_many _name, _options
57
80
 
58
- # cache required fields
59
- unless to_cache.nil?
60
- to_cache.each { |name| send(to_cache_prf + name + '=', obj.send(name)) }
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
- # push attribute, this will save the model if new.
64
- raise ActiveRecord::RecordInvalid.new(obj) unless self.send(tname).send('<<',obj)
94
+ after_step = ->(_ctx, _top) {
95
+ # update top value property
96
+ _ctx.send("#{top_value_prop}=", _top)
65
97
 
66
- # update inverse association if posible
67
- if self.attributes.has_key? "top_#{tname_single}_id"
68
- self["top_#{tname_single}_id"] = obj.id
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
- # execute after callback
74
- self.send(after_push, obj) unless after_push.nil?
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 self.send("push_#{tname_single}!", obj)
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
- unless self.column_names.include? "top_#{tname_single}_id"
89
- send :define_method, "top_#{tname_single}" do
90
- # Storing the last stacked value will not prevent race conditions
91
- # when simultaneous updates occur.
92
- return @_stacked_last unless @_stacked_last.nil?
93
- @_stacked_last = self.send(_name).first
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
@@ -1,3 +1,3 @@
1
1
  module Platanus
2
- VERSION = "0.0.19" # 0.1 will come with tests!
2
+ VERSION = "0.0.21" # 0.1 will come with tests!
3
3
  end
data/lib/platanus.rb CHANGED
@@ -5,7 +5,6 @@ require 'platanus/stacked'
5
5
  require 'platanus/activable'
6
6
  require 'platanus/traceable'
7
7
  require 'platanus/layered'
8
- require 'platanus/cmap'
9
8
  require 'platanus/enum'
10
9
  require 'platanus/http_helpers'
11
10
 
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.19
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-03 00:00:00.000000000 Z
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
-