Class: Lllibrary::DSL
- Inherits:
-
Object
- Object
- Lllibrary::DSL
- Defined in:
- lib/lllibrary/dsl.rb
Overview
Contains all the selectors used in Lllibrary’s DSL. See Lllibrary#select for how to access the DSL.
Instance Method Summary collapse
-
#all ⇒ Object
Selects all the tracks in the library.
-
#initialize(library) ⇒ DSL
constructor
A new instance of DSL.
-
#method_missing(field, *args) ⇒ Object
Turns all the columns on the tracks table into methods that I call selectors.
-
#none ⇒ Object
Selects no tracks, returning an empty playlist.
-
#numeric_selector(column, *values_or_hash) ⇒ Object
Returns an Array of tracks that satisfy certain conditions on the given numeric column.
-
#playlist(*names) ⇒ Object
Returns an Array of all tracks that are in a Playlist whose name matches one of the strings given to this selector.
-
#string_selector(column, *queries) ⇒ Object
Returns an Array of tracks that match the string query on the given column.
Constructor Details
#initialize(library) ⇒ DSL
Returns a new instance of DSL.
5 6 7 |
# File 'lib/lllibrary/dsl.rb', line 5 def initialize(library) @library = library end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(field, *args) ⇒ Object
Turns all the columns on the tracks table into methods that I call selectors. Based on the column’s type in the database, this either delegates to DSL#numeric_selector or DSL#string_selector.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/lllibrary/dsl.rb', line 12 def method_missing(field, *args) if Lllibrary::Track.column_names.include? field.to_s type = Lllibrary::Track.columns_hash[field.to_s].type case type when :integer, :float, :decimal, :boolean, :datetime, :timestamp, :date, :time numeric_selector(field, *args) when :string, :text string_selector(field, *args) else raise ArgumentError, "lllibrary: selectors aren't supported for #{field}'s type '#{type}'" end else super end end |
Instance Method Details
#all ⇒ Object
Selects all the tracks in the library.
29 30 31 |
# File 'lib/lllibrary/dsl.rb', line 29 def all @library.tracks.all end |
#none ⇒ Object
Selects no tracks, returning an empty playlist.
34 35 36 |
# File 'lib/lllibrary/dsl.rb', line 34 def none [] end |
#numeric_selector(column, *values_or_hash) ⇒ Object
Returns an Array of tracks that satisfy certain conditions on the given numeric column. You can pass an exact value to check for equality, a range, or a hash that specifies greater-than and less-than operators.
The possible operators, with their shortcuts, are:
:greater_than / :gt
:less_than / :lt
:greater_than_or_equal / :gte
:less_than_or_equal / :lte
:not_equal / :ne
Note: You can only use one of these operators at a time. If you want a range, use a Range.
Time strings like “1:23” can be given, and will be converted to a Range of milliseconds. For example, “1:00” will be treated like 60000..60999. Obviously, any field this is used on should be storing time in milliseconds.
Here’s some examples:
year(2010..2012) # equivalent to year(2010, 2011, 2012)
year(2011) # only matches 2011
year(gte: 2000) # matches 2000 and up
total_time("2:30") # matches 150000..150999 milliseconds
total_time("1:00".."2:00") # matches 60000..120999 milliseconds
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/lllibrary/dsl.rb', line 98 def numeric_selector(column, *values_or_hash) parse = lambda do |x| if x.is_a? String ms = Lllibrary.parse_time(x) if ms % 1000 == 0 ms...(ms + 1000) else ms end elsif x.is_a? Range left = x.begin.is_a?(String) ? Lllibrary.parse_time(x.begin) : x.begin right = x.end.is_a?(String) ? Lllibrary.parse_time(x.end) : x.end right += 999 if right % 1000 == 0 && !x.exclude_end? Range.new(left, right, x.exclude_end?) else x end end if values_or_hash.last.is_a? Hash operator = values_or_hash.last.keys.first.to_sym value = parse.(values_or_hash.last.values.first) case operator when :greater_than, :gt op = ">" if value.is_a? Range op = ">=" if value.exclude_end? value = value.end end @library.tracks.where("tracks.#{column} #{op} ?", value).all when :less_than, :lt value = value.begin if value.is_a? Range @library.tracks.where("tracks.#{column} < ?", value).all when :greater_than_or_equal, :gte value = value.begin if value.is_a? Range @library.tracks.where("tracks.#{column} >= ?", value).all when :less_than_or_equal, :lte op = "<=" if value.is_a? Range op = "<" if value.exclude_end? value = value.end end @library.tracks.where("tracks.#{column} #{op} ?", value) when :not_equal, :ne if value.is_a? Range op = value.exclude_end? ? ">=" : ">" @library.tracks.where("tracks.#{column} < ? AND tracks.#{column} #{op} ?", value.begin, value.end) else @library.tracks.where("tracks.#{column} != ?", value) end else raise ArgumentError, "'#{operator}' isn't a valid operator" end else values_or_hash.map do |value| @library.tracks.where(column => parse.(value)).all end.flatten end end |
#playlist(*names) ⇒ Object
Returns an Array of all tracks that are in a Playlist whose name matches one of the strings given to this selector. It’s kind of ugly, I will probably be changing this.
161 162 163 164 165 166 167 168 |
# File 'lib/lllibrary/dsl.rb', line 161 def playlist(*names) names.map do |name| playlists = @library.playlists.arel_table if playlist = @library.playlists.where(playlists[:name].matches(name.to_s)).first playlist.tracks.all end end.flatten end |
#string_selector(column, *queries) ⇒ Object
Returns an Array of tracks that match the string query on the given column. It’s SQL underneath, so you can use % and _ as wildcards in the query. By default, % wildcards are inserted on the left and right of your query. Use the :match option to change this:
:match => :middle "%query%" (default)
:match => :left "query%"
:match => :right "%query"
:match => :exact "query"
Here are some examples:
genre(:electronic, :edm) # matches "Electronic", "Electronica", "EDM", etc.
genre(:tmbg, match: :exact) # only matches "TMBG" (but case-insensitively)
composer(:rachmanino, match: :left) # matches "Rachmaninov", "Rachmaninoff", but not "Sergei Rachmaninov"
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/lllibrary/dsl.rb', line 54 def string_selector(column, *queries) = queries.last.is_a?(Hash) ? queries.pop : {} [:match] ||= :middle tracks = @library.tracks.arel_table queries.map do |query| query = { exact: "#{query}", left: "#{query}%", right: "%#{query}", middle: "%#{query}%" }[[:match]] @library.tracks.where(tracks[column].matches(query)).all end.flatten end |