has_moderated 1.0.rc8 → 1.0.rc9

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Add it to your project's Gemfile
7
7
 
8
- gem "has_moderated", ">=1.0.rc8"
8
+ gem "has_moderated", ">=1.0.rc9"
9
9
 
10
10
  and run
11
11
 
@@ -116,20 +116,18 @@ You can see a preview of what a moderation will do.
116
116
 
117
117
  === Get a read-only preview
118
118
 
119
- This method allows you to retrieve a fake object which behaves like an ActiveRecord object, but is read-only.
120
- This object allows you to see the value of all attributes, as well as all the associations (including non-moderated).
121
- It also supports dirty tracking through ActiveModel, so you can use all its functionality (http://api.rubyonrails.org/classes/ActiveModel/Dirty.html).
119
+ You can get a read-only preview of the actual ActiveRecord object. It allows you to see the value of all attributes, as well as all the associations (including non-moderated, except the moderations association). Since it is an ActiveRecord object, it also supports dirty tracking (http://api.rubyonrails.org/classes/ActiveModel/Dirty.html). Note that association methods (preview.some_association) are not real ActiveRecord relations, but just Arrays. If you really need real relations, look at live_preview below.
122
120
 
123
121
  preview = moderation.preview
124
122
  preview.some_attr # => "new value"
125
123
  preview.some_attr_change # => ["old value", "new value"]
126
- preview.some_association.first # => #<HasModerated::FakeTask @fake_of_model=Task, @attributes={"id"=>1, "title"=>"Task 1", "desc"=>nil, "created_at"=>"2012-06-08 15:49:56.726646", "updated_at"=>"2012-06-08 15:49:56.726646"}, @changed_attributes={}, reflections.keys => [:assoc1, :assoc2]>
124
+ preview.some_association.first # => AR object
127
125
 
128
- This preview is cached from applying the moderation in a transaction and then rolling it back (so it does not affect the database).
126
+ This preview is cached from applying the moderation in a transaction and then rolling it back (so it does not affect the database). Note if you have custom model methods that return other ActiveRecord records, those records will not be frozen, so if you save those changes WILL be written to the database.
129
127
 
130
- === Using the fake object to modify the moderation
128
+ === Using the preview to modify the moderation
131
129
 
132
- The fake object described above has a nifty feature (1.0.rc7+) that allows you to update attributes in the moderation by manipulating the fake object. Right now it can only handle attributes and not associations, but that might change in the future. See example below on how to use this feature.
130
+ The preview described above has a nifty feature (1.0.rc7+) that allows you to update attributes in the moderation by manipulating the fake object. Right now it can only handle attributes and not associations, but that might change in the future. See example below on how to use this feature.
133
131
 
134
132
  moderation.parsed_data # => {:attributes=>{"title"=>"Task 1"}}
135
133
  preview = moderation.preview(:saveable => true)
@@ -139,16 +137,18 @@ The fake object described above has a nifty feature (1.0.rc7+) that allows you t
139
137
  moderation.accept
140
138
  Task.first.title # => "Task 2"
141
139
 
142
- === Get a real ActiveRecord preview
140
+ === Get a live ActiveRecord preview
143
141
 
144
- This way, you will get a real ActiveRecord object that is really in the database. You can do whatever you want on it, however the difference is that you need to use it inside the live_preview block. This is because the moderation is applied in a transaction and after you are done using the preview, the transaction will be rolled back. I would recommend using the read-only fake object (see above) if you can, as it is safer. But sometimes you just need access to the real ActiveRecord object.
142
+ Usually you don't need to use this. The difference compared to the above method is that the above will freeze objects from the database and then roll back the transaction, and you will get the frozen copies that you can use at will. Those copies do have fake association methods that just return Arrays instead of AR relations.
143
+
144
+ In contrast to preview, live_preview will let you use the record while the transaction is still going on, so it is not even frozen. Also the association methods are real ActiveRecord relations in this case. After your block completes, the transaction will be rolled back and you will not have access to the preview anymore (and you should not use it outside of the block).
145
145
 
146
146
  moderation.live_preview do |preview|
147
147
  preview.some_attr # => "new value"
148
148
  # note how we need to use .previous_changes when using live_preview,
149
149
  # .changes will be blank as the record has already been saved to DB
150
150
  preview.previous_changes # => {"attr"=>["old", "new"]}
151
- preview.some_association.first # => ActiveRecord object
151
+ preview.some_association.first # => AR object
152
152
  end
153
153
 
154
154
  Note that since a transaction is used in both cases, all the changes made to the database to produce the preview are only in effect inside the transaction, so the changes are only visible inside the transaction. For example, if a second database query comes from another Rails instance, it will not see the changes that are in effect in the transaction, even if the transaction is still in process.
@@ -8,7 +8,7 @@ module HasModerated
8
8
 
9
9
  # attributes
10
10
  data[:attributes] ||= Hash.new
11
- @attributes.each_pair do |key, value|
11
+ @has_moderated_fake_attributes.each_pair do |key, value|
12
12
  if value != @attributes_initial[key]
13
13
  data[:attributes][key.to_s] = value
14
14
  end
@@ -20,64 +20,32 @@ module HasModerated
20
20
  end
21
21
  end
22
22
 
23
- class FakeRecord
24
- extend ActiveModel::Naming
25
- include ActiveModel::Conversion
26
- include ActiveModel::Dirty
27
-
28
- attr_accessor :attributes
29
-
30
- def initialize(based_on_model, based_on_moderation)
23
+ module FakeRecord
24
+ def fake!(based_on_model, based_on_moderation)
31
25
  @based_on_model = based_on_model
32
26
  @based_on_moderation = based_on_moderation
33
- end
34
-
35
- def persisted?
36
- false
37
- end
38
-
39
- def attribute name
40
- attributes[name]
41
- end
42
-
43
- def id
44
- attributes["id"]
45
- end
46
-
47
- def to_s
48
- "#<HasModerated::Fake#{@based_on_model.to_s}>"
49
- end
50
-
51
- def inspect
52
- to_s.chomp(">") +
53
- instance_variables.map{|name| " #{name}=#{instance_variable_get(name)}"}.join(",") +
54
- ", reflections.keys => [" + reflections.keys.map{|s| ":#{s}"}.join(", ")+ "]>"
55
- end
56
- end
57
-
58
- def self.create_fake_association(attr_name, value, target)
59
- target.send(:define_method, attr_name) do
60
- value
27
+ self.freeze
61
28
  end
62
29
  end
63
30
 
64
31
  def self.resolve_record(record, cache)
65
32
  return nil if record.blank?
66
- cache[record.class] ||= Hash.new
67
- if cache[record.class][record.id].present?
68
- cache[record.class][record.id]
33
+ idx = cache.index(record)
34
+ if idx && idx.present?
35
+ cache[idx] # the difference is that the one in cache is frozen for sure
69
36
  else
70
- cache[record.class][record.id] = from_live(record, nil, false, cache)
37
+ cache.push(from_live(record, nil, false, cache))
38
+ cache.last
71
39
  end
72
40
  end
73
41
 
74
- def self.from_live(record, moderation = nil, saveable = false, object_cache = nil)
42
+ def self.from_live(record, moderation = nil, saveable = false, object_cache = [])
75
43
  return nil if record.blank?
76
- obj = FakeRecord.new(record.class, moderation)
77
- eigenclass = (class << obj ; self ; end)
44
+ obj = record
45
+ eigenclass = (class << obj ; include FakeRecord ; self ; end)
46
+ obj.fake!(record.class, moderation)
78
47
 
79
48
  # attributes
80
- obj.instance_variable_set(:@attributes, record.instance_variable_get(:@attributes))
81
49
  changed_attributes = Hash.new
82
50
  record.previous_changes.each_pair do |attr_name, values|
83
51
  changed_attributes[attr_name] = values[0]
@@ -86,33 +54,31 @@ module HasModerated
86
54
 
87
55
  # saveable
88
56
  if saveable
89
- obj.instance_variable_set(:@attributes_initial, record.instance_variable_get(:@attributes).dup)
57
+ obj.instance_variable_set(:@attributes_initial, obj.instance_variable_get(:@attributes).dup)
90
58
  eigenclass.send(:include, Saveable)
59
+ obj.instance_variable_set(:@has_moderated_fake_attributes, Hash.new)
91
60
  obj.attributes.keys.each do |attr_name|
92
61
  eigenclass.send(:define_method, "#{attr_name}=") do |value|
93
- self.attributes[attr_name.to_s] = value
62
+ @has_moderated_fake_attributes[attr_name.to_s] = value
94
63
  end
95
64
  end
96
65
  end
97
66
 
98
67
  # associations
99
- object_cache ||= Hash.new
100
- object_cache[record.class] ||= Hash.new
101
- object_cache[record.class][record.id] = obj
102
- eigenclass.send(:define_method, :reflections) do
103
- record.class.reflections.reject{|k,v| k.to_sym == :moderations}
104
- end
68
+ object_cache.push(obj)
69
+ has_moderated_fake_associations = HashWithIndifferentAccess.new
105
70
  record.class.reflections.values.reject{|s| s.name.to_sym == :moderations}.each do |reflection|
106
71
  if reflection.macro == :has_one || reflection.macro == :belongs_to
107
- create_fake_association(
108
- reflection.name,
109
- resolve_record(record.send(reflection.name), object_cache),
110
- eigenclass)
72
+ has_moderated_fake_associations[reflection.name] = resolve_record(record.send(reflection.name), object_cache)
111
73
  elsif reflection.collection?
112
- create_fake_association(
113
- reflection.name,
114
- record.send(reflection.name).map{|r| resolve_record(r, object_cache)},
115
- eigenclass)
74
+ has_moderated_fake_associations[reflection.name] = record.send(reflection.name).map{|r| resolve_record(r, object_cache)}
75
+ end
76
+ end
77
+
78
+ obj.instance_variable_set(:@has_moderated_fake_associations, has_moderated_fake_associations.freeze)
79
+ eigenclass.class_eval do
80
+ def association(name)
81
+ OpenStruct.new(:reader => @has_moderated_fake_associations[name].freeze)
116
82
  end
117
83
  end
118
84
 
@@ -1,3 +1,3 @@
1
1
  module HasModerated
2
- VERSION = "1.0.rc8"
2
+ VERSION = "1.0.rc9"
3
3
  end