আমি কি রুবি মডিউলে এটি যুক্ত না করে কোনও উদাহরণ পদ্ধতিতে আবেদন করতে পারি?


180

পটভূমি:

আমার একটি মডিউল রয়েছে যা বেশ কয়েকটি উদাহরণ পদ্ধতি ঘোষণা করে

module UsefulThings
  def get_file; ...
  def delete_file; ...

  def format_text(x); ...
end

এবং আমি ক্লাসের মধ্যে থেকে এই পদ্ধতিগুলির কয়েকটি কল করতে চাই। আপনি রুবিতে সাধারণত কীভাবে এটি করেন তা হ'ল:

class UsefulWorker
  include UsefulThings

  def do_work
    format_text("abc")
    ...
  end
end

সমস্যা

include UsefulThingsথেকে সমস্ত পদ্ধতি এনেছে UsefulThings। এই ক্ষেত্রে আমি কেবল চাই format_textএবং স্পষ্টতই চাই না get_fileএবং চাই delete_file

আমি এর বেশ কয়েকটি সম্ভাব্য সমাধান দেখতে পাচ্ছি:

  1. কোনওভাবে এটিকে কোথাও না জুড়ে সরাসরি মডিউলটিতে সরাসরি অনুরোধ করুন
    • আমি জানি না কীভাবে / যদি এটি করা যায়। (সুতরাং এই প্রশ্ন)
  2. একরকম অন্তর্ভুক্ত করুন Usefulthingsএবং কেবল এর কিছু পদ্ধতি আনুন
    • কীভাবে / যদি এটি করা যায় তবে আমি জানি না
  3. একটি প্রক্সি ক্লাস তৈরি করুন, এতে অন্তর্ভুক্ত করুন UsefulThings, তারপরে format_textসেই প্রক্সি উদাহরণটিতে প্রতিনিধিত্ব করুন
    • এটি কাজ করবে তবে বেনামে প্রক্সি ক্লাসগুলি হ্যাক। ইশ।
  4. মডিউলটি 2 বা ততোধিক ছোট মডিউলে বিভক্ত করুন
    • এটিও কাজ করবে, এবং সম্ভবত আমি সবচেয়ে ভাল সমাধানটি ভাবতে পারি, তবে আমি এড়াতে পছন্দ করতাম কারণ আমি কয়েক ডজন এবং কয়েক ডজন মডিউলের বিস্তার লাভ করতাম - এটি পরিচালনা করা ভারী হবে d

কেন একটি একক মডিউলে প্রচুর সম্পর্কযুক্ত ফাংশন রয়েছে? এটি ApplicationHelperএকটি রেল অ্যাপ্লিকেশন থেকে এসেছে, যা আমাদের দল ডি-ফ্যাক্টো সিদ্ধান্ত নিয়েছে যে অন্য কোথাও থাকার মতো নির্দিষ্ট নির্দিষ্ট কিছু নয় বলে ডাম্পিং গ্রাউন্ড হিসাবে সিদ্ধান্ত নিয়েছে। বেশিরভাগ ক্ষেত্রে স্বতন্ত্র ইউটিলিটি পদ্ধতি যা সর্বত্র ব্যবহৃত হয়। আমি এটিকে পৃথক সহায়কদের মধ্যে ভেঙে ফেলতে পারতাম, তবে তাদের মধ্যে ৩০ জন থাকত, প্রত্যেকে ১ টি পদ্ধতি দিয়ে ... এটি অনুৎপর বলে মনে হচ্ছে


আপনি যদি চতুর্থ পন্থা গ্রহণ করেন (মডিউলটি বিভক্ত করে), আপনি এটি তৈরি করতে পারেন যাতে একটি মডিউল সর্বদা স্বয়ংক্রিয়ভাবে Module#includedঅন্যটির একটি ট্রিগার করতে কলব্যাক ব্যবহার করে অন্যটিকে অন্তর্ভুক্ত করে includeformat_textপদ্ধতি, এটা নিজস্ব মডিউল মধ্যে স্থানান্তরিত করা যেতে পারে যেহেতু এটি তে এটি নিজের ব্যাপার দরকারী বলে মনে হয়। এটি ম্যানেজমেন্টকে কিছুটা কম বোঝা করে তোলে।
Batkins

আমি মডিউল ফাংশনগুলির উত্তরগুলির সমস্ত রেফারেন্স দ্বারা বিভ্রান্ত হয়ে পড়েছি। মনে করুন আপনার আছে module UT; def add1; self+1; end; def add2; self+2; end; endএবং আপনি ব্যবহার করতে চান add1তবে add2শ্রেণিতে নয় Fixnum। এটির জন্য মডিউল ফাংশনগুলি কীভাবে সহায়তা করবে? আমি কিছু অনুপস্থিত করছি?
কেরি সোভোল্যান্ড

উত্তর:


135

যদি কোনও মডিউলটির কোনও পদ্ধতি মডিউল ফাংশনে রূপান্তরিত হয় তবে আপনি এটিকে কেবল মোডের বাইরে কল করতে পারেন যেন এটি ঘোষিত হয়েছে

module Mods
  def self.foo
     puts "Mods.foo(self)"
  end
end

নীচের মডিউল_ফাংশন পদ্ধতির কোনও ক্লাস ভাঙ্গা এড়ানো হবে যাতে সমস্ত মোড অন্তর্ভুক্ত থাকে।

module Mods
  def foo
    puts "Mods.foo"
  end
end

class Includer
  include Mods
end

Includer.new.foo

Mods.module_eval do
  module_function(:foo)
  public :foo
end

Includer.new.foo # this would break without public :foo above

class Thing
  def bar
    Mods.foo
  end
end

Thing.new.bar  

যাইহোক, আমি কৌতূহল করছি কেন একটি সম্পর্কহীন ফাংশনগুলির সেটগুলি একই স্থানে একই মডিউলে থাকে?

public :fooএরপরে ডাকা হলে এখনও কাজ করে তা দেখাতে সম্পাদিতmodule_function :foo


1
সরাইয়া হিসাবে, module_functionপদ্ধতিটি ব্যক্তিগতটিকে রূপান্তরিত করে, যা অন্য কোডগুলি ভেঙে দেয় - অন্যথায় এটি গ্রহণযোগ্য উত্তর হতে পারে
অরিওন এডওয়ার্ডস

আমি শালীন কাজটি শেষ করেছি এবং আমার কোডকে পৃথক মডিউলগুলিতে পুনরায় সংশোধন করেছি। এটি এতটা খারাপ ছিল না যতটা আমি ভেবেছিলাম এটি হতে পারে। আপনার উত্তরটি এখনও এটি সবচেয়ে সঠিকভাবে আমার মূল সীমাবদ্ধতার সমাধান করবে, তাই গ্রহণযোগ্য!
অরিয়ন এডওয়ার্ডস

@ ডিজিটাইজড সম্পর্কিত ফাংশনগুলি সর্বদা একটি মডিউলে শেষ হতে পারে, এর অর্থ এই নয় যে আমি সমস্তটির সাথে আমার নামস্থানটি দূষিত করতে চাই। একটি সাধারণ উদাহরণ যদি সেখানে Files.truncateএবং একটি থাকে Strings.truncateএবং আমি উভয়কে একই শ্রেণিতে, স্পষ্টতই ব্যবহার করতে চাই। আমি যখন কোনও রুবি দেব নই তবুও প্রতিবারই একটি নির্দিষ্ট পদ্ধতি প্রয়োজন বা মূলটি সংশোধন করা একটি নতুন শ্রেণি / উদাহরণ তৈরি করা একটি দুর্দান্ত পদ্ধতির নয়।
TWiStErRob

146

আমি মনে করি যে কেবলমাত্র একক কল (বিদ্যমান মডিউলগুলি পরিবর্তন না করে বা একটি নতুন তৈরি না করে) ফেলে দেওয়ার জন্য সবচেয়ে সংক্ষিপ্ততম উপায়টি নিম্নলিখিত হিসাবে থাকবে:

Class.new.extend(UsefulThings).get_file

2
ফাইল erb জন্য খুব দরকারী। html.erb, বা js.erb। ধন্যবাদ! আমি ভাবছি যদি এই সিস্টেমটি স্মৃতি অপচয় করে
অ্যালবার্ট

5
@ AlbertCatalà আমার পরীক্ষা এবং অনুযায়ী stackoverflow.com/a/23645285/54247 বেনামী ক্লাস আছে আবর্জনা, শুধু অন্য সব কিছুর সংগৃহীত তাই এটি মেমরি অপচয় করা উচিত নয়।
ডোলজেনকো

1
আপনি যদি প্রক্সি হিসাবে কোনও বেনাম শ্রেণি তৈরি করতে পছন্দ করেন না তবে আপনি পদ্ধতির জন্য প্রক্সি হিসাবে কোনও বস্তুও ব্যবহার করতে পারেন। Object.new.extend(UsefulThings).get_file
3limin4t0r

82

মডিউলটির "মালিকানা" থাকলে এটি করার আর একটি উপায় module_function

module UsefulThings
  def a
    puts "aaay"
  end
  module_function :a

  def b
    puts "beee"
  end
end

def test
  UsefulThings.a
  UsefulThings.b # Fails!  Not a module method
end

test

26
এবং আপনি নিজের মালিকানা পাচ্ছেন না এমন ক্ষেত্রে: ইউটিফুলটিংসিংসসেন্ড: মডিউল_ফানশন, বি বি
ডাস্টিন

3
মডিউল_ফংশন পদ্ধতিটি ব্যক্তিগতটিকে রূপান্তরিত করে (ভাল এটি আমার আইআরবিতে যাইহোক হয়), যা অন্যান্য কলকারীদের বিরতি দেয় :-(
অরিয়ন এডওয়ার্ডস

আমি আসলে এই পদ্ধতির পছন্দ করি, কমপক্ষে আমার উদ্দেশ্যে। এখন আমি ModuleName.method :method_nameকোনও পদ্ধতি অবজেক্ট পেতে কল করতে পারি এবং এটির মাধ্যমে কল করতে পারি method_obj.call। অন্যথায় আমাকে পদ্ধতিটি মূল বস্তুর একটি উদাহরণের সাথে আবদ্ধ করতে হবে, এটি সম্ভব নয় যদি মূল বস্তুটি মডিউল হয়। অরিয়ন এডওয়ার্ডসের প্রতিক্রিয়া হিসাবে, module_functionমূল উদাহরণ পদ্ধতিটি ব্যক্তিগত করে তোলে। রুবি
জন

2
ওরিওন - আমি বিশ্বাস করি না এটি সত্য। রুবি-ডোক.org / ডকস / প্রোগ্রামিংরুবি / html/… অনুসারে, মডিউল_ফংশন "নামকরণের পদ্ধতিগুলির জন্য মডিউল ফাংশন তৈরি করে functions এই ফাংশনগুলি মডিউলটির সাথে একটি রিসিভার হিসাবে ডাকা হতে পারে এবং ক্লাসে মিশ্রিত উদাহরণগুলির জন্যও উপলব্ধ হয়ে যায় available মডিউল। মডিউল ফাংশনগুলি মূলটির অনুলিপি, এবং তাই স্বতন্ত্রভাবে পরিবর্তিত হতে পারে instance উদাহরণ-পদ্ধতি সংস্করণগুলি ব্যক্তিগত করা হয় no
রায়ান ক্রিস্পিন হিনিস

2
আপনি এটি হিসাবে সংজ্ঞায়িত করতে পারেনdef self.a; puts 'aaay'; end
টিলো

17

আপনি যদি অন্য ক্লাসে মডিউল না রেখে এই পদ্ধতিগুলি কল করতে চান তবে আপনাকে সেগুলি মডিউল পদ্ধতি হিসাবে সংজ্ঞায়িত করতে হবে:

module UsefulThings
  def self.get_file; ...
  def self.delete_file; ...

  def self.format_text(x); ...
end

এবং তারপরে আপনি তাদের সাথে কল করতে পারেন

UsefulThings.format_text("xxx")

অথবা

UsefulThings::format_text("xxx")

তবে যাইহোক আমি আপনাকে সুপারিশ করব যে আপনি কেবলমাত্র একটি পদ্ধতিতে একটি মডিউল বা একটি শ্রেণিতে রেখে দিন। যদি আপনার যদি সমস্যা হয় যে আপনি মডিউল থেকে কেবল একটি পদ্ধতি অন্তর্ভুক্ত করতে চান তবে এটি একটি খারাপ কোড গন্ধের মতো শোনাচ্ছে এবং সম্পর্কযুক্ত পদ্ধতিগুলি একসাথে রাখা ভাল রুবি স্টাইল নয়।


17

মডিউলটি অন্তর্ভুক্ত না করে (এবং মধ্যস্থতাকারী বস্তু তৈরি না করে) একটি মডিউল উদাহরণ পদ্ধতিটি চাওয়ার জন্য:

class UsefulWorker
  def do_work
    UsefulThings.instance_method(:format_text).bind(self).call("abc")
    ...
  end
end

এই পদ্ধতির সাথে সাবধানতা অবলম্বন করুন: নিজের কাছে বাধ্যতামূলক পদ্ধতিটি যা প্রত্যাশা করে তা প্রদান করতে পারে না। উদাহরণস্বরূপ, সম্ভবত format_textমডিউল দ্বারা সরবরাহ করা অন্য পদ্ধতির অস্তিত্ব ধরে নিয়েছে, যা (সাধারণত) উপস্থিত থাকবে না।
নাথান

এই উপায়টিই, কোনও মডিউল লোড করতে পারে, মডিউল সমর্থন পদ্ধতিটি সরাসরি লোড করা যায় কিনা তা বিবেচনা করে। এমনকি মডিউল স্তরে পরিবর্তন করা ভাল। তবে কিছু ক্ষেত্রে, এই লাইনটি প্রশ্নকারীদের কী পেতে চায়।
twindai

6

প্রথমত, আমি আপনাকে প্রয়োজনীয় জিনিসগুলিতে মডিউলটি ভেঙে দেওয়ার পরামর্শ দিই। তবে আপনি সর্বদা আপনার অনুরোধের জন্য এটি বর্ধিত একটি বর্গ তৈরি করতে পারেন:

module UsefulThings
  def a
    puts "aaay"
  end
  def b
    puts "beee"
  end
end

def test
  ob = Class.new.send(:include, UsefulThings).new
  ob.a
end

test

4

উ: আপনি যদি সর্বদা এগুলিকে একটি "যোগ্য", এককভাবে (ইউজুয়াল থিংস.জেট_ফাইলে) কল করতে চান তবে অন্যরা যেমন বলেছে ঠিক তেমন এগুলি স্থির করুন,

module UsefulThings
  def self.get_file; ...
  def self.delete_file; ...

  def self.format_text(x); ...

  # Or.. make all of the "static"
  class << self
     def write_file; ...
     def commit_file; ...
  end

end

বি। আপনি যদি এখনও একই ক্ষেত্রে মিক্সিন পদ্ধতির পাশাপাশি একইভাবে এক-অফ স্ট্যান্ডোলোন অনুরোধ রাখতে চান তবে আপনার কাছে একটি ওয়ান-লাইনার মডিউল থাকতে পারে যা মিক্সিনের সাথে নিজেকে প্রসারিত করে:

module UsefulThingsMixin
  def get_file; ...
  def delete_file; ...

  def format_text(x); ...
end

module UsefulThings
  extend UsefulThingsMixin
end

সুতরাং উভয় কাজ করে:

  UsefulThings.get_file()       # one off

   class MyUser
      include UsefulThingsMixin  
      def f
         format_text             # all useful things available directly
      end
   end 

আইএমএইচও এটি module_functionপ্রতিটি একক পদ্ধতির চেয়ে ক্লিনার - যদি সেগুলি সমস্ত চায় want


extend selfএকটি সাধারণ প্রতিমা।
স্মার্ট

4

আমি যেমন প্রশ্নটি বুঝতে পারি, আপনি কোনও মডিউলের উদাহরণ পদ্ধতিগুলি কোনও শ্রেণিতে মিশ্রিত করতে চান।

মডিউল # কীভাবে কাজ করে তা বিবেচনা করে শুরু করা যাক । ধরুন আমাদের কাছে একটি মডিউল UsefulThingsরয়েছে যাতে দুটি উদাহরণ পদ্ধতি রয়েছে:

module UsefulThings
  def add1
    self + 1
  end
  def add3
    self + 3
  end
end

UsefulThings.instance_methods
  #=> [:add1, :add3]

এবং Fixnum includeযে মডিউল:

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  include UsefulThings
end

আমরা দেখতে পাচ্ছি:

Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
  #=> [:add2, :add3, :add1] 
1.add1
2 
1.add2
cat
1.add3
dog

আপনি কি UsefulThings#add3ওভাররাইডের প্রত্যাশা করছেন Fixnum#add3, যাতে 1.add3ফিরে আসবে 4? এই বিবেচনা:

Fixnum.ancestors
  #=> [Fixnum, UsefulThings, Integer, Numeric, Comparable,
  #    Object, Kernel, BasicObject] 

যখন ক্লাসটি includeমডিউল হয়, মডিউলটি শ্রেণীর সুপারক্লাসে পরিণত হয়। সুতরাং, উত্তরাধিকার কীভাবে কাজ করে তার কারণ add3হিসাবে একটি উদাহরণ প্রেরণ প্রেরণা, ফিরে আসার Fixnumকারণ হবে ।Fixnum#add3dog

এবার আসুন একটি পদ্ধতি যুক্ত :add2করুন UsefulThings:

module UsefulThings
  def add1
    self + 1
  end
  def add2
    self + 2
  end
  def add3
    self + 3
  end
end

আমরা এখন ইচ্ছুক Fixnumথেকে includeশুধুমাত্র পদ্ধতি add1এবং add3। তাই করা হয়, আমরা উপরের মত একই ফলাফল পেতে আশা করি।

ধরুন, উপরে হিসাবে, আমরা কার্যকর করি:

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  include UsefulThings
end

ফলাফলটি কি? অযাচিত পদ্ধতিতে :add2যুক্ত করা হয় Fixnum, :add1যুক্ত করা হয় এবং কারণগুলির জন্য আমি উপরে ব্যাখ্যা করেছি, :add3যোগ করা হয় না। সুতরাং আমাদের যা করতে হবে তা হল undef :add2। আমরা একটি সহজ সহায়ক পদ্ধতিতে এটি করতে পারি:

module Helpers
  def self.include_some(mod, klass, *args)
    klass.send(:include, mod)
    (mod.instance_methods - args - klass.instance_methods).each do |m|
      klass.send(:undef_method, m)
    end
  end
end

আমরা এই মত প্রার্থনা যা:

class Fixnum
  def add2
    puts "cat"
  end
  def add3
    puts "dog"
  end
  Helpers.include_some(UsefulThings, self, :add1, :add3)
end

তারপর:

Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
  #=> [:add2, :add3, :add1] 
1.add1
2 
1.add2
cat
1.add3
dog

যা আমরা চাই ফলাফল।


1

10 বছর পরে যদি কারও এখনও এটির প্রয়োজন হয় তা নিশ্চিত নয় তবে আমি ইজিঙ্ক্লাস ব্যবহার করে এটি সমাধান করেছি।

module UsefulThings
  def useful_thing_1
    "thing_1"
  end

  class << self
    include UsefulThings
  end
end

class A
  include UsefulThings
end

class B
  extend UsefulThings
end

UsefulThings.useful_thing_1 # => "thing_1"
A.new.useful_thing_1 # => "thing_1"
B.useful_thing_1 # => "thing_1"

0

প্রায় 9 বছর পরে এখানে একটি জেনেরিক সমাধান:

module CreateModuleFunctions
  def self.included(base)
    base.instance_methods.each do |method|
      base.module_eval do
        module_function(method)
        public(method)
      end
    end
  end
end

RSpec.describe CreateModuleFunctions do
  context "when included into a Module" do
    it "makes the Module's methods invokable via the Module" do
      module ModuleIncluded
        def instance_method_1;end
        def instance_method_2;end

        include CreateModuleFunctions
      end

      expect { ModuleIncluded.instance_method_1 }.to_not raise_error
    end
  end
end

আপনার যে দুর্ভাগ্যজনক কৌশলটি প্রয়োগ করতে হবে তা হল পদ্ধতিগুলি সংজ্ঞায়িত হওয়ার পরে মডিউলটি অন্তর্ভুক্ত করা। বিকল্প হিসাবে আপনি প্রসঙ্গ হিসাবে সংজ্ঞায়িত করার পরে এটি অন্তর্ভুক্ত করতে পারেনModuleIncluded.send(:include, CreateModuleFunctions)

অথবা আপনি এটি প্রতিবিম্ব_উটিলস রত্নের মাধ্যমে ব্যবহার করতে পারেন ।

spec.add_dependency "reflection_utils", ">= 0.3.0"

require 'reflection_utils'
include ReflectionUtils::CreateModuleFunctions

ঠিক আছে, আমরা দেখতে পাই যে সংখ্যাগরিষ্ঠ প্রতিক্রিয়াগুলির মতো আপনার পদ্ধতির মূল সমস্যাটি চিহ্নিত করা যায় না এবং সমস্ত পদ্ধতি লোড করা হয়। কেবলমাত্র উত্তম উত্তরটি হ'ল মূল মডিউলটি থেকে পদ্ধতিটি আবদ্ধ করা এবং এটি লক্ষ্যবস্তু শ্রেণিতে আবদ্ধ করা, যেমন @ রেইনার ইতিমধ্যে 3 বছর আগে প্রতিক্রিয়া জানিয়েছে।
জোয়েল আজমের

@ জোয়েলআজেমার আমার ধারণা আপনি এই সমাধানটি ভুল বুঝছেন। এটি আপনি যে মডিউলটি ব্যবহার করতে চান তাতে যুক্ত করতে হবে। ফলস্বরূপ এর ব্যবহারের জন্য এর কোনও পদ্ধতিরই অন্তর্ভুক্ত থাকতে হবে না। ওপির সম্ভাব্য সমাধানগুলির মধ্যে একটি হিসাবে প্রস্তাবিত: "1, কোনওভাবে এটিকে কোথাও অন্তর্ভুক্ত না করে সরাসরি মডিউলটিতে সরাসরি অনুরোধ করুন"। এটা এভাবে কাজ করে.
thisismydesign
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.