amoeba-rb / amoeba Goto Github PK
View Code? Open in Web Editor NEWA ruby gem to allow the copying of ActiveRecord objects and their associated children, configurable with a DSL on the model
License: Other
A ruby gem to allow the copying of ActiveRecord objects and their associated children, configurable with a DSL on the model
License: Other
Is it possible to use Amoeba from within a Concern? I'm currently getting this error:
undefined method `amoeba' for Amoeba:Module
Otherwise, love this gem. Total lifesaver!
Is there a way to use this gem and switch to a database that has the same structure and then save the duped data into that new database?
I'm getting this error (amoeba 2.0.0) after upgrading to ruby 2.0.0
NoMethodError: private method `initialize_dup' called for #<Class:something>
We have lots of models that should be copied recursively. This means that each involved model needs to have amoeba enabled. This is error-prone since a developer can forget to include new models. Because of this, we simply enable by convention all models by hand. That means adding an enable-amoeba block which is cumbersome.
Is it possible to just enable amoeba for all models?
First off, in what order are the model and associations duplicated? I couldn't tell from the documentation.
Secondly, I'm trying to duplicate a Site record, which in turn is duplicating the association. This is fine. The issue is that I'm hitting a validation on the child Placement model before the duplicated Site is created.
Simplified code...
class Placement < ActiveRecord::Base
belongs_to :site, inverse_of: :placements
validate :site_must_have_stuff, if: proc { |record| record.some_condition? }
def site_must_have_stuff
if site.url.blank?
errors.add(:url, "must be present")
false
else
true
end
end
end
class Site < ActiveRecord::Base
has_many :placements, inverse_of: :site
end
site = Site.new(attributes)
site.amoeba_dup #=> (Placement invalid error)
Am I doing something wrong? Is the expected behavior to create the duplicated associations before the object you call amoeba_dup
on? And if all of this is true, is there a way to coerce the duplication order?
Is this intended? We have the following
class Activity < ActiveRecord::Base
amoeba do
nullify :uuid
end
end
module Activities
class Video < ::Activity
end
end
activity = Activities::Video.last
cloned_activity = activity.amoeba_dup
cloned_activity.uuid == activity.uuid # true when I would have expected false
It does work when I move the amoeba block into the the child class.
In my use-case, I have an incoming email message, which has_many :attachments
. I want to copy these attachments to another model. To use amoeba with this, my code now is as follows:
def attachments_deep_copy
self.attachments.map {|attachment| attachment.amoeba_dup }
end
What seems nicer to me, is to add an amoeba_dup
method to ActiveRecord::Associations::CollectionProxy
, so it would simply be possible to use self.attachments.amoeba_dup
.
I have a model that looks like this:
class Foo < ActiveRecord::Base
belongs_to :bar
amoeba do
enable
include_field :bar
end
end
But it doesn't work --- how should i go about duplicating the belongs_to
association with amoeba?
Assume i am having a Model like as below :
Recipe is the model which is having a "name" attribute. So Recipe is having name as 'test1'
Now i am able to made a copy of recipe by amoeba_dup method and able to save that into database also like following :
r = Recipe.find(1).amoeba_dup
r.save;
But before storing i want to update the name of the recipe after made a copy.
r = Recipe.find(1).amoeba_dup
r.name = 'test2';
r.save;
While doing this iam getting :
NoMethodError: undefined method `name=' for #Recipe:0x0000000ab971e8
First of all, is it possible to update the value after copy by amoeba_dup ?
Could you please clarify me...Thank you
It works only for associations.
One more example:
a is boolean attribute
If you have have:
class Post
has_many :comments
amoeba do enabled end
end
class EnhancedPost < Post
end
and then following code fails:
a=EnhancedPost.find(... a enhanced post with comments)
b = a.dup
b.comments.size.should == b.comments.size ## FAILS
The reason is that the Amoeba::Config in the EnhancedPost class has enabled=false (the default) b/c it is not the same instance as the Amoeba::Config in the Post base class.
I'm thinking that the amoeba block in EnhancedPost should perhaps be written this way:
class EnhancedPost < Post
amoeba do
super.amoeba ## get the defaults from the base class
## then do anything special for EnhancedPost class
end
end
and I'm wishing that you could include a note about the behaviour of Amoeba and inherited classes in your wonderful documentation, it took two of us quite a few hours to figure out what was happening.
Thanks!
Rob.
Vaughn, thanks for awesome gem.
I found it doesn't maintain carrierwave cloning. Do you plan to maintain it?
Hi!
I doing the same thing like in docs:
class Post < ActiveRecord::Base
has_many :post_widgets
has_many :widgets, :through => :post_widgets
amoeba do
enable
clone [:widgets]
end
end
Then when I cloned post instance, amoeba adds extra records into widgets
table, but not into post_widgets
where they should really be.
Is this a bug?
(Rails 4.2.1, amoeba 3.0.0)
Let's say one thread calls the amoeba
method (as in, some_object.class.amoeba do ...
) to set custom configurations. But then before it has a chance to complete its some_object.amoeba_dup
call, another thread calls amoeba
on the same type of object (but a different instance) and sets configurations differently. Is the process happening in amoeba_dup
on that first thread/object affected by that second call to amoeba
?
If so, is there a supported way to ensure thread safety and concurrency while using On The Fly configurations?
I was wondering if it is possible to dump the copied model to a file so it can be loaded from another application with same model configuration. I search a way to replicate content between same apps but with complex models.
Hi there,
I have a Tasklist & a Task Model.
I'm using amoeba to duplicate one task list with its associated tasks.
Both Tasklist & Task have a user_id field.
The user_id is set to NULL by default.
When I duplicate, I want the user_id to be set to the current_user.id (from Devise).
I'm able to copy the task list with the proper user_id using:
@tasklist = Tasklist.find(topic_params[:tasklist])
@tasklist.user_id = current_user.id
@tasklist.tasks.user_id = current_user.id
@tasklist.amoeba_dup.save
Tasks are also properly copied but the user_id is not updated in the copied tasks (only task list).
I can't use the current_user.id in the Model with :set.
So I'm wondering how I can achieve this?
Thanks!
Vincent
Not sure of the details here - I only know that the dup subcollection was unable to save b/c of validation errors.
I was started to debug it, but then I simply flipped a bit on this library instead, rewrite my copy functions as
alias :orig_dup :dup
def dup
copy = orig_dup
model_runs.each {|model_run| copy.model_runs << model_run.dup}
copy
end
and moved on. Filed this ticket here in hopes it would trigger some better test case on your part.
Looks like it'll be a great library when it's finished.
Not working in rails 4, any suggestions?
I'm on Rails 4.1.9 and when trying to clone a through association I'm getting the following error.
Cannot modify association 'Client#cost_timespans' because the source reflection class 'CostTimespan' is associated to 'Cost' via :has_many.
Heres my simplified code
class CostTimespan < ActiveRecord::Base
belongs_to :cost
end
class Cost < ActiveRecord::Base
has_many :cost_timespans, -> { order(end_date: :desc) }
end
class Client < ActiveRecord::Base
has_many :costs
has_many :cost_timespans, through: :costs
amoeba do
include_association :available_services
include_association :sign_cancellation_charges
include_association :costs
include_association :cost_timespans
clone [:cost_timespans]
customize ->(original_client, new_client) {
new_client.service_ids = original_client.service_ids
new_client.assign_attributes new_client.attributes.select { |k, v| k.ends_with?('_count') }.inject({}) { |h, (k, v)| h[k] = 0; h }
}
end
end
Here's the stack trace
activerecord (4.1.9) lib/active_record/associations/through_association.rb:86:in `ensure_mutable'
activerecord (4.1.9) lib/active_record/associations/has_many_through_association.rb:84:in `build_through_record'
activerecord (4.1.9) lib/active_record/associations/has_many_through_association.rb:46:in `block in concat_records'
activerecord (4.1.9) lib/active_record/associations/has_many_through_association.rb:45:in `concat_records'
activerecord (4.1.9) lib/active_record/associations/collection_association.rb:157:in `concat'
activerecord (4.1.9) lib/active_record/associations/has_many_through_association.rb:36:in `concat'
activerecord (4.1.9) lib/active_record/associations/collection_proxy.rb:972:in `<<'
amoeba (3.0.0) lib/amoeba/macros/has_many.rb:19:in `block in follow_with_clone'
activerecord (4.1.9) lib/active_record/relation/delegation.rb:46:in `each'
amoeba (3.0.0) lib/amoeba/macros/has_many.rb:16:in `follow_with_clone'
amoeba (3.0.0) lib/amoeba/macros/has_many.rb:6:in `follow'
amoeba (3.0.0) lib/amoeba/cloner.rb:108:in `follow_association'
amoeba (3.0.0) lib/amoeba/cloner.rb:80:in `block in follow_all_except_excludes'
amoeba (3.0.0) lib/amoeba/cloner.rb:78:in `follow_all_except_excludes'
amoeba (3.0.0) lib/amoeba/cloner.rb:94:in `apply_associations'
amoeba (3.0.0) lib/amoeba/cloner.rb:102:in `apply'
amoeba (3.0.0) lib/amoeba/cloner.rb:23:in `run'
amoeba (3.0.0) lib/amoeba/instance_methods.rb:34:in `amoeba_dup'
app/controllers/account/clients_controller.rb:39:in `duplicate'
actionpack (4.1.9) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
actionpack (4.1.9) lib/abstract_controller/base.rb:189:in `process_action'
actionpack (4.1.9) lib/action_controller/metal/rendering.rb:10:in `process_action'
actionpack (4.1.9) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
activesupport (4.1.9) lib/active_support/callbacks.rb:113:in `call'
activesupport (4.1.9) lib/active_support/callbacks.rb:149:in `block in halting_and_conditional'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:229:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:229:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:166:in `block in halting'
activesupport (4.1.9) lib/active_support/callbacks.rb:86:in `run_callbacks'
actionpack (4.1.9) lib/abstract_controller/callbacks.rb:19:in `process_action'
actionpack (4.1.9) lib/action_controller/metal/rescue.rb:29:in `process_action'
actionpack (4.1.9) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
activesupport (4.1.9) lib/active_support/notifications.rb:159:in `block in instrument'
activesupport (4.1.9) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
activesupport (4.1.9) lib/active_support/notifications.rb:159:in `instrument'
actionpack (4.1.9) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
actionpack (4.1.9) lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
activerecord (4.1.9) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
actionpack (4.1.9) lib/abstract_controller/base.rb:136:in `process'
actionview (4.1.9) lib/action_view/rendering.rb:30:in `process'
actionpack (4.1.9) lib/action_controller/metal.rb:196:in `dispatch'
actionpack (4.1.9) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch'
actionpack (4.1.9) lib/action_controller/metal.rb:232:in `block in action'
actionpack (4.1.9) lib/action_dispatch/routing/route_set.rb:82:in `dispatch'
actionpack (4.1.9) lib/action_dispatch/routing/route_set.rb:50:in `call'
actionpack (4.1.9) lib/action_dispatch/journey/router.rb:73:in `block in call'
actionpack (4.1.9) lib/action_dispatch/journey/router.rb:59:in `call'
actionpack (4.1.9) lib/action_dispatch/routing/route_set.rb:685:in `call'
meta_request (0.3.4) lib/meta_request/middlewares/app_request_handler.rb:13:in `call'
meta_request (0.3.4) lib/meta_request/middlewares/meta_request_handler.rb:13:in `call'
rack-ssl-enforcer (0.2.6) lib/rack/ssl-enforcer.rb:56:in `call'
warden (1.2.3) lib/warden/manager.rb:35:in `block in call'
warden (1.2.3) lib/warden/manager.rb:34:in `call'
rack (1.5.2) lib/rack/etag.rb:23:in `call'
rack (1.5.2) lib/rack/conditionalget.rb:25:in `call'
rack (1.5.2) lib/rack/head.rb:11:in `call'
actionpack (4.1.9) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
actionpack (4.1.9) lib/action_dispatch/middleware/flash.rb:254:in `call'
rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context'
rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call'
actionpack (4.1.9) lib/action_dispatch/middleware/cookies.rb:562:in `call'
activerecord (4.1.9) lib/active_record/query_cache.rb:36:in `call'
activerecord (4.1.9) lib/active_record/connection_adapters/abstract/connection_pool.rb:621:in `call'
activerecord (4.1.9) lib/active_record/migration.rb:380:in `call'
actionpack (4.1.9) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
activesupport (4.1.9) lib/active_support/callbacks.rb:82:in `run_callbacks'
actionpack (4.1.9) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
actionpack (4.1.9) lib/action_dispatch/middleware/reloader.rb:73:in `call'
actionpack (4.1.9) lib/action_dispatch/middleware/remote_ip.rb:76:in `call'
rollbar (1.4.2) lib/rollbar/middleware/rails/rollbar.rb:24:in `block in call'
rollbar (1.4.2) lib/rollbar.rb:754:in `scoped'
rollbar (1.4.2) lib/rollbar/middleware/rails/rollbar.rb:22:in `call'
better_errors (2.0.0) lib/better_errors/middleware.rb:84:in `protected_app_call'
better_errors (2.0.0) lib/better_errors/middleware.rb:79:in `better_errors_call'
better_errors (2.0.0) lib/better_errors/middleware.rb:57:in `call'
rack-contrib (1.1.0) lib/rack/contrib/response_headers.rb:17:in `call'
meta_request (0.3.4) lib/meta_request/middlewares/headers.rb:16:in `call'
actionpack (4.1.9) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
rollbar (1.4.2) lib/rollbar/middleware/rails/show_exceptions.rb:22:in `call_with_rollbar'
actionpack (4.1.9) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
railties (4.1.9) lib/rails/rack/logger.rb:38:in `call_app'
railties (4.1.9) lib/rails/rack/logger.rb:20:in `block in call'
activesupport (4.1.9) lib/active_support/tagged_logging.rb:68:in `block in tagged'
activesupport (4.1.9) lib/active_support/tagged_logging.rb:26:in `tagged'
activesupport (4.1.9) lib/active_support/tagged_logging.rb:68:in `tagged'
railties (4.1.9) lib/rails/rack/logger.rb:20:in `call'
quiet_assets (1.0.2) lib/quiet_assets.rb:18:in `call_with_quiet_assets'
actionpack (4.1.9) lib/action_dispatch/middleware/request_id.rb:21:in `call'
rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
rack (1.5.2) lib/rack/runtime.rb:17:in `call'
activesupport (4.1.9) lib/active_support/cache/strategy/local_cache_middleware.rb:26:in `call'
rack (1.5.2) lib/rack/lock.rb:17:in `call'
actionpack (4.1.9) lib/action_dispatch/middleware/static.rb:84:in `call'
rack (1.5.2) lib/rack/sendfile.rb:112:in `call'
railties (4.1.9) lib/rails/engine.rb:514:in `call'
railties (4.1.9) lib/rails/application.rb:144:in `call'
rack (1.5.2) lib/rack/content_length.rb:14:in `call'
thin (1.6.2) lib/thin/connection.rb:86:in `block in pre_process'
thin (1.6.2) lib/thin/connection.rb:84:in `pre_process'
thin (1.6.2) lib/thin/connection.rb:53:in `process'
thin (1.6.2) lib/thin/connection.rb:39:in `receive_data'
eventmachine (1.0.3) lib/eventmachine.rb:187:in `run'
thin (1.6.2) lib/thin/backends/base.rb:73:in `start'
thin (1.6.2) lib/thin/server.rb:162:in `start'
rack (1.5.2) lib/rack/handler/thin.rb:16:in `run'
rack (1.5.2) lib/rack/server.rb:264:in `start'
railties (4.1.9) lib/rails/commands/server.rb:69:in `start'
railties (4.1.9) lib/rails/commands/commands_tasks.rb:81:in `block in server'
railties (4.1.9) lib/rails/commands/commands_tasks.rb:76:in `server'
railties (4.1.9) lib/rails/commands/commands_tasks.rb:40:in `run_command!'
railties (4.1.9) lib/rails/commands.rb:17:in `<top (required)>'
spring (1.0.0) lib/spring/client/rails.rb:29:in `call'
spring (1.0.0) lib/spring/client/command.rb:7:in `call'
spring (1.0.0) lib/spring/client.rb:23:in `run'
spring (1.0.0) bin/spring:31:in `<top (required)>'
bin/rails:9:in `<main>'
The following code will cause nil
and false
to be frozen after loading amoeba. This is unexpected imho and causes problems in code that depends on object.frozen?
.
class Config
DEFAULTS = {
enabled: false,
inherit: false,
do_preproc: false,
parenting: false,
raised: false,
dup_method: :dup,
remap_method: nil,
...
}
...
DEFAULTS.freeze
DEFAULTS.each do |key, value|
value.freeze
...
end
I don't think freezing every value inside the hash is needed here?
Love the gem.
So in my application, I'm saving duplicated objects in the same table as the original object. I associate the two (with self-referential associations), so I'm able to find the "draft" version of an object. It works great for the most part.
Assume all objects have an :approved_version_id, with this amoeba configuration:
amoeba do
include_association :ratings
exclude_association [:approved, :draft]
customize(lambda {|live_obj, draft_obj|
draft_obj.approved_version_id = live_obj.id
})
end
(see the entire sample of 3 models at https://gist.github.com/stevehodges/e85a1e367d546d527984)
By default, propagation uses submissive parenting, meaning the config settings on the parent will be applied, but any child settings, if present, will either add to or overwrite the parent settings depending on how you call the DSL methods.
I've found that the customize directive doesn't get passed to the model's children. The easy way around this is to define another amoeba block in the child, which is convenient when the child model is also duplicating its children (the grandchildren).
However, I can't figure out a way to set that customize directive on the grandchild.
The result: the original object has an approved_version_id, as does its child. The grandchild does not.
Gist of Rails models
Gist of Rails Console output
Hi,
I have trouble with using Amoeba together with PaperTrail ( ). The reason for that is overwrite of ActiveRecord::Base#dup by Amoeba. PaperTrail uses .dup method to clone object and serialize it. Is there any way to use .dup method by skiping Amoeba part for 'amobeoized' ( ๐ ) models?
How to copy the following association? Seems doesn't work right now. Thx.
class Field < ActiveRecord::Base
has_many :field_trees
has_many :subfields, through: :field_trees, dependent: :destroy
end
class FieldTree < ActiveRecord::Base
belongs_to :field
belongs_to :subfield, :class_name => "Field"
end
the override blocks receive as both first and second arguments the new object, instead of old_object as first argument as the doc suggests.
https://github.com/amoeba-rb/amoeba/blob/master/lib/amoeba/cloner.rb#L113
def process_overrides
amoeba.overrides.each do |block|
block.call(@new_object, @new_object)
end
end
should be
def process_overrides
amoeba.overrides.each do |block|
block.call(@old_object, @new_object)
end
end
Hi,
we use Globalize3 (https://github.com/svenfuchs/globalize3) which is used to have translations for your objects. That is one can use things like below, which generates the needed translations in a table:
class Answer < ActiveRecord::Base
attr_accessible :text
belongs_to :question
translate :text
amoeba do
enable
end
end
Globalize3 creates a has_many :translations relation. If one creates a new answer, sets the text and saves everything works fine, and the text is also saved to the translations' table. If one dups an answer and saves it, it won't create a translation row. If one saves again it creates a nil raw. And if one then changes the text of the duplicated element, the translation of the first element is changed:
a = Answer.new
a.text = "Test"
a.save
-> Everything ok, entry in answers table and translations table with correct text
b = a.dup
b.save
-> Nothing happens in translations table
b.save (again)
-> Empty entry in translations table
b.text = "Test 2"
b.save
-> Changes in the row of object a
Summary: The has_many :translations has to be recognized and used correctly, which is not the case at the moment
Reading through Known Limitations... section in the docs, there is a part that talks about case sensitive substitutions using gsub.
I may have not understood correctly what you meant gsub is case sensitive. gsub doesn't care about case sensitivity unless you pass a string, otherwise when passing a regexp it depends on the /i flag determining case insensitivity...
I recently added a counter_cache to a model only to be surprised when duplication via amoeba brought it out of sync. I should have anticipated this, but it would be nice for amoeba to "just work" in this situation.
Here's the scenario. I dup a deep model hierarchy. One of the collections (A) on the root object has another collection (B). Collection B is not to be duplicated. Easy enough; I simply did not include amoeba do
in the class for A. But then I added a counter_cache for collection B.
On the clone collection B empty, but the counter_cache says that it has items. The fix is simple:
amoeba do
set signups_count: 0
propagate #this is in a base class
end
But it would be nice if the counter cache stayed in sync without doing anything special. I'm not sure how it would be implemented but probably involves this: http://apidock.com/rails/ActiveRecord/CounterCache/reset_counters
I can't seem to find a 'great' way to hook up polymorphic has many. Any preferred workarounds?
Hello! What does the roadmap look like for the next gem release? We're using a few features from master and would love to be able to use a true released version before releasing.
I tried out amoeba and running in a problem. My model has an after_create callback where some associated datas will be created. The user can change these predefined datas. On dup I will duplicate the changed associated datas. The original data I will not clone.
I'm trying to load index action
My model
class Responder < ActiveRecord::Base
has_many :responder_actions
validates :name, presence: true, uniqueness: true
accepts_nested_attributes_for :responder_actions
def self.search(search)
if search
where('name LIKE ?', "%#{search}%").order(id: :desc)
else
all.order(id: :desc)
end
end
amoeba do
enable
prepend :title => "Copy of "
include_association :responder_actions
end
end
I'm getting an error
NoMethodError at /dashboard/messages/responders
undefined method `include_association' for #<Amoeba::Dsl::Config:0x007ff83c37cd78>
Where is my mistake?
amoeba 2.1.0
rails 4.1.7
So i have Tour model wich defines amoeba like this:
amoeba do
enable
append :name => " COPY"
include_field [:tags, :managers]
clone [:days, :departures, :assets, :prices, :locations]
end
All related and relation models has this:
amoeba do
enable
end
From 'rails c' i do
Tour.first.amoeba_dup.save!
And model itself is duplicated.. But relations to tags and managers does not respected aswell as other relations does not get cloned...
All i see is INSERT SQL query to Tours table..
Hi, first amoeba is a great gem and i really enjoy using it :)
But now my following situation doesn't seem to work:
3 Models:
project.rb
has_many :risks
has_many :targets
amoeba do
enable
prepend :project_name => "Copy of: "
end
risk.rb
belongs_to :project
has_many :risk_target_allocations
has_many :targets, :through => :risk_target_allocations
amoeba do
enable
end
target.rb
belongs_to :project
has_many :risk_target_allocations
has_many :risks, :through => :risk_target_allocations
With the setup described above amoeba generates a copy of a project, the risks of that project and the targets of that project. But the association between the copied risks and the copied targets isn't showing up anymore.
I'm not sure if this is a real issue or that I've done something wrong.
Thanks for some feedback.
Given I have a post and comments, if Comment has:
belongs_to :post
validates :post, presence: true
Then when I do post.amoeba_dup
it clones the post and the comments.
However if my Comment model has:
belongs_to :post
validates :post_id, presence: true
amoeba won't set the post_id
, so the comments are not saved, the error for comments contains:
@messages={:post_id=>["can't be blank"]}
Sorry to open the ticket so fast again, but the serialization only had one problem and then it works with the 'old_post'
def load(str)
str.split(',').collect(&:to_i)
end
I was missing the to_i
so it was trying to match ['1'] to [1]. This all seems rather silly but I use it in my app to store a path so I can search for [6,7] in the database as '6,7'. It is working fine, i just thought i would simplify the cloning.
I wish there was more documentation for the custom serialization to actually prove it's a Rails feature ;-) The only thing I can find in the code is here:
activerecord/lib/active_record/attribute_methods/serialization.rb:
coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
class_name
else
Coders::YAMLColumn.new(class_name)
end
After fixing the custom serializer the old_post works fine, but when cloning it throws an exception
NoMethodError: undefined method `to_i' for [1, 2]:Array
from /Users/a/RubyMineProjects/amoeba/spec/support/models.rb:79:in `collect'
from /Users/a/RubyMineProjects/amoeba/spec/support/models.rb:79:in `load'
from /Users/a/.rvm/gems/ruby-1.9.3-p125-falcon@amoeba/gems/activerecord-3.2.3/lib/active_record/attribute_methods/serialization.rb:24:in `unserialize'
from /Users/a/.rvm/gems/ruby-1.9.3-p125-falcon@amoeba/gems/activerecord-3.2.3/lib/active_record/attribute_methods/serialization.rb:15:in `unserialized_value'
So what is interesting is that if you take the first custom_thing and you dup & save it, it works fine. But when you dup and save the post object, it throws the exception. So there is something happening through the recursion. I looked at the code but don't see anything that would explain it to me.
I want create clone for all references from user1 to user2, both present in db
When creating a deep-copy of a model, I'd like to pass in a parameter to be used in the preprocessing of fields. Is it possible to send parameters in to model.dup? Or is it necessary to instead do this logic separately after running the dup (but before actually saving the deep-copy to the database).
Thanks for your help--not to mention writing a helpful gem!
This gem is amazing. This is some serious meta-fu. Is very impressive.
I am using a custom serialization class in one of my models and I am not getting a correct clone.
This is probably some bad programming but I have a class to store an array of numbers as a sorted comma separated string. I don't think it is cloning correctly.
class ArrayPack
def load(array_or_text)
array_or_text = array_or_text.split(',').collect(&:to_i) if array_or_text.class == String
return [] if array_or_text.blank?
raise "Need to search a string or array, you passed #{array_or_text}" unless array_or_text.class == Array
array_or_text.collect(&:to_i).sort
end
def dump(int_array)
self.load(int_array).join(',')
end
end
serialize :path, ArrayPack.new
Hi,
We experiment some troubles lately, as took us time to find the root of the problem, here it is with the solution, to keep a record for next ones :
In 2.0.0 version of amoeba, an object which has a self references through a belongs_to and a has_one make a second record on cloning.
class Post < ActiveRecord::Base
belongs_to :next_post, class_name: 'Post', inverse_of: :father_post
has_one :father_post, class_name: 'Post', dependent: :nullify, inverse_of: :next_post, foreign_key: :next_post_id
amoeba do
enable
clone :comments
end
To resolve that we had to add a '''exclude_field :father_post''' inside the amoeaba block.
NB: it is exclude_field
and NOT exclude_association
in 2.0.0
Dunno how and why it would create another record in addition to the clone but that how we stopped it from doing it !
Cheers.
before_validation seems to be called twice.
Callback:
before_validation :convert_tax_to_decimal, if: :tax_changed?
private
def convert_tax_to_decimal
puts 'here'
self.tax ||= 0
self.tax = tax / 100
end
Controller:
job = current_business.jobs.find(params[:job_id])
copy_job = job.amoeba_dup
copy_job.save!
copy_job.build_client if copy_job.client.nil?
redirect_to edit_job_path(copy_job)
Output:
here
here
Hi guys,
I have a small challenge. I'd like to apply different configs for amoeba, when I have different cases of cloning my objects.
Let assume that I have class Job
with fields: name
, description
. The thing is that in one case I'd like to clone name
& description
and in the other case, I'd like to skip cloning description.
The other example can be, that I have a structure: MilestoneTemplate
, TaskTemplate
. Milestone template has many task templates. For one case I cast objects into the other classes: Milestone
& Task
and in the other case, I'd like to keep the original classes.
I'm not sure how to handle these cases - conditionally applying logic for cloning across multiple classes. Most likely I'll write custom thing for that. I'd really like to use / extend amoeba for this case. Maybe it could be solved by e.g. using refinements and overriding method for cloning?
Amoeba config block:
amoeba do
enable
include_association :redacted_associatons_1
include_association :redacted_associatons_2
include_association :redacted_associatons_3
nullify :created_at
nullify :updated_at
nullify :published_at
set published: false
prepend title: 'Copy of '
end
Here's the error:
/Users/austinsaylor/.rvm/gems/ruby-1.9.3-p484/gems/hobo_support-2.0.1/lib/hobo_support/string.rb:23:in `class_eval': undefined method `include_association' for #<Amoeba::Dsl::Config:0x007fbea38eca60> (NoMethodError)
Could it be a conflict with Hobo? (http://www.hobocentral.net/manual/about)
The amoeba_dup works with the nullify, set, and prepend config when include_association isn't present.
When I call amoeba_dup, everything works correctly, except that all cloned models and associations are committed to the database without calling save. Here is my config block:
35 amoeba do
36 include_field [:campaign_providers, :data_fields, :delivery_methods]
37 append :name => ' (copy)'
38 end
in the console i type: ModelName.last.amoeba_dup and all models and associations are saved/commited. This should only happen when i save the model after calling amoeba_dup.
Howdy,
I understand that as things are, include_field and exclude_field are really only for associations (it might be more clear to new users to call them include_assoc and exclude_assoc, just a thought as the 'field' part tripped me up a bit at first). However, I can see some serious benefit to allowing them and the associated behavior to be used for normal, non-associative record attributes as well.
For example, I have a model with 30 fields in it and when a dup() is called, I want to exclude 25 of them. For those 25, I want them to get the default value for a new record. Of the 5 remaining, 3 are associations and 2 are regular fields. If I could use the inclusive model for everything, then my amoeba block would be nice and short (5 include_field or one with an array of the fields/attrs to copy and leave all the rest alone). Right now, I have a lengthy series of nullify lines (which don't exactly do the same thing as exclude_ would -- see the other issue I created about nullify).
Alternately, if refitting the include/exclude_field methods is a problem, would be to add include/exclude_attr that would work the same as include/exclude_field, but for non-assoc fields.
Hi @simonoff I just tried transfering ownership to the amoeba-rb organization but github will not let me do that unless I have admin rights.
Hi,
We use amoeba to preview modifications on our data models, but we have an issue with deep inheritance as described below:
Basically a box has_many sub_products. In our example, box.sub_products
returns SubSubProduct objects and we are unable to access to a parent class association, like in the graph above.
box.amoeba_dup.sub_products.first.another_product
returns nil instead of AnotherProduct instance.
We have created a dummy app that reproduces the problem, you can find specs in the following repository: https://github.com/kdisneur/amoeba_inheritance
We've been really happy with amoeba so far and we're sorry we don't come with a fix (for now) but we wanted to share the issue with you.
Kevin
I'd like to use include/exclude_association with conditions, similar to the :if option of the validation methods in rails. For example
amoeba do
include_association :tags, if: my_condition_method?
end
def my_condition_method?
# ... do some stuff that returns bool
end
Any chance that you build that in?
Howdy,
Reading over the docs for nullify, while it does say that it puts nil into specified fields, it also says those fields should take on their default values. At least for us using PostgreSQL, that is not the case. The nil values installed via 'nullify' as nil/null and then are stored that way (or blow up in the save for fields that have default values, but do not allow null values).
I suspect this is a DB thing and may be different between DBs.
What we are really looking for is a way to exclude copying a simple field/attribute so that the 'new' record (the copy) has the initial/default value as if you had just executed a .new on the model. Perhaps a "default" command that, like "nullify", would not copy the value but would leave it in an unmolested state (i.e. DB default, like .new). Or, alternately, from my other post, extend the concept of "exclude/include_field" to record attributes and/or add a exclude/include_attr that would leave un-specified record attribute fields alone.
Is it possible to prevent a child from being copied based on some condition?
I tried amoeba in a rails 2 console (with Ruby 2.1):
class Dict < ActiveRecord::Base
belongs_to :user
has_many :cards, dependent: :destroy
has_many :idioms, through: :cards
end
class Card < ActiveRecord::Base
belongs_to :dict
has_many :idioms, dependent: :destroy
amoeba do
exclude_field :dict_id
end
end
class Idiom < ActiveRecord::Base
belongs_to :card
amoeba do
include_field :repres
include_field :card_id
include_field :kind
include_field :note
end
end
NOW IN THE CONSOLE:
c=Card.find_by_id(19) # yields a Card object
c.amoeba_dup
THIS RAISES THE EXCEPTION:
NoMethodError: undefined method `macro' for nil:NilClass
from .../amoeba-2.0.0/lib/amoeba.rb:412:in `amo_process_association'
from .../amoeba-2.0.0/lib/amoeba.rb:381:in `block in amoeba_dup'
from .../amoeba-2.0.0/lib/amoeba.rb:379:in `each'
from ..../amoeba-2.0.0/lib/amoeba.rb:379:in `amoeba_dup'
from .../amoeba-2.0.0/lib/amoeba.rb:457:in `block in amo_process_association'
Where did I make a mistake?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.