Skip to content

Commit

Permalink
Merge branch 'master' into add_order_method
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean0628 committed Sep 28, 2019
2 parents 338d262 + 8d7fa60 commit b19a8de
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 106 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ gemfile:
- gemfiles/rails_5.0.gemfile
- gemfiles/rails_5.1.gemfile
- gemfiles/rails_5.2.gemfile
- gemfiles/rails_6.0.beta.gemfile
- gemfiles/rails_6.0.gemfile

matrix:
exclude:
- rvm: 2.4.5
gemfile: gemfiles/rails_6.0.beta.gemfile
gemfile: gemfiles/rails_6.0.gemfile

script: "bundle exec rake spec"
8 changes: 8 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
2019-09-28 (v3.0.0)
- Make #where chainable [#178](https://github.com/zilkey/active_hash/pull/178)

2019-09-28 (v2.3.0)
- Add ::scope method (inspired by ActiveRecord) [#173](https://github.com/zilkey/active_hash/pull/173)
- Let `.find(nil)` raise ActiveHash::RecordNotFound (inspired by ActiveRecord) [#174](https://github.com/zilkey/active_hash/pull/174)
- `where` clause now works with range argument [#175](https://github.com/zilkey/active_hash/pull/175)

2019-03-06 (v2.2.1)
- Allow empty YAML [#171](https://github.com/zilkey/active_hash/pull/171) Thanks, @ppworks

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ ActiveHash also ships with:
* ActiveFile: a base class that you can use to create file data sources
* ActiveYaml: a base class that will turn YAML into a hash and load the data into an ActiveHash object

## !!! Important notice !!!
We have changed returned value to chainable by v3.0.0. It's not just an `Array` instance anymore.
If it breaks your application, please report us on [issues](https://github.com/zilkey/active_hash/issues), and use v2.x.x as following..

```ruby
gem 'active_hash', '~> 2.3.0'
```

## Installation

Bundler:
Expand Down
1 change: 1 addition & 0 deletions active_hash.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ Gem::Specification.new do |s|
].flatten
s.test_files = s.files.grep(%r{^(test|spec|features)/})
s.add_runtime_dependency('activesupport', '>= 5.0.0')
s.add_development_dependency "pry"
s.required_ruby_version = '>= 2.4.0'
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
source 'http://rubygems.org/'

gem 'activerecord', '~> 6.0.0.beta2'
gem 'activerecord', '~> 6.0.0'
gem 'rspec', '~> 2.2.0'
gem 'wwtd'
gem 'rake', '~> 10.0'
Expand Down
1 change: 1 addition & 0 deletions lib/active_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
end

require 'active_hash/base'
require 'active_hash/relation'
require 'active_file/multiple_files'
require 'active_file/hash_and_array_files'
require 'active_file/base'
Expand Down
101 changes: 7 additions & 94 deletions lib/active_hash/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def initialize(scope)
end

def not(options)
return @records if options.blank?
return @scope if options.blank?

# use index if searching by id
if options.key?(:id) || options.key?("id")
Expand All @@ -32,9 +32,11 @@ def not(options)
end
return candidates if options.blank?

(candidates || @records || []).reject do |record|
filtered_records = (candidates || @records || []).reject do |record|
match_options?(record, options)
end

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

def match_options?(record, options)
Expand Down Expand Up @@ -182,40 +184,8 @@ def create!(attributes = {})
record
end

def all(options={})
if options.has_key?(:conditions)
where(options[:conditions])
else
@records ||= []
end
end

def where(options = :chain)
if options == :chain
return WhereChain.new(self)
elsif options.blank?
return @records
end

# use index if searching by id
if options.key?(:id) || options.key?("id")
ids = (options.delete(:id) || options.delete("id"))
ids = range_to_array(ids) if ids.is_a?(Range)
candidates = Array.wrap(ids).map { |id| find_by_id(id) }.compact
end
return candidates if options.blank?

(candidates || @records || []).select do |record|
match_options?(record, options)
end
end

def find_by(options)
where(options).first
end

def find_by!(options)
find_by(options) || (raise RecordNotFound.new("Couldn't find #{name}"))
def all(options = {})
ActiveHash::Relation.new(self, @records || [], options[:conditions] || {})
end

def order(*options)
Expand All @@ -230,33 +200,6 @@ def order(*options)
candidates
end

def match_options?(record, options)
options.all? do |col, match|
if match.kind_of?(Array)
match.any? { |v| normalize(v) == normalize(record[col]) }
else
normalize(record[col]) == normalize(match)
end
end
end

private :match_options?

def normalize(v)
v.respond_to?(:to_sym) ? v.to_sym : v
end

private :normalize

def range_to_array(range)
return range.to_a unless range.end.nil?

e = data.last[:id]
(range.begin..e).to_a
end

private :range_to_array

def check_if_method_has_arguments!(method_name, args)
if args.blank?
raise ArgumentError, "The method .#{method_name}() must contain arguments."
Expand Down Expand Up @@ -297,13 +240,7 @@ def order_by_args!(candidates, args)

private :order_by_args!

def count
all.length
end

def pluck(*column_names)
column_names.map { |column_name| all.map(&column_name.to_sym) }.inject(&:zip)
end
delegate :where, :find, :find_by, :find_by!, :find_by_id, :count, :pluck, :first, :last, to: :all

def transaction
yield
Expand All @@ -321,30 +258,6 @@ def delete_all
@records = []
end

def find(id, * args)
case id
when :all
all
when :first
all(*args).first
when Array
id.map { |i| find(i) }
when nil
raise RecordNotFound.new("Couldn't find #{name} without an ID")
else
find_by_id(id) || begin
raise RecordNotFound.new("Couldn't find #{name} with ID=#{id}")
end
end
end

def find_by_id(id)
index = record_index[id.to_s]
index and @records[index]
end

delegate :first, :last, :to => :all

def fields(*args)
options = args.extract_options!
args.each do |field|
Expand Down
128 changes: 128 additions & 0 deletions lib/active_hash/relation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
module ActiveHash
class Relation
include Enumerable

delegate :each, to: :records # Make Enumerable work
delegate :equal?, :==, :===, :eql?, to: :records
delegate :empty?, :length, :first, :second, :third, :last, to: :records

def initialize(klass, all_records, query_hash = nil)
self.klass = klass
self.all_records = all_records
self.query_hash = query_hash
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 || {})
self
end

def all(options = {})
if options.has_key?(:conditions)
where(options[:conditions])
else
where({})
end
end

def find_by(options)
where(options).first
end

def find_by!(options)
find_by(options) || (raise RecordNotFound.new("Couldn't find #{klass.name}"))
end

def find(id, *args)
case id
when :all
all
when :first
all(*args).first
when Array
id.map { |i| find(i) }
when nil
raise RecordNotFound.new("Couldn't find #{klass.name} without an ID")
else
find_by_id(id) || begin
raise RecordNotFound.new("Couldn't find #{klass.name} with ID=#{id}")
end
end
end

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

def count
length
end

def pluck(*column_names)
column_names.map { |column_name| all.map(&column_name.to_sym) }.inject(&:zip)
end

def reload
@records = filter_all_records_by_query_hash
end

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

private

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

def records
if @records.nil? || records_dirty
reload
else
@records
end
end

def filter_all_records_by_query_hash
self.records_dirty = false
return all_records if query_hash.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"))
ids = range_to_array(ids) if ids.is_a?(Range)
candidates = Array.wrap(ids).map { |id| klass.find_by_id(id) }.compact
end

return candidates if query_hash.blank?

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

def match_options?(record, options)
options.all? do |col, match|
if match.kind_of?(Array)
match.any? { |v| normalize(v) == normalize(record[col]) }
else
normalize(record[col]) == normalize(match)
end
end
end

def normalize(v)
v.respond_to?(:to_sym) ? v.to_sym : v
end

def range_to_array(range)
return range.to_a unless range.end.nil?

e = records.last[:id]
(range.begin..e).to_a
end
end
end
2 changes: 1 addition & 1 deletion lib/active_hash/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module ActiveHash
module Gem
VERSION = "2.2.1"
VERSION = "3.0.0"
end
end
Loading

0 comments on commit b19a8de

Please sign in to comment.