itemable 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e027584a4648f266c9f045a25ded47b3d81845b6
4
+ data.tar.gz: b888279d7af0b65ea947486e53502529ed1b073f
5
+ SHA512:
6
+ metadata.gz: 427c69a78de009e03fa0052591974a50a76a0cd4f9687ddb5f677af6b2391c122921b25237ef29c3bb911901d9e95a1ffabfae0d8bd4a185e47644051cd14cf7
7
+ data.tar.gz: b108c8fc19ebf6dc7e6a73b45e2cedf45144ba65b80faea2223482748728452ccbceb107c27b526f989bc0af73baff8ed4a16648848f445953a5b7268f027a93
@@ -0,0 +1,20 @@
1
+ Copyright 2017 xiaohui
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,171 @@
1
+ # Itemable
2
+
3
+ Make your ActiveRecord model as a item, it can be child or parent of self-modle or other-model
4
+
5
+ ## When will you need this gem?
6
+
7
+ Think about you are using PostgreSQL database, you just want to simplely store some unusually-query data into one table, put various files into one jsonb filed, record many have some child records, or parent records, such as a Car has many accessories: 4 Wheels, 3 Chairs, and a Wheel has one Tyre, 5 Screws. I don't care how to store them, just care about their relations, so now let's make this with `Itemable`:
8
+
9
+ 1. Setup `Itemable`
10
+ 2. Create Models `Car`, `Wheel`, `Chair`, `Tyre`, `Screw`
11
+ 3. Create relations to each model
12
+
13
+ ## Installation
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'itemable'
18
+ ```
19
+
20
+ And then execute below to create tables `itemable_items` and `itemable_item_relations`:
21
+
22
+ ```bash
23
+ $ rails g itemable:migration
24
+ $ rake db:migrate
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ Create `Car` modle
30
+
31
+ ```
32
+ $ rails g model car name
33
+ ```
34
+
35
+ Update `app/models/car.rb`:
36
+
37
+ class Car < ActiveRecord::Base
38
+ acts_as_itemable
39
+ has_many_items :wheels, dependent: :destroy
40
+ has_many_items :chairs, dependent: :destroy
41
+ end
42
+
43
+ - `acts_as_itemable` makes `Car` has all feature of `Itemable`
44
+ - `has_many_items :wheels` declares a car has many wheels
45
+ - `has_many_items :chairs` declares a car has many chairs
46
+
47
+ Create `Wheel` model without db migration:
48
+
49
+ Add `app/models/wheel.rb`
50
+
51
+ class Wheel < ActiveRecord::Base
52
+ acts_as_itemable sti: true
53
+ belongs_to_item :car
54
+ has_many_items :screws, dependent: :destroy
55
+ has_one_item: :tyre, dependent: :destroy
56
+ end
57
+
58
+ - `acts_as_itemable sti: true`, `sti = true` means `Wheel` is a single table inherited Model, will get records from table `itemable_items`
59
+ - `belongs_to_item :car` declares wheel belongs to car
60
+ - `has_one_item: :tyre` declares wheel has only one tyre
61
+
62
+ Add `app/models/chair.rb`
63
+
64
+ class Chair < ActiveRecord::Base
65
+ acts_as_itemable sti: true
66
+ belongs_to_item :car
67
+ end
68
+
69
+ Add `app/models/tyre.rb`
70
+
71
+ class Tyre < ActiveRecord::Base
72
+ acts_as_itemable sti: true
73
+ belongs_to_item :wheel
74
+ end
75
+
76
+ Add `app/models/screw.rb`
77
+
78
+ class Screw < ActiveRecord::Base
79
+ acts_as_itemable sti: true
80
+ belongs_to_item :wheel
81
+ end
82
+
83
+ **Now everything is ready, let store/query data**
84
+
85
+ ```ruby
86
+ car = Car.create
87
+ 4.times do
88
+ car.wheels << Wheel.create
89
+ end
90
+ 3.times do
91
+ car.chairs << Chair.create
92
+ end
93
+ wheel = Wheel.first
94
+ wheel.tyre = Tyre.create
95
+ 5.times do
96
+ wheel.screws << Screw.create
97
+ end
98
+
99
+ car.reload
100
+ car.item_children.count => 7
101
+ car.item_children.map(&:child)
102
+ => [#<Wheel:0x007f86ba65f930 id: 16, type: "Wheel", created_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00>,
103
+ #<Wheel:0x007f86ba677fa8 id: 17, type: "Wheel", created_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00>,
104
+ #<Wheel:0x007f86ba685568 id: 18, type: "Wheel", created_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00>,
105
+ #<Wheel:0x007f86ba69c038 id: 19, type: "Wheel", created_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00>,
106
+ #<Chair:0x007f86ba6cd0e8 id: 20, type: "Chair", created_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00>,
107
+ #<Chair:0x007f86ba6e4e28 id: 21, type: "Chair", created_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00>,
108
+ #<Chair:0x007f86ba706c58 id: 22, type: "Chair", created_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00>]
109
+ car.wheels.count => 4
110
+ => [#<Wheel:0x007f86c018b8e0 id: 16, type: "Wheel", created_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00>,
111
+ #<Wheel:0x007f86c018b750 id: 17, type: "Wheel", created_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00>,
112
+ #<Wheel:0x007f86c018b5c0 id: 18, type: "Wheel", created_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00>,
113
+ #<Wheel:0x007f86c018b430 id: 19, type: "Wheel", created_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:18 UTC +00:00>]
114
+ car.chairs.count => 3
115
+ => [#<Chair:0x007f86be2baab0 id: 20, type: "Chair", created_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00>,
116
+ #<Chair:0x007f86be2ba830 id: 21, type: "Chair", created_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00>,
117
+ #<Chair:0x007f86be2ba538 id: 22, type: "Chair", created_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:22 UTC +00:00>]
118
+ wheels = car.wheels
119
+ wheels[0].item_children.count => 6
120
+ wheels[0].item_children.map(&:child)
121
+ => [#<Tyre:0x007f86be44f290 id: 23, type: "Tyre", created_at: Fri, 07 Apr 2017 04:53:33 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:33 UTC +00:00>,
122
+ #<Screw:0x007f86be46e0f0 id: 24, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>,
123
+ #<Screw:0x007f86be47cf60 id: 25, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>,
124
+ #<Screw:0x007f86be497d38 id: 26, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>,
125
+ #<Screw:0x007f86be4a6ba8 id: 27, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>,
126
+ #<Screw:0x007f86be4b59f0 id: 28, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>]
127
+ wheels[0].tyre
128
+ => #<Tyre:0x007f86b8fb04c8 id: 23, type: "Tyre", created_at: Fri, 07 Apr 2017 04:53:33 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:33 UTC +00:00>
129
+ wheels[0].screws.count => 5
130
+ => [#<Screw:0x007f86b833c750 id: 24, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>,
131
+ #<Screw:0x007f86b833c570 id: 25, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>,
132
+ #<Screw:0x007f86b833c3e0 id: 26, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>,
133
+ #<Screw:0x007f86b833c250 id: 27, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>,
134
+ #<Screw:0x007f86b833c0c0 id: 28, type: "Screw", created_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:53:49 UTC +00:00>]
135
+ wheels[1].tyre => nil
136
+ wheels[1].tyre = Tyre.create
137
+ => #<Tyre:0x007f86bbb8e450 id: 29, type: "Tyre", created_at: Fri, 07 Apr 2017 04:58:05 UTC +00:00, updated_at: Fri, 07 Apr 2017 04:58:05 UTC +00:00>
138
+ wheels[1].item_children.count => 1
139
+ ```
140
+
141
+ See, it's so easy to build model relations
142
+
143
+ ## Advanced Usage
144
+
145
+ `acts_as_itemable` has following options:
146
+
147
+ - `sti`: `true` or `false`
148
+ - `children`: `true` or `false`
149
+ - `true` generates association `item_children`, then support `has_many_items`
150
+ - `false` does nothing
151
+ - `child`: `true` or `false`
152
+ - `true` generates association `item_child`, then support `has_one_item`
153
+ - `false` does nothing
154
+ - `parents`: `true` or `false`
155
+ - `true` generates association `item_parents`, then support `belongs_to_items`
156
+ - `false` does nothing
157
+ - `parent`: `true` or `false`
158
+ - `true` generates association `item_parents`, then support `belongs_to_item`
159
+ - `false` does nothing
160
+
161
+ options of `has_many_items`, `has_one_item`, `belongs_to_items`, `belongs_to_item` are the same to `has_many` or `belongs_to`
162
+
163
+ ## Test
164
+
165
+ rake
166
+
167
+ ## Contributing
168
+ Contribution directions go here.
169
+
170
+ ## License
171
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,15 @@
1
+ module Itemable
2
+ module Generators
3
+ module Migration
4
+ # Implement the required interface for Rails::Generators::Migration.
5
+ def next_migration_number(dirname) #:nodoc:
6
+ next_migration_number = current_migration_number(dirname) + 1
7
+ if ::ActiveRecord::Base.timestamped_migrations
8
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
9
+ else
10
+ "%.3d" % next_migration_number
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'migration'
2
+ require_relative 'migration_helper'
3
+ module Itemable
4
+ module Generators
5
+ class MigrationGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+ include Itemable::Generators::MigrationHelper
8
+ extend Itemable::Generators::Migration
9
+ source_root File.expand_path('../templates', __FILE__)
10
+ def copy_migration
11
+ migration_template 'create_items.rb', 'db/migrate/create_items.itemable.rb'
12
+ migration_template 'create_item_relations.rb', 'db/migrate/create_item_relations.itemable.rb'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Itemable
2
+ module Generators
3
+ module MigrationHelper
4
+ def migration_parent
5
+ Rails::VERSION::MAJOR == 4 ? 'ActiveRecord::Migration' : "ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class CreateItemRelations < <%= migration_parent %>
2
+ def change
3
+ create_table :itemable_item_relations do |t|
4
+ t.references :parent, polymorphic: true
5
+ t.references :child, polymorphic: true
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class CreateItems < <%= migration_parent %>
2
+ def change
3
+ create_table :itemable_items do |t|
4
+ t.string :type
5
+ t.string :name
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Itemable
2
+ autoload :ActsAsItemable, 'itemable/acts_as_itemable'
3
+ autoload :ItemRelation, 'itemable/item_relation'
4
+ end
5
+
6
+ require 'itemable/railtie' if defined?(Rails)
@@ -0,0 +1,143 @@
1
+ module Itemable
2
+ module ActsAsItemable
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_itemable(sti: false, parents: true, children: true, child: true, parent: true)
7
+ if sti
8
+ self.table_name = "itemable_items"
9
+ self.instance_variable_set('@finder_needs_type_condition', :true)
10
+ end
11
+
12
+ if children
13
+ include ChildrenAssociation
14
+ include HasManyItems
15
+ end
16
+
17
+ if child
18
+ include ChildAssociation
19
+ include HasOneItem
20
+ end
21
+
22
+ if parents
23
+ include ParentsAssociation
24
+ include BelongsToItems
25
+ end
26
+
27
+ if parent
28
+ include ParentAssociation
29
+ include BelongsToItem
30
+ end
31
+ end
32
+ end
33
+
34
+ module HasManyItems
35
+ extend ActiveSupport::Concern
36
+ module ClassMethods
37
+ def has_many_items(association_name, options={})
38
+ default_options = {
39
+ through: :item_children,
40
+ source: :child,
41
+ source_type: association_name.to_s.classify
42
+ }.merge(options.symbolize_keys)
43
+ if default_options[:dependent] == :destroy
44
+ before_destroy prepend: true do
45
+ send(association_name).each do |rec|
46
+ rec.destroy
47
+ end
48
+ end
49
+ end
50
+ has_many association_name, default_options
51
+ end
52
+ end
53
+ end
54
+
55
+ module HasOneItem
56
+ extend ActiveSupport::Concern
57
+ module ClassMethods
58
+ def has_one_item(association_name, options={})
59
+ default_options = {
60
+ through: :item_child,
61
+ source: :child,
62
+ source_type: association_name.to_s.classify
63
+ }.merge(options.symbolize_keys)
64
+ if default_options[:dependent] == :destroy
65
+ before_destroy prepend: true do
66
+ rec = send(association_name)
67
+ rec.destroy if rec
68
+ end
69
+ end
70
+ has_one association_name, default_options
71
+ end
72
+ end
73
+ end
74
+
75
+ module BelongsToItems
76
+ extend ActiveSupport::Concern
77
+ module ClassMethods
78
+ def belongs_to_items(association_name, options={})
79
+ default_options = {
80
+ through: :item_parents,
81
+ source: :parent,
82
+ source_type: association_name.to_s.classify
83
+ }
84
+ has_many association_name, default_options.merge(options.symbolize_keys)
85
+ end
86
+ end
87
+ end
88
+
89
+ module BelongsToItem
90
+ extend ActiveSupport::Concern
91
+ module ClassMethods
92
+ def belongs_to_item(association_name, options={})
93
+ default_options = {
94
+ through: :item_parent,
95
+ source: :parent,
96
+ source_type: association_name.to_s.classify
97
+ }
98
+ has_one association_name, default_options.merge(options.symbolize_keys)
99
+ end
100
+ end
101
+ end
102
+
103
+ module ChildrenAssociation
104
+ extend ActiveSupport::Concern
105
+ included do
106
+ has_many :item_children,
107
+ class_name: 'Itemable::ItemRelation',
108
+ as: :parent,
109
+ dependent: :destroy
110
+ end
111
+ end
112
+
113
+ module ChildAssociation
114
+ extend ActiveSupport::Concern
115
+ included do
116
+ has_one :item_child,
117
+ class_name: 'Itemable::ItemRelation',
118
+ as: :parent,
119
+ dependent: :destroy
120
+ end
121
+ end
122
+
123
+ module ParentsAssociation
124
+ extend ActiveSupport::Concern
125
+ included do
126
+ has_many :item_parents,
127
+ class_name: 'Itemable::ItemRelation',
128
+ as: :child,
129
+ dependent: :destroy
130
+ end
131
+ end
132
+
133
+ module ParentAssociation
134
+ extend ActiveSupport::Concern
135
+ included do
136
+ has_one :item_parent,
137
+ class_name: 'Itemable::ItemRelation',
138
+ as: :child,
139
+ dependent: :destroy
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,7 @@
1
+ module Itemable
2
+ class ItemRelation < ActiveRecord::Base
3
+ self.table_name = 'itemable_item_relations'
4
+ belongs_to :parent, polymorphic: true
5
+ belongs_to :child, polymorphic: true
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Itemable
2
+ class Railtie < Rails::Railtie
3
+ ActiveSupport.on_load(:active_record) do
4
+ ActiveRecord::Base.send :include, Itemable::ActsAsItemable
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Itemable
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :itemable do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: itemable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - xiaohui
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Make your ActiveRecord model to be an item, has polymorphic children
42
+ and parents.
43
+ email:
44
+ - xiaohui@zhangxh.net
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - MIT-LICENSE
50
+ - README.md
51
+ - lib/generators/itemable/migration.rb
52
+ - lib/generators/itemable/migration_generator.rb
53
+ - lib/generators/itemable/migration_helper.rb
54
+ - lib/generators/itemable/templates/create_item_relations.rb
55
+ - lib/generators/itemable/templates/create_items.rb
56
+ - lib/itemable.rb
57
+ - lib/itemable/acts_as_itemable.rb
58
+ - lib/itemable/item_relation.rb
59
+ - lib/itemable/railtie.rb
60
+ - lib/itemable/version.rb
61
+ - lib/tasks/itemable_tasks.rake
62
+ homepage: http://github.com/xiaohui-zhangxh/itemable
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.5.2
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Make your ActiveRecord model to be an item, has polymorphic children and
86
+ parents.
87
+ test_files: []