flattery 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +70 -0
- data/Rakefile +24 -0
- data/flattery.gemspec +32 -0
- data/lib/flattery.rb +4 -0
- data/lib/flattery/value_cache.rb +64 -0
- data/lib/flattery/version.rb +3 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/active_record_fixtures.rb +54 -0
- data/spec/unit/value_cache_spec.rb +133 -0
- metadata +208 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@flattery --create
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Paul Gallagher
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Flattery [](http://travis-ci.org/evendis/flattery)
|
2
|
+
|
3
|
+
Sometimes you want to do the non-DRY thing and repeat yourself, by caching values from associated records in a master model.
|
4
|
+
The two main reasons you might want to do this are probably:
|
5
|
+
* for performance - to avoid joins in search queries and display
|
6
|
+
* to save values from association records that are subject to deletion yet still have them available when looking at the master record - if you are using the [https://rubygems.org/gems/paranoid](paranoid) gem for example.
|
7
|
+
|
8
|
+
Hence flattery - a gem that provides a simple declarative method for caching and maintaining such values.
|
9
|
+
|
10
|
+
Flattery is primarily intended for use with relational Active::Record storage, and is only tested with sqlite and PostgreSQL.
|
11
|
+
If you are using NoSQL, you probably wouldn't design your schema in a way for which flattery adds any value - but if you find a situation where this makes sense, then feel free to fork and add the support .. or lobby for it's inclusion!
|
12
|
+
|
13
|
+
## Requirements
|
14
|
+
|
15
|
+
* Ruby 1.9 or 2
|
16
|
+
* Rails 3.x/4.x
|
17
|
+
* ActiveRecord (only splite and PostgreQL tested. Others _should_ work; raise an issue if you find problems)
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
gem 'flattery'
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install flattery
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
### How to define a model that has cached values from a :belongs_to association
|
36
|
+
|
37
|
+
Given a model with a :category assoociation, and you want to cache instance.category.name as instance.category_name.
|
38
|
+
|
39
|
+
First, add a migration to add :category_name column to your table with the same type as category.name.
|
40
|
+
Then just include Flattery::ValueCache in your model and define flatten_values like this:
|
41
|
+
|
42
|
+
class Note < ActiveRecord::Base
|
43
|
+
belongs_to :category
|
44
|
+
|
45
|
+
include Flattery::ValueCache
|
46
|
+
flatten_value :category => :name
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
### How to cache the value in a specific column name
|
51
|
+
|
52
|
+
In the usual case, the cache column name is inferred from the association (e.g. category_name in the example above).
|
53
|
+
If you want to store in another column name, use the :as option on the +flatten_value+ call:
|
54
|
+
|
55
|
+
class Note < ActiveRecord::Base
|
56
|
+
belongs_to :category
|
57
|
+
|
58
|
+
include Flattery::ValueCache
|
59
|
+
flatten_value :category => :name, :as => 'cat_name'
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
1. Fork it
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
desc "Run all test examples"
|
6
|
+
RSpec::Core::RakeTask.new do |t|
|
7
|
+
t.rspec_opts = ["-c", "-f progress"]
|
8
|
+
t.pattern = 'spec/**/*_spec.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :spec
|
12
|
+
|
13
|
+
require 'rdoc/task'
|
14
|
+
RDoc::Task.new do |rdoc|
|
15
|
+
rdoc.main = "README.md"
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = "Flattery"
|
18
|
+
rdoc.rdoc_files.include('README*', 'lib/**/*.rb')
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Open an irb session preloaded with this library"
|
22
|
+
task :console do
|
23
|
+
sh "irb -rubygems -I lib -r flattery.rb"
|
24
|
+
end
|
data/flattery.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'flattery/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "flattery"
|
8
|
+
spec.version = Flattery::VERSION
|
9
|
+
spec.authors = ["Paul Gallagher"]
|
10
|
+
spec.email = ["paul@evendis.com"]
|
11
|
+
spec.description = %q{Flatten/normalise values from ActiveModel associations}
|
12
|
+
spec.summary = %q{Flatter your nicely normalised AR models}
|
13
|
+
spec.homepage = "https://github.com/evendis/flattery"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "activesupport", ">= 3.0.3"
|
22
|
+
spec.add_runtime_dependency "activerecord", ">= 3.0.3"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rdoc"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "guard-rspec"
|
29
|
+
spec.add_development_dependency "rb-fsevent"
|
30
|
+
spec.add_development_dependency "sqlite3", ">= 1.3.2"
|
31
|
+
|
32
|
+
end
|
data/lib/flattery.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Flattery::ValueCache
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
class_attribute :value_cache_options
|
6
|
+
self.value_cache_options = {}
|
7
|
+
|
8
|
+
before_save :resolve_value_cache
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
# Command: adds flattery definition +options+.
|
14
|
+
# The +options+ define a single cache setting. To define multiple cache settings, call flatten_value once for each setting.
|
15
|
+
#
|
16
|
+
# +options+ by example:
|
17
|
+
# flatten_value :category => :name
|
18
|
+
# # => will cache self.category.name to self.category_name
|
19
|
+
# flatten_value :category => :name, :as => 'cat_name'
|
20
|
+
# # => will cache self.category.name to self.cat_name
|
21
|
+
#
|
22
|
+
# When explicitly passed nil, it clears all existing settings
|
23
|
+
#
|
24
|
+
def flatten_value(options={})
|
25
|
+
if options.nil?
|
26
|
+
self.value_cache_options = {}
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
self.value_cache_options ||= {}
|
31
|
+
opt = options.symbolize_keys
|
32
|
+
as_setting = opt.delete(:as)
|
33
|
+
association_name = opt.keys.first
|
34
|
+
association_method = opt[association_name]
|
35
|
+
cache_attribute = as_setting || "#{association_name}_#{association_method}"
|
36
|
+
|
37
|
+
assoc = reflect_on_association(association_name)
|
38
|
+
cache_options = if assoc && assoc.belongs_to? && assoc.klass.column_names.include?("#{association_method}")
|
39
|
+
{
|
40
|
+
association_name: association_name,
|
41
|
+
association_method: association_method,
|
42
|
+
changed_on: [assoc.foreign_key]
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
if cache_options
|
47
|
+
self.value_cache_options[cache_attribute] = cache_options
|
48
|
+
else
|
49
|
+
self.value_cache_options.delete(cache_attribute)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def resolve_value_cache
|
56
|
+
self.class.value_cache_options.each do |key,options|
|
57
|
+
if changed & options[:changed_on]
|
58
|
+
self.send("#{key}=", self.send(options[:association_name]).try(:send,options[:association_method]))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'flattery'
|
2
|
+
|
3
|
+
# Requires supporting files with custom matchers and macros, etc,
|
4
|
+
# in ./support/ and its subdirectories.
|
5
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
# == Mock Framework
|
9
|
+
#
|
10
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
11
|
+
#
|
12
|
+
# config.mock_with :mocha
|
13
|
+
# config.mock_with :flexmock
|
14
|
+
# config.mock_with :rr
|
15
|
+
config.mock_with :rspec
|
16
|
+
|
17
|
+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
18
|
+
# config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
19
|
+
|
20
|
+
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
21
|
+
# examples within a transaction, remove the following line or assign false
|
22
|
+
# instead of true.
|
23
|
+
# config.use_transactional_fixtures = true
|
24
|
+
|
25
|
+
config.before do
|
26
|
+
truncate_records
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ":memory:")
|
3
|
+
|
4
|
+
ActiveRecord::Migration.suppress_messages do
|
5
|
+
ActiveRecord::Schema.define(:version => 0) do
|
6
|
+
|
7
|
+
create_table(:notes, :force => true) do |t|
|
8
|
+
t.string :name;
|
9
|
+
t.belongs_to :category;
|
10
|
+
t.string :category_name;
|
11
|
+
t.string :person_name;
|
12
|
+
t.string :person_email;
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table(:categories, :force => true) do |t|
|
16
|
+
t.string :name;
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table(:people, :force => true) do |t|
|
20
|
+
t.string :username;
|
21
|
+
t.string :email;
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Basic models for testing
|
28
|
+
class Note < ActiveRecord::Base
|
29
|
+
belongs_to :category
|
30
|
+
belongs_to :person, primary_key: "username", foreign_key: "person_name"
|
31
|
+
end
|
32
|
+
|
33
|
+
class Category < ActiveRecord::Base
|
34
|
+
has_many :notes
|
35
|
+
end
|
36
|
+
|
37
|
+
class Person < ActiveRecord::Base
|
38
|
+
has_many :notes, inverse_of: :person
|
39
|
+
end
|
40
|
+
|
41
|
+
module ArHelper
|
42
|
+
|
43
|
+
# Clears any configuration
|
44
|
+
def truncate_records
|
45
|
+
Note.delete_all
|
46
|
+
Category.delete_all
|
47
|
+
Person.delete_all
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
RSpec.configure do |conf|
|
53
|
+
conf.include ArHelper
|
54
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
class FlatteryValueCacheTestHarness < Note
|
4
|
+
include Flattery::ValueCache
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Flattery::ValueCache do
|
8
|
+
|
9
|
+
let(:resource_class) { FlatteryValueCacheTestHarness }
|
10
|
+
after { resource_class.value_cache_options = {} }
|
11
|
+
|
12
|
+
describe "##included_modules" do
|
13
|
+
subject { resource_class.included_modules }
|
14
|
+
it { should include(Flattery::ValueCache) }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "##value_cache_options" do
|
18
|
+
before { resource_class.flatten_value flatten_value_options }
|
19
|
+
subject { resource_class.value_cache_options }
|
20
|
+
|
21
|
+
context "when set to empty" do
|
22
|
+
let(:flatten_value_options) { {} }
|
23
|
+
it { should be_empty }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with simple belongs_to associations" do
|
27
|
+
|
28
|
+
context "when set by association name and attribute value" do
|
29
|
+
let(:flatten_value_options) { {category: :name} }
|
30
|
+
it { should eql({
|
31
|
+
"category_name" => {
|
32
|
+
association_name: :category,
|
33
|
+
association_method: :name,
|
34
|
+
changed_on: ["category_id"]
|
35
|
+
}
|
36
|
+
}) }
|
37
|
+
|
38
|
+
context "when force reset to nil" do
|
39
|
+
it "should clear all settings" do
|
40
|
+
expect {
|
41
|
+
resource_class.flatten_value nil
|
42
|
+
}.to change {
|
43
|
+
resource_class.value_cache_options
|
44
|
+
}.to({})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when set by association name and invalid attribute value" do
|
50
|
+
let(:flatten_value_options) { {category: :bogative} }
|
51
|
+
it { should be_empty }
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with a belongs_to association having non-standard primary and foreign keys" do
|
57
|
+
|
58
|
+
context "when set by association name and attribute value" do
|
59
|
+
let(:flatten_value_options) { {person: :email} }
|
60
|
+
it { should eql({
|
61
|
+
"person_email" => {
|
62
|
+
association_name: :person,
|
63
|
+
association_method: :email,
|
64
|
+
changed_on: ["person_name"]
|
65
|
+
}
|
66
|
+
}) }
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when set by association name and invalid attribute value" do
|
70
|
+
let(:flatten_value_options) { {person: :bogative} }
|
71
|
+
it { should be_empty }
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#resolve_value_cache" do
|
80
|
+
it "should be called when record created" do
|
81
|
+
resource_class.any_instance.should_receive(:resolve_value_cache).and_return(true)
|
82
|
+
resource_class.create!
|
83
|
+
end
|
84
|
+
it "should be called when record updated" do
|
85
|
+
instance = resource_class.create!
|
86
|
+
instance.should_receive(:resolve_value_cache).and_return(true)
|
87
|
+
instance.save
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#before_save" do
|
92
|
+
let!(:resource) { resource_class.create }
|
93
|
+
|
94
|
+
context "with simple belongs_to associations with cached values" do
|
95
|
+
before { resource_class.flatten_value category: :name }
|
96
|
+
let!(:category) { Category.create(name: 'category_a') }
|
97
|
+
|
98
|
+
context "when association is changed by id" do
|
99
|
+
it "should cache the new value" do
|
100
|
+
expect {
|
101
|
+
resource.update_attributes(category_id: category.id)
|
102
|
+
}.to change {
|
103
|
+
resource.category_name
|
104
|
+
}.from(nil).to(category.name)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
context "when already set" do
|
108
|
+
before { resource.update_attributes(category_id: category.id) }
|
109
|
+
context "then set to nil" do
|
110
|
+
it "should cache the new value" do
|
111
|
+
expect {
|
112
|
+
resource.update_attributes(category_id: nil)
|
113
|
+
}.to change {
|
114
|
+
resource.category_name
|
115
|
+
}.from(category.name).to(nil)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
context "and associated record is destroyed" do
|
119
|
+
before { category.destroy }
|
120
|
+
it "should not recache the value when other values updated" do
|
121
|
+
expect {
|
122
|
+
resource.update_attributes(name: 'a new name')
|
123
|
+
}.to_not change {
|
124
|
+
resource.category_name
|
125
|
+
}.from(category.name)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
metadata
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flattery
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Gallagher
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-09-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.3
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.0.3
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 3.0.3
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 3.0.3
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rdoc
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rspec
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: guard-rspec
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rb-fsevent
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: sqlite3
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 1.3.2
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.3.2
|
158
|
+
description: Flatten/normalise values from ActiveModel associations
|
159
|
+
email:
|
160
|
+
- paul@evendis.com
|
161
|
+
executables: []
|
162
|
+
extensions: []
|
163
|
+
extra_rdoc_files: []
|
164
|
+
files:
|
165
|
+
- .gitignore
|
166
|
+
- .rvmrc
|
167
|
+
- .travis.yml
|
168
|
+
- Gemfile
|
169
|
+
- Guardfile
|
170
|
+
- LICENSE.txt
|
171
|
+
- README.md
|
172
|
+
- Rakefile
|
173
|
+
- flattery.gemspec
|
174
|
+
- lib/flattery.rb
|
175
|
+
- lib/flattery/value_cache.rb
|
176
|
+
- lib/flattery/version.rb
|
177
|
+
- spec/spec_helper.rb
|
178
|
+
- spec/support/active_record_fixtures.rb
|
179
|
+
- spec/unit/value_cache_spec.rb
|
180
|
+
homepage: https://github.com/evendis/flattery
|
181
|
+
licenses:
|
182
|
+
- MIT
|
183
|
+
post_install_message:
|
184
|
+
rdoc_options: []
|
185
|
+
require_paths:
|
186
|
+
- lib
|
187
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
188
|
+
none: false
|
189
|
+
requirements:
|
190
|
+
- - ! '>='
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: '0'
|
193
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
|
+
none: false
|
195
|
+
requirements:
|
196
|
+
- - ! '>='
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: '0'
|
199
|
+
requirements: []
|
200
|
+
rubyforge_project:
|
201
|
+
rubygems_version: 1.8.25
|
202
|
+
signing_key:
|
203
|
+
specification_version: 3
|
204
|
+
summary: Flatter your nicely normalised AR models
|
205
|
+
test_files:
|
206
|
+
- spec/spec_helper.rb
|
207
|
+
- spec/support/active_record_fixtures.rb
|
208
|
+
- spec/unit/value_cache_spec.rb
|