2013-01-13 11 views
8

मैं पेपरक्लिप के साथ रेल पर रूबी के लिए कुछ छवि अपलोड कोड लिख रहा हूं, और मेरे पास एक कामकाजी समाधान है लेकिन यह बहुत हैकी है इसलिए मैं वास्तव में बेहतर कार्यान्वित करने के बारे में सलाह की सराहना करता हूं यह। मेरे पास एक 'संपत्ति' श्रेणी है जिसमें अपलोड की गई छवियों के बारे में जानकारी शामिल है जिसमें पेपरक्लिप अटैचमेंट, और 'जेनरेटर' क्लास शामिल है जो साइजिंग जानकारी को समाहित करता है। प्रत्येक 'परियोजना' में कई संपत्तियां और जनरेटर होते हैं; सभी जनरेटर द्वारा निर्दिष्ट आकारों के अनुसार सभी संपत्तियों का आकार बदलना चाहिए; इसलिए प्रत्येक परियोजना के आकार का एक निश्चित सेट होता है कि इसकी सभी संपत्तियों में होना चाहिए।रेल पर रूबी - पेपरक्लिप और गतिशील पैरामीटर

जेनरेटर मॉडल:

class Generator < ActiveRecord::Base 
    attr_accessible :height, :width 

    belongs_to :project 

    def sym 
    "#{self.width}x#{self.height}".to_sym 
    end 
end 

एसेट मॉडल:

class Asset < ActiveRecord::Base 
    attr_accessible :filename, 
    :image # etc. 
    attr_accessor :generators 

    has_attached_file :image, 
    :styles => lambda { |a| a.instance.styles } 

    belongs_to :project 

    # this is utterly horrendous 
    def styles 
    s = {} 
    if @generators == nil 
     @generators = self.project.generators 
    end 

    @generators.each do |g| 
     s[g.sym] = "#{g.width}x#{g.height}" 
    end 
    s 
    end 
end 

एसेट नियंत्रक बनाने के विधि:

def create 
    @project = Project.find(params[:project_id]) 
    @asset = Asset.new 
    @asset.generators = @project.generators 
    @asset.update_attributes(params[:asset]) 
    @asset.project = @project 
    @asset.uploaded_by = current_user 

    respond_to do |format| 
     if @asset.save_(current_user) 
     @project.last_asset = @asset 
     @project.save 

     format.html { redirect_to project_asset_url(@asset.project, @asset), notice: 'Asset was successfully created.' } 
     format.json { render json: @asset, status: :created, location: @asset } 
     else 
     format.html { render action: "new" } 
     format.json { render json: @asset.errors, status: :unprocessable_entity } 
     end 
    end 
    end 
समस्या मैं कर रहा हूँ

एक चिकन अंडे मुद्दा है: नव बनाई गई संपत्ति को पता नहीं है कि कौन से जनरेटर (आकार विनिर्देश) का उपयोग तब तक किया जा सकता है जब तक इसे तुरंत चालू नहीं किया जाता है ly। मैंने @ project.assets.build का उपयोग करने की कोशिश की, लेकिन फिर एसेट को प्रोजेक्ट एसोसिएशन सेट होने और मेरे ऊपर निकलने से पहले पेपरक्लिप कोड अभी भी निष्पादित किया गया है।

'if @generators == nil' हैक है तो अद्यतन विधि नियंत्रक में आगे हैकिंग के बिना काम करेगी।

सब कुछ बहुत बुरा लगता है। क्या कोई सुझाव दे सकता है कि इसे और अधिक समझदार तरीके से कैसे लिखना है, या इस तरह की चीज लेने के लिए एक दृष्टिकोण भी?

अग्रिम धन्यवाद! :)

+0

यह सवाल है और यह जवाब मेरे App में एक ही सुविधा के निर्माण के लिए एक बहुत मदद की है। हर किसी को धन्यवाद! :) – Gediminas

उत्तर

15

मैं एक पॉलिमॉर्फिक रिश्ते के साथ संबंधित मॉडल के आधार पर गतिशील शैलियों का उपयोग करने की कोशिश कर रहे एक परियोजना पर एक ही पेपरक्लिप चिकन/अंडे की समस्या में भाग गया। मैंने अपने समाधान को अपने मौजूदा कोड में अनुकूलित कर लिया है। एक व्याख्या इस प्रकार है:

class Asset < ActiveRecord::Base 
    attr_accessible :image, :deferred_image 
    attr_writer :deferred_image 

    has_attached_file :image, 
    :styles => lambda { |a| a.instance.styles } 

    belongs_to :project 

    after_save :assign_deferred_image 

    def styles 
    project.generators.each_with_object({}) { |g, hsh| hsh[g.sym] = "#{g.width}x#{g.height}" } 
    end 

    private 
    def assign_deferred_image 
    if @deferred_image 
     self.image = @deferred_image 
     @deferred_image = nil 
     save! 
    end 
    end 
end 

असल में, पेपरक्लिप के मुद्दे गतिशील शैलियों को पुनः प्राप्त करने से पहले परियोजना संबंध जानकारी प्रचारित कर दिया गया है, तो आप (एक गैर पेपरक्लिप विशेषता को image विशेषताओं के सभी प्रदान कर सकते हैं की कोशिश कर रहा से बचने के लिए इस उदाहरण में, मैंने इसे deferred_image नाम दिया है)। after_save हुक @deferred_image से self.image का मान निर्दिष्ट करता है, जो सभी पेपरक्लिप जैज़ को बंद कर देता है।

आपका नियंत्रक हो जाता है:

# AssetsController 
def create 
    @project = Project.find(params[:project_id]) 
    @asset = @project.assets.build(params[:asset]) 
    @asset.uploaded_by = current_user 

    respond_to do |format| 
    # all this is unrelated and can stay the same 
    end 
end 

और दृश्य:

<%= form_for @asset do |f| %> 
    <%# other asset attributes %> 
    <%= f.label :deferred_upload %> 
    <%= f.file_field :deferred_upload %> 
    <%= f.submit %> 
<% end %> 

यह समाधान भी Project मॉडल में assets संबंध के लिए accepts_nested_attributes का उपयोग कर (जो वर्तमान में मैं इसे कैसे उपयोग कर रहा हूँ है की अनुमति देता है - परियोजना बनाने/संपादित करने के हिस्से के रूप में संपत्तियों को अपलोड करने के लिए)।

इस दृष्टिकोण के लिए कुछ कमियां हैं (उदा। Asset उदाहरण की वैधता के संबंध में पेपरक्लिप image मान्य मुश्किल हो जाता है), लेकिन यह सबसे अच्छा मैं बंदर की कमी पेपरक्लिप पैचिंग किसी भी तरह के निष्पादन को स्थगित करने के साथ आ सकता है style विधि जब तक कि एसोसिएशन की जानकारी पॉप्युलेट नहीं हुई थी।

मैं इस प्रश्न पर नजर रखूंगा कि यह देखने के लिए कि किसी के पास इस समस्या का बेहतर समाधान है या नहीं!


कम से कम, अगर आप अपने एक ही समाधान का उपयोग कर रखने के लिए चुनते हैं, तो आप निम्नलिखित शैलीगत सुधार अपने Asset#styles विधि करने के लिए कर सकते हैं:

def styles 
    (@generators || project.generators).each_with_object({}) { |g, hsh| hsh[g.sym] = "#{g.width}x#{g.height}" } 
end 

अपने मौजूदा पद्धति के रूप में सटीक एक ही बात करता है , लेकिन अधिक संक्षेप में।

+0

अरे वहाँ, आपके उत्तर के लिए धन्यवाद! :) – Dave

5

जबकि मुझे वास्तव में कैड के समाधान पसंद हैं, बस एक सुझाव। ऐसा लगता है कि 'शैलियों' एक परियोजना से संबंधित हैं ... तो आप जेनरेटर की गणना क्यों नहीं कर रहे हैं?

उदाहरण के लिए:

class Asset < ActiveRecord::Base 
    attr_accessible :filename, 
    :image # etc. 
    attr_accessor :generators 

    has_attached_file :image, 
    :styles => lambda { |a| a.instance.project.styles } 
end 


class Project < ActiveRecord::Base 
    .... 

    def styles 
    @generators ||= self.generators.inject {} do |hash, g| 
     hash[g.sym] = "#{g.width}x#{g.height}" 
    end 
    end 
end 

संपादित करें: के लिए अपने नियंत्रक बदलने का प्रयास करें (परियोजना संभालने कई संपत्तियां हैं):

def create 
    @project = Project.find(params[:project_id]) 
    @asset = @project.assets.new 
    @asset.generators = @project.generators 
    @asset.update_attributes(params[:asset]) 
    @asset.uploaded_by = current_user 
end 
+0

जबकि वास्तविक शैलियों विधि का पता लगाने का सवाल एक दिलचस्प है, यह एक नए रिकॉर्ड के साथ काम नहीं करेगा। शैली lambda में, 'a.instance.project' रिकॉर्ड' सहेजने तक 'nil' होगा, इसलिए आपको' nil' पर 'NoMethodError' मिलेगा। – Cade

+0

मैंने अपना समाधान संपादित किया ... मुझे यकीन है कि उदाहरण @सेट वैरिएबल को इंगित करना चाहिए? यदि आपने नियंत्रक को मेरे जैसा बदल दिया है, तो इसके पास पेपरक्लिप के लिए एक छिपे हुए इंस्टेंस वैरिएबल के अंदर प्रोजेक्ट होना चाहिए ... –

+0

रेल एक प्रोजेक्ट मॉडल, स्टाइल एक्शन पर एक त्रुटि डालता है। कहते हैं: 'वाक्यविन्यास त्रुटि, अप्रत्याशित कीवर्ड_do_block, keyword_end' की अपेक्षा – Gediminas

2

मैं सिर्फ एक समान समस्या यह है कि मैं था समाधान कर लिया है। मेरी "शैलियों" लैम्ब्डा में मैं "श्रेणी" विशेषता के मूल्य के आधार पर एक अलग शैली लौट रहा हूं। समस्या यह है कि Image.new (attrs), और image.update_attributes (attrs) गुणों को अनुमानित क्रम में सेट नहीं करता है, और इस प्रकार मुझे गारंटी नहीं दी जा सकती है कि image.category के पास मेरी शैलियों लैम्ब्डा से पहले एक मूल्य होगा कहा जाता है।

class Image 
    ... 
    has_attached_file :image, :styles => my_lambda, ... 
    ... 
    def attributes=(new_attributes, guard_protected_attributes = true) 
    return unless new_attributes.is_a?(Hash) 
    if new_attributes.key?("image") 
     only_attached_file = { 
     "image" => new_attributes["image"] 
     } 
     without_attached_file = new_attributes 
     without_attached_file.delete("image") 
     # set the non-paperclip attributes first 
     super(without_attached_file, guard_protected_attributes) 
     # set the paperclip attribute(s) after 
     super(only_attached_file, guard_protected_attributes) 
    else 
     super(new_attributes, guard_protected_attributes) 
    end 
    end 
    ... 
end 

यह सुनिश्चित करता है कि पेपरक्लिप विशेषता अन्य विशेषताओं के बाद सेट कर दिया जाता है और इस तरह उन्हें एक में उपयोग कर सकते हैं: शैली लैम्ब्डा मेरे समाधान विशेषताओं =() ओवरराइड करने के लिए मेरी छवि मॉडल में इस प्रकार था।

यह स्पष्ट रूप से उन परिस्थितियों में सहायता नहीं करेगा जहां पेपरक्लिप विशेषता "मैन्युअल रूप से" सेट है। हालांकि उन परिस्थितियों में आप एक समझदार आदेश निर्दिष्ट करके स्वयं की मदद कर सकते हैं। मेरे मामले में मैं लिख सकते हैं:

image = Image.new 
image.category = "some category" 
image.image = File.open("/somefile") # styles lambda can use the "category" attribute 
image.save! 

(पेपरक्लिप 2.7.4, रेल 3, माणिक 1.8.7)

संबंधित मुद्दे