Guest User

Untitled

a guest
May 25th, 2018
217
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.53 KB | None | 0 0
  1. # Oleg Andreev <oleganza@gmail.com>
  2. # November 3, 2008
  3. # (code is awful, but it works)
  4. #
  5. # The idea is to avoid composite SQL queries when association collection is already here,
  6. # but to use composite SQL query when collection is not here to load less data:
  7. #
  8. # # When no data available:
  9. #
  10. # person.articles.first(:coauthor_id => 2)
  11. # # => SELECT * FROM articles WHERE ((person_id = 1) AND (coauthor_id = 2)) LIMIT 1
  12. #
  13. # # When all collection is loaded already: (e.g. by `person.articles.to_a`)
  14. #
  15. # person.articles.first(:coauthor_id => 2)
  16. # # => person.articles.detect{|a| a.coauthor_id == 2 } # no sql request!
  17. #
  18. #
  19. module DataMapper
  20. module Associations
  21. module OneToMany
  22. class Proxy
  23.  
  24. def first(*args)
  25. if args.last.respond_to?(:merge)
  26. query = args.pop
  27. # Patch start.
  28. if c = @children
  29. Query.filter_first(c, query)
  30. else # Patch end.
  31. @relationship.get_children(@parent, query, :first, *args)
  32. end
  33. else
  34. super
  35. end
  36. end # first
  37.  
  38.  
  39. def all(query = {})
  40. if query.empty?
  41. self
  42. else
  43. # Patch start.
  44. if c = @children
  45. Query.filter_all(c, query)
  46. else # Patch end.
  47. @relationship.get_children(@parent, query)
  48. end
  49. end
  50. end # all
  51.  
  52. end # Proxy
  53. end # OneToMany
  54. end # Associations
  55.  
  56. class Query
  57. class <<self
  58. def filter_first(collection, hash)
  59. hash = hash.dup
  60. collection = _prefilter_collection(collection, hash)
  61. collection.detect do |r|
  62. filter_record(r, hash)
  63. end
  64. end
  65.  
  66. def filter_all(collection, hash)
  67. hash = hash.dup
  68. collection = _prefilter_collection(collection, hash)
  69. collection.select do |r|
  70. filter_record(r, hash)
  71. end
  72. end
  73.  
  74. def filter_record(r, hash)
  75. hash.each do |k, v|
  76. return false unless k.__native_apply__(r, v)
  77. end
  78. end
  79.  
  80. # TODO: :limit, :offset
  81. def _prefilter_collection(collection, hash)
  82. if order = hash.delete(:order)
  83. order = [ order ] unless Array === order
  84. collection = collection.sort do |a, b|
  85. __order_tuple__(a, b, order)
  86. end
  87. end
  88. collection
  89. end
  90.  
  91. def __order_tuple__(a, b, order)
  92. order.each do |operator|
  93. r = operator.__native_order__(a,b)
  94. return r unless r == 0
  95. end
  96. return 0
  97. end
  98. end # Query eigenclass
  99.  
  100. class Operator
  101. def __native_apply__(r, v)
  102. left = r.send(@target)
  103. case @operator
  104. when :gt: left > v
  105. when :gte: left >= v
  106. when :lt: left < v
  107. when :lte: left <= v
  108. when :not: left != v
  109. when :eql: left == v
  110. when :like: left =~ /^#{v.gsub(/%/,'.*')}$/u
  111. when :in: v.include?(left)
  112. end
  113. end
  114. def __native_order__(a,b)
  115. a = a.send(@target)
  116. b = b.send(@target)
  117. r = (a <=> b)
  118. r *= -1 if @operator == :desc
  119. r
  120. end
  121. end
  122.  
  123. class ::Symbol
  124. def __native_apply__(r, v)
  125. r.send(self) == v
  126. end
  127. def __native_order__(a,b) # ascending
  128. a.__send__(self) <=> b.__send__(self)
  129. end
  130. end
  131.  
  132. end # Query
  133. end # DM
Add Comment
Please, Sign In to add comment