pluckers 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +51 -0
  3. data/Appraisals +22 -0
  4. data/CHANGELOG +7 -0
  5. data/CODE_OF_CONDUCT.md +49 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +56 -0
  8. data/LICENSE +674 -0
  9. data/README.md +40 -0
  10. data/Rakefile +10 -0
  11. data/circle.yml +15 -0
  12. data/doc/idea.md +49 -0
  13. data/doc/usage/basics.md +46 -0
  14. data/doc/usage/extending.md +109 -0
  15. data/doc/usage/globalize.md +54 -0
  16. data/doc/usage/relationships.md +216 -0
  17. data/doc/usage/renaming.md +26 -0
  18. data/lib/pluckers/base.rb +166 -0
  19. data/lib/pluckers/features/active_record_3_2/belongs_to_reflections.rb +16 -0
  20. data/lib/pluckers/features/active_record_3_2/globalize.rb +11 -0
  21. data/lib/pluckers/features/active_record_3_2/has_and_belongs_to_many_reflections.rb +40 -0
  22. data/lib/pluckers/features/active_record_3_2/has_many_reflections.rb +16 -0
  23. data/lib/pluckers/features/active_record_3_2/has_many_through_reflections.rb +17 -0
  24. data/lib/pluckers/features/active_record_3_2/has_one_reflections.rb +17 -0
  25. data/lib/pluckers/features/active_record_3_2/has_one_through_reflections.rb +17 -0
  26. data/lib/pluckers/features/active_record_3_2/pluck.rb +26 -0
  27. data/lib/pluckers/features/active_record_3_2/renaming.rb +11 -0
  28. data/lib/pluckers/features/active_record_3_2/simple_attributes.rb +11 -0
  29. data/lib/pluckers/features/active_record_3_2.rb +10 -0
  30. data/lib/pluckers/features/active_record_4_0/belongs_to_reflections.rb +16 -0
  31. data/lib/pluckers/features/active_record_4_0/globalize.rb +11 -0
  32. data/lib/pluckers/features/active_record_4_0/has_and_belongs_to_many_reflections.rb +40 -0
  33. data/lib/pluckers/features/active_record_4_0/has_many_reflections.rb +16 -0
  34. data/lib/pluckers/features/active_record_4_0/has_many_through_reflections.rb +17 -0
  35. data/lib/pluckers/features/active_record_4_0/has_one_reflections.rb +17 -0
  36. data/lib/pluckers/features/active_record_4_0/has_one_through_reflections.rb +17 -0
  37. data/lib/pluckers/features/active_record_4_0/pluck.rb +11 -0
  38. data/lib/pluckers/features/active_record_4_0/renaming.rb +11 -0
  39. data/lib/pluckers/features/active_record_4_0/simple_attributes.rb +11 -0
  40. data/lib/pluckers/features/active_record_4_0.rb +10 -0
  41. data/lib/pluckers/features/active_record_4_1/belongs_to_reflections.rb +16 -0
  42. data/lib/pluckers/features/active_record_4_1/globalize.rb +11 -0
  43. data/lib/pluckers/features/active_record_4_1/has_and_belongs_to_many_reflections.rb +40 -0
  44. data/lib/pluckers/features/active_record_4_1/has_many_reflections.rb +16 -0
  45. data/lib/pluckers/features/active_record_4_1/has_many_through_reflections.rb +17 -0
  46. data/lib/pluckers/features/active_record_4_1/has_one_reflections.rb +17 -0
  47. data/lib/pluckers/features/active_record_4_1/has_one_through_reflections.rb +17 -0
  48. data/lib/pluckers/features/active_record_4_1/pluck.rb +11 -0
  49. data/lib/pluckers/features/active_record_4_1/renaming.rb +11 -0
  50. data/lib/pluckers/features/active_record_4_1/simple_attributes.rb +11 -0
  51. data/lib/pluckers/features/active_record_4_1.rb +10 -0
  52. data/lib/pluckers/features/active_record_4_2/belongs_to_reflections.rb +15 -0
  53. data/lib/pluckers/features/active_record_4_2/globalize.rb +11 -0
  54. data/lib/pluckers/features/active_record_4_2/has_and_belongs_to_many_reflections.rb +39 -0
  55. data/lib/pluckers/features/active_record_4_2/has_many_reflections.rb +15 -0
  56. data/lib/pluckers/features/active_record_4_2/has_many_through_reflections.rb +17 -0
  57. data/lib/pluckers/features/active_record_4_2/has_one_reflections.rb +16 -0
  58. data/lib/pluckers/features/active_record_4_2/has_one_through_reflections.rb +17 -0
  59. data/lib/pluckers/features/active_record_4_2/pluck.rb +11 -0
  60. data/lib/pluckers/features/active_record_4_2/renaming.rb +11 -0
  61. data/lib/pluckers/features/active_record_4_2/simple_attributes.rb +11 -0
  62. data/lib/pluckers/features/active_record_4_2.rb +10 -0
  63. data/lib/pluckers/features/active_record_5_0/belongs_to_reflections.rb +15 -0
  64. data/lib/pluckers/features/active_record_5_0/globalize.rb +11 -0
  65. data/lib/pluckers/features/active_record_5_0/has_and_belongs_to_many_reflections.rb +39 -0
  66. data/lib/pluckers/features/active_record_5_0/has_many_reflections.rb +15 -0
  67. data/lib/pluckers/features/active_record_5_0/has_many_through_reflections.rb +17 -0
  68. data/lib/pluckers/features/active_record_5_0/has_one_reflections.rb +16 -0
  69. data/lib/pluckers/features/active_record_5_0/has_one_through_reflections.rb +17 -0
  70. data/lib/pluckers/features/active_record_5_0/pluck.rb +11 -0
  71. data/lib/pluckers/features/active_record_5_0/renaming.rb +11 -0
  72. data/lib/pluckers/features/active_record_5_0/simple_attributes.rb +11 -0
  73. data/lib/pluckers/features/active_record_5_0.rb +10 -0
  74. data/lib/pluckers/features/base/belongs_to_reflections.rb +131 -0
  75. data/lib/pluckers/features/base/globalize.rb +116 -0
  76. data/lib/pluckers/features/base/has_and_belongs_to_many_reflections.rb +190 -0
  77. data/lib/pluckers/features/base/has_many_reflections.rb +193 -0
  78. data/lib/pluckers/features/base/has_many_through_reflections.rb +131 -0
  79. data/lib/pluckers/features/base/has_one_reflections.rb +122 -0
  80. data/lib/pluckers/features/base/has_one_through_reflections.rb +129 -0
  81. data/lib/pluckers/features/base/pluck.rb +30 -0
  82. data/lib/pluckers/features/base/renaming.rb +55 -0
  83. data/lib/pluckers/features/base/simple_attributes.rb +64 -0
  84. data/lib/pluckers/version.rb +3 -0
  85. data/lib/pluckers.rb +7 -0
  86. data/pluckers.gemspec +38 -0
  87. metadata +236 -0
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Pluckers
2
+
3
+ [![CircleCI](https://circleci.com/gh/simplelogica/pluckers/tree/master.svg?style=svg)](https://circleci.com/gh/simplelogica/pluckers/tree/master)
4
+
5
+ This gem extends the idea behind AR's pluck method so we can fetch data from multiple tables and create our own classes to encapsulate how we fetch data from the database and which bussines logic may be applied to them. You can read more about [The Idea](./doc/idea.md).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'pluckers'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install pluckers
22
+
23
+ ## USAGE
24
+
25
+ In this section you will learn
26
+
27
+ * [How to use a plucker and what do you obtain from it](./doc/usage/basics.md)
28
+ * [How to use your plucker with your globalized methods](./doc/usage/globalize.md)
29
+ * [How to rename your fetched attributes](./doc/usage/renaming.md)
30
+ * [How to use your plucker for traversing through relationships in a recursive way and obtain data from several tables without N+1 and with the minimum queries](./doc/usage/relationships.md)
31
+ * [How to create your own plucker classes to encapsulate all your plucking options and logic](./doc/usage/extending.md)
32
+
33
+ ## Development
34
+
35
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
+
37
+ ## Contributing
38
+
39
+ Bug reports and pull requests are welcome on GitHub at https://github.com/simplelogica/pluckers. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
40
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/circle.yml ADDED
@@ -0,0 +1,15 @@
1
+ dependencies:
2
+ pre:
3
+ - gem uninstall bundler -x
4
+ - gem install bundler
5
+ post:
6
+ - bundle exec appraisal install
7
+ test:
8
+ override:
9
+ - bundle exec appraisal rake test
10
+ database:
11
+ override:
12
+ - bundle exec rake -T
13
+ machine:
14
+ ruby:
15
+ version: "2.3"
data/doc/idea.md ADDED
@@ -0,0 +1,49 @@
1
+ ## The idea
2
+
3
+ ActiveRecord is a powerful tool to create and mantain the model and persistence of a Ruby application, but just as every tool it must be used when it's really needed.
4
+
5
+ As an example, ActiveRecord objects are quite expensive to instantiate compared with simpler objects, such as arrays or hashes. In the following benchmark we can see times returned by Ruby's benchmark for some simple samples.
6
+
7
+ ```
8
+ > puts Benchmark.measure { 1000.times{BlogPost.new} }
9
+ 0.030000 0.010000 0.040000 ( 0.079227)
10
+ => nil
11
+ > puts Benchmark.measure { 10000.times{BlogPost.new} }
12
+ 0.320000 0.000000 0.320000 ( 0.381990)
13
+ => nil
14
+ > puts Benchmark.measure { 100000.times{BlogPost.new} }
15
+ 3.350000 0.010000 3.360000 ( 3.546527)
16
+ => nil
17
+ ```
18
+
19
+ ```
20
+ > puts Benchmark.measure { 1000.times{Hash.new} }
21
+ 0.010000 0.000000 0.010000 ( 0.001668)
22
+ => nil
23
+ > puts Benchmark.measure { 10000.times{Hash.new} }
24
+ 0.020000 0.000000 0.020000 ( 0.039580)
25
+ => nil
26
+ > puts Benchmark.measure { 100000.times{Hash.new} }
27
+ 0.140000 0.020000 0.160000 ( 0.228560)
28
+ => nil
29
+ ```
30
+
31
+ This is the idea behind the `pluck` method ActiveRecord includes. Use ActiveRecord to manage the persistence and connection to the database, but return simple objects such as Arrays or Hashes to manage the information in ruby, avoiding to instantiate heavier objects.
32
+
33
+ ```
34
+ > 10000.times {|i| BlogPost.create(title: "Title #{i}")}
35
+
36
+ > puts Benchmark.measure { BlogPost.all.map(&:title) }
37
+ 0.560000 0.010000 0.570000 ( 0.704659)
38
+ => nil
39
+
40
+ > puts Benchmark.measure { BlogPost.pluck(:title) }
41
+ 0.110000 0.000000 0.110000 ( 0.172678)
42
+ => nil
43
+ ```
44
+
45
+ Unfortunately, `pluck` method is limited to attributes from the model and lack some other features, such as navigate through relations.
46
+
47
+ This gem `pluckers` creates a new kind of objects (yes, Pluckers) that encapsulate all the logic of plucking attributes and relations in a recursive way just using the definition of our model created by ActiveRecord but instantiating just arrays and hashes, not a single ActiveRecord::Base object.
48
+
49
+ Furthermore, these objects will become a single point of access to the database that will force us to think which information we really need, avoiding long SELECT queries and ending with N+1 issues in a clean and transparent way.
@@ -0,0 +1,46 @@
1
+ # Creating the plucker and fetching basic data
2
+
3
+ ## Creating the plucker
4
+
5
+ You may use the `Pluckers::Base` class to pluck all the information you need:
6
+
7
+ ```ruby
8
+ Pluckers::Base.new(Model.scope, options)
9
+ ```
10
+
11
+ You can use any ActiveRecord Relation. It means you can pluck any scope or collection just as you would use them in your Rails applications:
12
+
13
+ ```ruby
14
+ plucker = Pluckers::Base.new(BlogPost.published)
15
+ plucker = Pluckers::Base.new(Author.all)
16
+ plucker = Pluckers::Base.new(post.categories.published)
17
+ ```
18
+
19
+ Once you have the plucker object you just... pluck.
20
+
21
+ ```ruby
22
+ plucker.pluck
23
+ ```
24
+
25
+ If you don't select any columns the plucker will use the `attributes` method of the class to pluck all the attributes from the database.
26
+
27
+ ## Selecting columns
28
+
29
+ When you create the plucker you can configure some options to customize it.
30
+
31
+ First, you can choose which columns to pluck from the table, so you don't pluck 50 columns when you only need three of them. To do so you will use the `attributes` option.
32
+
33
+ ```ruby
34
+ Pluckers::Base.new(BlogPost.published, { attributes: [:title, :slug, :published_at] }).pluck
35
+ ```
36
+ ```ruby
37
+ [
38
+ { id: 33, title: "Lorem Ipsum", slug: 'lorem-ipsum', published_at: "2016-04-07"},
39
+ { id: 34, title: "Lorem Ipsum 3", slug: 'lorem-ipsum-3', published_at: "2016-04-09"},
40
+ { id: 35, title: "Lorem Ipsum 4", slug: 'lorem-ipsum-4', published_at: "2016-04-12"}
41
+ ]
42
+ ```
43
+
44
+ Of course, this will be done in just one query.
45
+
46
+ NEXT: [How to use your plucker with your globalized methods](./globalize.md)
@@ -0,0 +1,109 @@
1
+ # Extending pluckers
2
+
3
+ PREVIOUSLY: [How to use your plucker for traversing through relationships and obtain data from several tables without N+1 and with the minimum queries](./relationships.md)
4
+
5
+ In some cases you may have a plucker configuration you use in several places in your application, or complex enough to extract it from the controller and isolate it so it can be easily debugged or even tested in some unit test.
6
+
7
+ You can achieve this by creating your own plucker classes that initialize the right options and delegate the complex stuff to its base class.
8
+
9
+ ## Initializing options
10
+
11
+ As an example, imagine we want a plucker so we can show in a menu all the categories of our blog. We could use the base plucker as before:
12
+
13
+ ```ruby
14
+ Pluckers::Base.new(Category.published, {
15
+ attributes: [:name],
16
+ attributes_with_locale: { es: [:name] }
17
+ }).pluck
18
+ ```
19
+
20
+ Or we could have a new class:
21
+
22
+ ```ruby
23
+ class CategoryMenuPlucker < Pluckers::Base
24
+
25
+ def initialize
26
+
27
+ super(Category.published, {
28
+ attributes: [:name],
29
+ attributes_with_locale: { es: [:name] }
30
+ })
31
+
32
+ end
33
+ end
34
+ ```
35
+
36
+ And just a simple pluck call in our controller:
37
+
38
+ ```ruby
39
+ CategoryMenuPlucker.new.pluck
40
+ ```
41
+
42
+ We could even allow an options argument in the new class so we can customize it in different invocations.
43
+
44
+ ```ruby
45
+
46
+ class CategoryMenuPlucker < Pluckers::Base
47
+
48
+ def initialize options = {}
49
+
50
+ options[:attributes] ||= []
51
+ options[:attributes] << :name
52
+
53
+ options[:attributes_with_locale] ||= {}
54
+ options[:attributes_with_locale][:es] ||= []
55
+ options[:attributes_with_locale][:es] << :name
56
+
57
+
58
+ super(Category.published, options)
59
+
60
+ end
61
+ end
62
+ ```
63
+
64
+ ```ruby
65
+ CategoryMenuPlucker.new.pluck
66
+ CategoryMenuPlucker.new({renames: { name_es: :name_for_analytics}}).pluck
67
+ ```
68
+
69
+ ## Customizing the plucked results
70
+
71
+ In this extended plucker we could even add some logic after all the results are plucked. Imagine we only want those categories with published posts. We could do the following:
72
+
73
+ ```ruby
74
+
75
+ class CategoryMenuPlucker < Pluckers::Base
76
+
77
+ def initialize options = {}
78
+
79
+ options[:attributes] ||= []
80
+ options[:attributes] << :name
81
+
82
+ options[:attributes_with_locale] ||= {}
83
+ options[:attributes_with_locale][:es] ||= []
84
+ options[:attributes_with_locale][:es] << :name
85
+
86
+
87
+ options[:reflections] ||= {}
88
+ options[:reflections][:posts] = {
89
+ attributes: [:id],
90
+ }
91
+
92
+ super(Category.published, options)
93
+
94
+ end
95
+
96
+ def pluck
97
+ results = super
98
+
99
+ results.select do |category|
100
+ category[:posts].count > 0
101
+ end
102
+ end
103
+
104
+ end
105
+ ```
106
+
107
+ This way, when we execute `CategoryMenuPlucker.new.pluck` we are plucking the categories and discarding those that don't meet our expectations.
108
+
109
+ And more important, this logic is encapsulated in the class responsible for fetching the data from the database, isolated from the controller and easily testeable.
@@ -0,0 +1,54 @@
1
+ # Selecting Globalized columns
2
+
3
+ PREVIOUSLY: [How to use a plucker and what do you obtain from it](./basics.md)
4
+
5
+ If you are using Globalize you may find useful to pluck translated columns. You just have to include it in the attributes options and it will automatically recognize it as a translated column and will pluck it from that table.
6
+
7
+ ```ruby
8
+ Pluckers::Base.new(post.categories.published, { attributes: [:name] }).pluck
9
+ ```
10
+ ```ruby
11
+ [
12
+ { id: 2, name: "gifs" },
13
+ { id: 34, name: "shiba" },
14
+ { id: 35, name: "ducktales" }
15
+ ]
16
+ ```
17
+ ## Selecting a specific locale
18
+
19
+ In some scenarios you may need to pluck some specific language. You can do it with the `attributes_with_locale` options.
20
+
21
+ ```ruby
22
+ Pluckers::Base.new(post.categories.published, { attributes_with_locale: { es: [:name] }).pluck
23
+ ```
24
+ ```ruby
25
+ [
26
+ { id: 2, name_es: "gifs" },
27
+ { id: 34, name_es: "shiba" },
28
+ { id: 35, name_es: "patoaventuras" }
29
+ ]
30
+ ```
31
+
32
+ Since these are independent options you can combine them.
33
+
34
+ ```ruby
35
+ Pluckers::Base.new(post.categories.published, {
36
+ attributes: [:name],
37
+ attributes_with_locale: { es: [:name] }
38
+ }).pluck
39
+ ```
40
+ ```ruby
41
+ [
42
+ { id: 2, name: "gifs", name_es: "gifs" },
43
+ { id: 34, name: "shiba", name_es: "shiba" },
44
+ { id: 35, name: "ducktales", name_es: "patoaventuras" }
45
+ ]
46
+ ```
47
+
48
+ ## Fallback translations
49
+
50
+ Pluckers will use Globalize fallback locales configuration to return the most appropiate value. I.e. If some post has no content on english locale and its fallback is spanish, it will return the value in spanish locale.
51
+
52
+ All these operations will be done in an extra query, no matter the number of locales available in Globalize.
53
+
54
+ NEXT: [How to rename your fetched attributes](./renaming.md)
@@ -0,0 +1,216 @@
1
+ # Traversing relationships
2
+
3
+ PREVIOUSLY: [How to rename your fetched attributes](./renaming.md)
4
+
5
+ Until now you can pluck attributes. Now we introduce an option to traverse relationships in the model, so you can pluck not only one model, but any related model, through the `reflections` option.
6
+
7
+ ## Fetching relationships
8
+
9
+ Imagine, for the previous example, you want to pluck the post information for each category.
10
+
11
+ ```ruby
12
+ Pluckers::Base.new(post.categories.published, {
13
+ attributes: [:name],
14
+ attributes_with_locale: { es: [:name] },
15
+ renames: { name_es: :name_for_analytics},
16
+ reflections: {
17
+ posts: {
18
+ attributes: [:title, :slug, :published_at],
19
+ }
20
+ }
21
+ }).pluck
22
+ ```
23
+ ```ruby
24
+ [
25
+ { id: 2, name: "gifs", name_for_analytics: "gifs",
26
+ posts: [
27
+ { id: 33, title: "Lorem Ipsum", slug: 'lorem-ipsum', published_at: "2016-04-07"},
28
+ { id: 32, title: "Lorem Ipsum not", slug: 'lorem-ipsum-not', published_at: nil}
29
+ ]
30
+ },
31
+ { id: 34, name: "shiba", name_for_analytics: "shiba",
32
+ posts: [
33
+ { id: 34, title: "Lorem Ipsum 3", slug: 'lorem-ipsum-3', published_at: "2016-04-09"},
34
+ { id: 35, title: "Lorem Ipsum 4", slug: 'lorem-ipsum-4', published_at: "2016-04-12"}
35
+ ]
36
+ },
37
+ { id: 35, name: "ducktales", name_for_analytics: "patoaventuras"
38
+ posts: [
39
+ { id: 33, title: "Lorem Ipsum", slug: 'lorem-ipsum', published_at: "2016-04-07"}
40
+ { id: 34, title: "Lorem Ipsum 3", slug: 'lorem-ipsum-3', published_at: "2016-04-09"},
41
+ { id: 35, title: "Lorem Ipsum 4", slug: 'lorem-ipsum-4', published_at: "2016-04-12"}
42
+ ]
43
+ }
44
+ ]
45
+ ```
46
+
47
+ As we just use the `attributes` option for the reflection we just perform an extra database query, avoiding N+1.
48
+
49
+ Each element in the reflections options has a key and a hash of options. The key is the name of the relationship as defined in the Active Record model. The value is a hash of options that takes the exact same options that are allowed for the plucker. In fact, internally we create another plucker to retrieve the posts.
50
+
51
+ This means that we can do everything in this "secondary" plucker. We can get globalize columns, we can rename... and we can also get another related models, giving us the ability to obtain a whole tree of models and objects in just one single point, with the minimum database queries required.
52
+
53
+ ## Foreign keys and minimum data plucked
54
+
55
+ Although in the examples we only show ids involved, relationships configured with different foreign keys can be fetched too as the configuration is read by the plucker to use the proper columns in both involved tables.
56
+
57
+ In order to be able to relate the plucked data all the foreign keys must be plucked, that's why in the previous example all the posts plucked their `id` although in the `attributes` option we specied only `[:title, :slug, :published_at]`.
58
+
59
+ ## Fetching only ids
60
+
61
+ For `has_many` and `has_and_belongs_to_many` relationships you could be interested in only fetching the ids of the related objects. You can get this by using the `only_ids` option in the relationship to fetch.
62
+
63
+ ```ruby
64
+ Pluckers::Base.new(post.categories.published, {
65
+ attributes: [:name],
66
+ attributes_with_locale: { es: [:name] },
67
+ renames: { name_es: :name_for_analytics},
68
+ reflections: {
69
+ posts: { only_ids: true }
70
+ }
71
+ }).pluck
72
+ ```
73
+
74
+ ```ruby
75
+ [
76
+ { id: 2, name: "gifs", name_for_analytics: "gifs",
77
+ post_ids: [33, 32]
78
+ },
79
+ { id: 34, name: "shiba", name_for_analytics: "shiba",
80
+ posts_ids: [34, 35]
81
+ },
82
+ { id: 35, name: "ducktales", name_for_analytics: "patoaventuras"
83
+ post_ids: [33, 34, 35]
84
+ }
85
+ ]
86
+ ```
87
+
88
+ ## Traversing relationships in a recursive way
89
+
90
+ As an example, imagine we want to obtain the name of the author of each one of the retrieved post.
91
+
92
+ ```ruby
93
+ Pluckers::Base.new(post.categories.published, {
94
+ attributes: [:name],
95
+ attributes_with_locale: { es: [:name] },
96
+ renames: { name_es: :name_for_analytics},
97
+ reflections: {
98
+ posts: {
99
+ attributes: [:title, :slug, :published_at],
100
+ reflections: {
101
+ author: { attributes: [:name] }
102
+ }
103
+ }
104
+ }
105
+ }).pluck
106
+ ```
107
+ ```ruby
108
+ [
109
+ { id: 2, name: "gifs", name_for_analytics: "gifs",
110
+ posts: [
111
+ { id: 33, title: "Lorem Ipsum", slug: 'lorem-ipsum', published_at: "2016-04-07",
112
+ autor: { id: 1, name: "Someone"}
113
+ },
114
+ { id: 32, title: "Lorem Ipsum not", slug: 'lorem-ipsum-not', published_at: nil,
115
+ autor: { id: 2, name: "Someone else"}
116
+ }
117
+ ]
118
+ },
119
+ { id: 34, name: "shiba", name_for_analytics: "shiba",
120
+ posts: [
121
+ { id: 34, title: "Lorem Ipsum 3", slug: 'lorem-ipsum-3', published_at: "2016-04-09",
122
+ autor: { id: 3, name: "Another one"}
123
+ },
124
+ { id: 35, title: "Lorem Ipsum 4", slug: 'lorem-ipsum-4', published_at: "2016-04-12",
125
+ autor: { id: 1, name: "Someone"}
126
+ }
127
+ ]
128
+ },
129
+ { id: 35, name: "ducktales", name_for_analytics: "patoaventuras"
130
+ posts: [
131
+ { id: 33, title: "Lorem Ipsum", slug: 'lorem-ipsum', published_at: "2016-04-07",
132
+ autor: { id: 1, name: "Someone"}
133
+ },
134
+ { id: 34, title: "Lorem Ipsum 3", slug: 'lorem-ipsum-3', published_at: "2016-04-09",
135
+ autor: { id: 3, name: "Another one"}
136
+ },
137
+ { id: 35, title: "Lorem Ipsum 4", slug: 'lorem-ipsum-4', published_at: "2016-04-12",
138
+ autor: { id: 1, name: "Someone"}
139
+ }
140
+ ]
141
+ }
142
+ ]
143
+ ```
144
+
145
+ This would've been performed with 4 database queries:
146
+
147
+ - Category attributes.
148
+ - Category globalized attributes.
149
+ - Related posts attributes.
150
+ - Related authors from related posts attributes.
151
+
152
+ ## Applying scopes
153
+
154
+ Sometimes may be useful to apply some restrictions on the related objects we are plucking. Maybe we don't want all the posts to be plucked, only the published ones in order to show the links.
155
+
156
+ We can restrict the related objects to be plucked through the `scope` option which accepts standard ActiveRecord scopes which will be used to build the query when plucking related objects.
157
+
158
+ ```ruby
159
+ Pluckers::Base.new(post.categories.published, {
160
+ attributes: [:name],
161
+ attributes_with_locale: { es: [:name] },
162
+ renames: { name_es: :name_for_analytics},
163
+ reflections: {
164
+ posts: {
165
+ attributes: [:title, :slug, :published_at],
166
+ reflections: {
167
+ author: { attributes: [:name] }
168
+ },
169
+ scope: BlogPost.published
170
+ }
171
+ }
172
+ }).pluck
173
+ ```
174
+ ```ruby
175
+ [
176
+ { id: 2, name: "gifs", name_for_analytics: "gifs",
177
+ posts: [
178
+ { id: 33, title: "Lorem Ipsum", slug: 'lorem-ipsum', published_at: "2016-04-07",
179
+ autor: { id: 1, name: "Someone"}
180
+ }
181
+ ]
182
+ },
183
+ { id: 34, name: "shiba", name_for_analytics: "shiba",
184
+ posts: [
185
+ { id: 34, title: "Lorem Ipsum 3", slug: 'lorem-ipsum-3', published_at: "2016-04-09",
186
+ autor: { id: 3, name: "Another one"}
187
+ },
188
+ { id: 35, title: "Lorem Ipsum 4", slug: 'lorem-ipsum-4', published_at: "2016-04-12",
189
+ autor: { id: 1, name: "Someone"}
190
+ }
191
+ ]
192
+ },
193
+ { id: 35, name: "ducktales", name_for_analytics: "patoaventuras"
194
+ posts: [
195
+ { id: 33, title: "Lorem Ipsum", slug: 'lorem-ipsum', published_at: "2016-04-07",
196
+ autor: { id: 1, name: "Someone"}
197
+ },
198
+ { id: 34, title: "Lorem Ipsum 3", slug: 'lorem-ipsum-3', published_at: "2016-04-09",
199
+ autor: { id: 3, name: "Another one"}
200
+ },
201
+ { id: 35, title: "Lorem Ipsum 4", slug: 'lorem-ipsum-4', published_at: "2016-04-12",
202
+ autor: { id: 1, name: "Someone"}
203
+ }
204
+ ]
205
+ }
206
+ ]
207
+ ```
208
+
209
+ This would've been performed with 4 database queries:
210
+
211
+ - Category attributes.
212
+ - Category globalized attributes.
213
+ - Related posts attributes filtering with the `published` scope as defined in the `BlogPost` model.
214
+ - Related authors from related posts attributes.
215
+
216
+ NEXT: [How to create your own plucker classes to encapsulate all your plucking options and logic](./extending.md)
@@ -0,0 +1,26 @@
1
+ # Renaming columns
2
+
3
+ PREVIOUSLY: [How to use your plucker with your globalized methods](./globalize.md)
4
+
5
+ Imagine you're plucking your spanish name to use it in Google Analytics integration so the visit is registered to the same category, no matter the language.
6
+
7
+ You may use `name_es` in your code or you could rename the attribute to a more meaningful name such as `name_for_analytics` through the `renames` option.
8
+
9
+ ```ruby
10
+ Pluckers::Base.new(post.categories.published, {
11
+ attributes: [:name],
12
+ attributes_with_locale: { es: [:name] },
13
+ renames: { name_es: :name_for_analytics}
14
+ }).pluck
15
+ ```
16
+ ```ruby
17
+ [
18
+ { id: 2, name: "gifs", name_for_analytics: "gifs" },
19
+ { id: 34, name: "shiba", name_for_analytics: "shiba" },
20
+ { id: 35, name: "ducktales", name_for_analytics: "patoaventuras" }
21
+ ]
22
+ ```
23
+
24
+ This will require no extra database query.
25
+
26
+ NEXT: [How to use your plucker for traversing through relationships in a recursive way and obtain data from several tables without N+1 and with the minimum queries](./relationships.md)