Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,11 @@ Country#name= # => sets the name
The ActiveHash::Base.all method functions like an in-memory data store. You can save your records as ActiveHash::Relation object by using standard ActiveRecord create and save methods:
```ruby
Country.all
=> #<ActiveHash::Relation:0x00007f861e043bb0 @klass=Country, @all_records=[], @query_hash={}, @records_dirty=false>
=> #<ActiveHash::Relation:0x00007f861e043bb0 @klass=Country, @all_records=[], @query_list=[], @records_dirty=false>
Country.create
=> #<Country:0x00007f861b7abce8 @attributes={:id=>1}>
Country.all
=> #<ActiveHash::Relation:0x00007f861b7b3628 @klass=Country, @all_records=[#<Country:0x00007f861b7abce8 @attributes={:id=>1}>], @query_hash={}, @records_dirty=false>
=> #<ActiveHash::Relation:0x00007f861b7b3628 @klass=Country, @all_records=[#<Country:0x00007f861b7abce8 @attributes={:id=>1}>], @query_list=[], @records_dirty=false>
country = Country.new
=> #<Country:0x00007f861e059938 @attributes={}>
country.new_record?
Expand All @@ -225,7 +225,7 @@ country.save
country.new_record?
# => false
Country.all
=> #<ActiveHash::Relation:0x00007f861e0ca610 @klass=Country, @all_records=[#<Country:0x00007f861b7abce8 @attributes={:id=>1}>, #<Country:0x00007f861e059938 @attributes={:id=>2}>], @query_hash={}, @records_dirty=false>
=> #<ActiveHash::Relation:0x00007f861e0ca610 @klass=Country, @all_records=[#<Country:0x00007f861b7abce8 @attributes={:id=>1}>, #<Country:0x00007f861e059938 @attributes={:id=>2}>], @query_list=[], @records_dirty=false>
```
Notice that when adding records to the collection, it will auto-increment the id for you by default. If you use string ids, it will not auto-increment the id. Available methods are:
```
Expand Down
4 changes: 2 additions & 2 deletions lib/active_hash/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def not(options)
options.present? && match_options?(record, options)
end

ActiveHash::Relation.new(@scope.klass, filtered_records, {})
ActiveHash::Relation.new(@scope.klass, filtered_records, [])
end

def match_options?(record, options)
Expand Down Expand Up @@ -205,7 +205,7 @@ def create!(attributes = {})
end

def all(options = {})
ActiveHash::Relation.new(self, @records || [], options[:conditions] || {})
ActiveHash::Relation.new(self, @records || [], options[:conditions].to_a)
end

delegate :where, :find, :find_by, :find_by!, :find_by_id, :count, :pluck, :ids, :pick, :first, :last, :order, to: :all
Expand Down
39 changes: 22 additions & 17 deletions lib/active_hash/relation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ class Relation
delegate :empty?, :length, :first, :second, :third, :last, to: :records
delegate :sample, to: :records

def initialize(klass, all_records, query_hash = nil)
def initialize(klass, all_records, query_list = nil)
self.klass = klass
self.all_records = all_records
self.query_hash = query_hash
self.query_list = query_list
self.records_dirty = false
self
end

def where(query_hash = :chain)
return ActiveHash::Base::WhereChain.new(self) if query_hash == :chain

self.records_dirty = true unless query_hash.nil? || query_hash.keys.empty?
self.query_hash.merge!(query_hash || {})
unless query_hash.blank?
self.records_dirty = true
self.query_list.concat(query_hash.to_a)
end

self
end

Expand Down Expand Up @@ -58,10 +61,10 @@ def find(id = nil, *args, &block)
end

def find_by_id(id)
return where(id: id).first if query_hash.present?
return where(id: id).first if query_list.present?

index = klass.send(:record_index)[id.to_s] # TODO: Make index in Base publicly readable instead of using send?
index and records[index]
index and all_records[index]
end

def count
Expand Down Expand Up @@ -94,7 +97,7 @@ def pick(*column_names)
end

def reload
@records = filter_all_records_by_query_hash
@records = filter_all_records_by_query_list
end

def order(*options)
Expand All @@ -120,11 +123,11 @@ def method_missing(method_name, *args)
instance_exec(*args, &self.klass.scopes[method_name])
end

attr_reader :query_hash, :klass, :all_records, :records_dirty
attr_reader :query_list, :klass, :all_records, :records_dirty

private

attr_writer :query_hash, :klass, :all_records, :records_dirty
attr_writer :query_list, :klass, :all_records, :records_dirty

def records
if !defined?(@records) || @records.nil? || records_dirty
Expand All @@ -134,21 +137,23 @@ def records
end
end

def filter_all_records_by_query_hash
def filter_all_records_by_query_list
self.records_dirty = false
return all_records if query_hash.blank?
return all_records if query_list.blank?

# use index if searching by id
if query_hash.key?(:id) || query_hash.key?("id")
ids = (query_hash.delete(:id) || query_hash.delete("id"))
query_by_id_list = query_list.select { |option| option.first == :id || option.first == "id" }
candidates = query_by_id_list.reduce(nil) do |candidates, option|
ids = query_list.delete(option).last
ids = range_to_array(ids) if ids.is_a?(Range)
candidates = Array.wrap(ids).map { |id| klass.find_by_id(id) }.compact
new_candidates = Array.wrap(ids).map { |id| klass.find_by_id(id) }.compact

candidates.nil? ? new_candidates : candidates & new_candidates
end

return candidates if query_hash.blank?
return candidates if query_list.blank?

(candidates || all_records || []).select do |record|
match_options?(record, query_hash)
match_options?(record, query_list)
end
end

Expand Down
19 changes: 16 additions & 3 deletions spec/active_hash/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -307,15 +307,20 @@ class Region < ActiveHash::Base
expect(Country.where(:name => [:US, :Canada]).map(&:name)).to match_array(%w(US Canada))
end

it 'is chainable' do
where_relation = Country.where(language: 'English')
it "is chainable" do
where_relation = Country.where(language: "English")

expect(where_relation.length).to eq 2
expect(where_relation.map(&:id)).to eq([1, 2])
chained_where_relation = where_relation.where(name: 'US')
chained_where_relation = where_relation.where(name: "US")
expect(chained_where_relation.length).to eq 1
expect(chained_where_relation.map(&:id)).to eq([1])
end

it "is chainable with same attribute" do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice test

expect(Country.where(id: 1..2).where(id: 2..3).pluck(:id)).to match_array([2])
expect(Country.where(language: "English").where(language: "Spanish").length).to eq 0
end
end

describe ".where.not" do
Expand Down Expand Up @@ -410,6 +415,14 @@ class Region < ActiveHash::Base
it "filters records for multiple conditions" do
expect(Country.where.not(:id => 1, :name => 'Mexico')).to match_array([Country.find(2)])
end

it "is chainable with where" do
expect(Country.where.not(name: "US").where(language: "English").pluck(:name)).to match_array(["Canada"])
end

it "is chainable with where for same attribute" do
expect(Country.where(id: 2..3).where(id: 1..3).where.not(id: 1..2).where.not(id: 1).pluck(:id)).to match_array([3])
end
end

describe ".find_by" do
Expand Down