Rubyã§ã¡ã½ãããã§ã¤ã³å¯è½ãªã¯ã¨ãªãªãã¸ã§ã¯ããæ¸ã
ã²ã¨ã§ããã®ãã®åã®YAPCã§ã®çºè¡¨ï¼ãã¹ãã¹ãã¼ã«ã¼ããã§ã¨ããããã¾ãï¼ã® ã¯ã¦ãªããã°ã§ã¯ãã¡ã¤ã³é§åãªã©ä½¿ã£ã¦ã¤ã±ã¦ããµã¼ãã¹ãä½ãç¶ãã¦ãããã£ã¦ãã話ã ãªãã¸ã§ã¯ãæåãããã¡ã¤ã³é§åã®è©±ã¨ããããããã£ãããã©ãã çµå±ã®ã¨ããã¤ã±ã¦ããµã¼ãã¹ãä½ãç¶ããããã«ã¯ç¶ç¶çã«ã³ã¼ããã¼ã¹ãè¯ããã¦ãããªããã°ãããªã ã£ã¦ããã¡ãã»ã¼ã¸ããã£ãã®ã ã£ãã®ã ã¨åæã«è§£éãã¦ãã¦ãããå ±æãã¦ããã
ã¨ãªãã¯ã»ã¨ã´ã¡ã³ã¹ã®æ¬èªä½è²·ã£ã¦ä¸éå端ã«èªãã§èªã¿ã£ã±ãªããªã®ã§ã ãã¡ã¤ã³é§åè¨è¨ã«ã¤ãã¦ã¯ä½ãæè¦ã§ããã¨ããã¯ã¾ã ãªãã
ã§ãæè¿æãã®ããè¤éãªä»æ§ãæç¶ãçã«å®è£ ããã¨ç¢ºå®ã«è¤éãªã¾ã¾ã«ãªã£ã¦ãã¾ãã®ã§ã ãã£ã±ããã¸ãã¹ä¸ã«ç¾ããã«ã¼ã«ã¯åèªã§è¡¨ç¾ãã¦ã³ã¼ãã«èµ·ããã¨ããä½æ¥ãããªã㨠ãã¡ã ã¨æã£ã¦ãããæ°æ¥éå»ã«ä½ã£ãã³ã¼ãã«æãå ããã¤ãã§ã«å¤§å¹ ã«æ¸ãç´ãã¦ããã
ã¾ã ã¡ããã¨èº«ã«ã¤ããããã§ã¯ãªããã©ãã¨ã´ã¡ã³ã¹æ¬èªã¿è¿ãã¦ããä¸è¦ã¤ããã Specificationãã¿ã¼ã³ãç´ æµã ãªã¨æã£ã¦ãã¦ä»å®è£ ãã¦ããè¤éãªä»æ§ã®å®è£ ã«æ´»ç¨ãããã¨ãã¦ããã
Specificationãã¿ã¼ã³ãå©ç¨ãããã¨ããã¨ãActiveRecordã«ç´ã«ä¾åããªãã¬ã¤ã¤ã¼ã¨ã㦠Repositoryãå®è£ ããªãã¨ãããªããªãã¨ãªã£ã¦æã£ã¦ãããRailsã®ä¸ã«åãè¾¼ãã§ããã¨ã©ããªããèãã以ä¸ã®ãããªã·ã³ãã«ãªã©ã¤ãã©ãªãæ¸ããã
module ChainableQuery def initialize(init_value, filters = []) @init_value = init_value @filters = filters.dup.freeze end def add_filter(&block) self.class.new(@init_value, [*@filters, block]) end def value @filters.reduce(@init_value) { |last_value, filter| filter.call(last_value) } end end
ãã便å©ã§ãç°¡åã«é 延è©ä¾¡ããã¦ã¡ã½ãããã§ã¼ã³åºæ¥ãæ±ç¨çãªã¯ã¨ãªãªãã¸ã§ã¯ããä½ããã ä¾ãã°Userã¯ã©ã¹ã®çµãè¾¼ã¿å¦çãªã©ãæã£ãRepositoryãå®ç¾©ãããã¨ããã¨ä»¥ä¸ã®ããã«æ¸ããã
class UserRepository def initialize(scope = User) @scope end def query Query.new(@scope) end def find_piad_premium_user query.and_premium.and_paid.value end class Query include ::ChainableQuery def and_premium add_filter { |query| query.where(is_premium: true) } end def and_paid add_filter { |query| query.select { |user| user.payments.exists? } } end def and_has_profile_image add_filter { |query| query.where("profile_image_url IS NOT NULL") } end end end repository = UserRepository.new premium_users = repository.find_paid_premium_users
ãµã³ãã«ã¯ãããé©å½ã ãã©ãåæç¡æãã¯ã¼ãã³ã®å©ç¨ãªã©ã®ããã§å®éã«ã¾ã 課éãããã¨ããªãã¦ã¼ã¶ã§ã¯ãªãã ä½ããã®æ±ºæ¸æ段ã§èª²éãããã¨ãæããããªã¦ã¼ã¶ã®ãªãã¸ã§ã¯ããåå¾ããå¦çãâã®ããã«ã ã¡ã½ãããã§ã¤ã³ãå©ç¨ããã¯ã¨ãªãªãã¸ã§ã¯ããéãã¦ã¡ã½ãããæ¸ããã¯ãã
Railsã ã£ããããããã®ã¯ã ãããScopeã¨ãã¦å®ç¾©ãã¦ããã°æ¸ãã®ã ãã©ã ãããã¦ãããã¨ã®ã¡ãªããã¨ãã¦ãã®å ´åã¯ãActiveRecordï¼MySQLï¼ã«ä¾åããªãå¦çãªã©ã æµãããããªã¤ã³ã¿ã¼ãã§ã¤ã¹ã§åãåããå¦çãå¯è½ã«ãªã£ãããSpecificationã¯ã©ã¹ããæ¡ä»¶ãæºãã ãã¼ã¿ãç°¡åã«å¼ãåºããããã«ãªããã¨ã ã¨ã¯æãã
DDD詳ãããªãã®ã§ãã»ãã¨ãã«è¯ããã©ããããããªããã©ãã¡ã½ãããã§ã¼ã³èªä½ã¯æ°æã¡ãã®ã§ã ActiveRecordã«ä¾åããããªãæ°æã¡ã«ãªã£ãæã«ã¯ãã®ãããªæ¸ãæ¹ã試ããããã¦ã¿ãã
è¯ãè¨è¨ç®æãã¦æ¯ãé£ããã°ããã¾ãããã