pliable 0.1.0
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/.bundle/install.log +1575 -0
- data/.document +3 -0
- data/.travis.yml +16 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +4 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +134 -0
- data/LICENSE.txt +20 -0
- data/README.md +287 -0
- data/Rakefile +39 -0
- data/lib/generators/pliable/model_generator.rb +40 -0
- data/lib/generators/pliable/templates/migration.rb +24 -0
- data/lib/pliable/configure.rb +12 -0
- data/lib/pliable/ply.rb +89 -0
- data/lib/pliable/ply_relation.rb +4 -0
- data/lib/pliable/text_helper.rb +3 -0
- data/lib/pliable/version.rb +4 -0
- data/lib/pliable.rb +17 -0
- data/pliable.gemspec +35 -0
- data/spec/.DS_Store +0 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/invoice.rb +6 -0
- data/spec/dummy/app/models/line_item.rb +5 -0
- data/spec/dummy/app/models/merchandise.rb +5 -0
- data/spec/dummy/app/models/ply.rb +7 -0
- data/spec/dummy/config/application.rb +65 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +19 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/pliable.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/.DS_Store +0 -0
- data/spec/dummy/db/migrate/20140117210156_create_plies_and_ply_relations.rb +23 -0
- data/spec/dummy/db/migration_helper.rb +42 -0
- data/spec/dummy/db/schema.rb +39 -0
- data/spec/dummy/db/seeds.rb +17 -0
- data/spec/dummy/db/static/bottles.csv +201 -0
- data/spec/dummy/db/static/varietal_aliases.csv +433 -0
- data/spec/dummy/db/static/varietals.csv +295 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/log/development.log +63 -0
- data/spec/dummy/log/test.log +2339 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/fixtures/varietals.yml +2135 -0
- data/spec/dummy/spec/models/.DS_Store +0 -0
- data/spec/dummy/spec/models/fake_ply_test.rb +12 -0
- data/spec/dummy/spec/models/ply_relation_spec.rb +12 -0
- data/spec/dummy/spec/pliable_integartion_spec.rb +48 -0
- data/spec/dummy/spec/tmp/app/models/foo_alias.rb +4 -0
- data/spec/spec_helper.rb +36 -0
- data/sub +4 -0
- metadata +389 -0
data/.document
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
|
6
|
+
addons:
|
7
|
+
postgresql: 9.2
|
8
|
+
|
9
|
+
before_script:
|
10
|
+
- psql -c 'create database dummy_test;' -U postgres
|
11
|
+
|
12
|
+
script:
|
13
|
+
- cd spec/dummy/
|
14
|
+
- bundle exec rake db:test:prepare
|
15
|
+
- cd ../..
|
16
|
+
- bundle exec rspec
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --title "pliable Documentation" --protected
|
data/ChangeLog.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pliable (0.1)
|
5
|
+
pg
|
6
|
+
rails (>= 4.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionmailer (4.0.2)
|
12
|
+
actionpack (= 4.0.2)
|
13
|
+
mail (~> 2.5.4)
|
14
|
+
actionpack (4.0.2)
|
15
|
+
activesupport (= 4.0.2)
|
16
|
+
builder (~> 3.1.0)
|
17
|
+
erubis (~> 2.7.0)
|
18
|
+
rack (~> 1.5.2)
|
19
|
+
rack-test (~> 0.6.2)
|
20
|
+
activemodel (4.0.2)
|
21
|
+
activesupport (= 4.0.2)
|
22
|
+
builder (~> 3.1.0)
|
23
|
+
activerecord (4.0.2)
|
24
|
+
activemodel (= 4.0.2)
|
25
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
26
|
+
activesupport (= 4.0.2)
|
27
|
+
arel (~> 4.0.0)
|
28
|
+
activerecord-deprecated_finders (1.0.3)
|
29
|
+
activesupport (4.0.2)
|
30
|
+
i18n (~> 0.6, >= 0.6.4)
|
31
|
+
minitest (~> 4.2)
|
32
|
+
multi_json (~> 1.3)
|
33
|
+
thread_safe (~> 0.1)
|
34
|
+
tzinfo (~> 0.3.37)
|
35
|
+
arel (4.0.1)
|
36
|
+
atomic (1.1.14)
|
37
|
+
builder (3.1.4)
|
38
|
+
coderay (1.1.0)
|
39
|
+
columnize (0.3.6)
|
40
|
+
database_cleaner (1.0.1)
|
41
|
+
debugger (1.6.5)
|
42
|
+
columnize (>= 0.3.1)
|
43
|
+
debugger-linecache (~> 1.2.0)
|
44
|
+
debugger-ruby_core_source (~> 1.3.1)
|
45
|
+
debugger-linecache (1.2.0)
|
46
|
+
debugger-ruby_core_source (1.3.1)
|
47
|
+
diff-lcs (1.2.5)
|
48
|
+
erubis (2.7.0)
|
49
|
+
hike (1.2.3)
|
50
|
+
i18n (0.6.9)
|
51
|
+
kramdown (1.3.1)
|
52
|
+
mail (2.5.4)
|
53
|
+
mime-types (~> 1.16)
|
54
|
+
treetop (~> 1.4.8)
|
55
|
+
metaclass (0.0.2)
|
56
|
+
method_source (0.8.2)
|
57
|
+
mime-types (1.25.1)
|
58
|
+
minitest (4.7.5)
|
59
|
+
mocha (1.0.0)
|
60
|
+
metaclass (~> 0.0.1)
|
61
|
+
multi_json (1.8.4)
|
62
|
+
pg (0.17.1)
|
63
|
+
polyglot (0.3.3)
|
64
|
+
pry (0.9.12.4)
|
65
|
+
coderay (~> 1.0)
|
66
|
+
method_source (~> 0.8)
|
67
|
+
slop (~> 3.4)
|
68
|
+
pry-debugger (0.2.2)
|
69
|
+
debugger (~> 1.3)
|
70
|
+
pry (~> 0.9.10)
|
71
|
+
rack (1.5.2)
|
72
|
+
rack-test (0.6.2)
|
73
|
+
rack (>= 1.0)
|
74
|
+
rails (4.0.2)
|
75
|
+
actionmailer (= 4.0.2)
|
76
|
+
actionpack (= 4.0.2)
|
77
|
+
activerecord (= 4.0.2)
|
78
|
+
activesupport (= 4.0.2)
|
79
|
+
bundler (>= 1.3.0, < 2.0)
|
80
|
+
railties (= 4.0.2)
|
81
|
+
sprockets-rails (~> 2.0.0)
|
82
|
+
railties (4.0.2)
|
83
|
+
actionpack (= 4.0.2)
|
84
|
+
activesupport (= 4.0.2)
|
85
|
+
rake (>= 0.8.7)
|
86
|
+
thor (>= 0.18.1, < 2.0)
|
87
|
+
rake (0.9.6)
|
88
|
+
rspec (2.14.0)
|
89
|
+
rspec-core (~> 2.14.0)
|
90
|
+
rspec-expectations (~> 2.14.0)
|
91
|
+
rspec-mocks (~> 2.14.0)
|
92
|
+
rspec-core (2.14.7)
|
93
|
+
rspec-expectations (2.14.4)
|
94
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
95
|
+
rspec-mocks (2.14.4)
|
96
|
+
rubygems-tasks (0.2.4)
|
97
|
+
slop (3.4.7)
|
98
|
+
sprockets (2.10.1)
|
99
|
+
hike (~> 1.2)
|
100
|
+
multi_json (~> 1.0)
|
101
|
+
rack (~> 1.0)
|
102
|
+
tilt (~> 1.1, != 1.3.0)
|
103
|
+
sprockets-rails (2.0.1)
|
104
|
+
actionpack (>= 3.0)
|
105
|
+
activesupport (>= 3.0)
|
106
|
+
sprockets (~> 2.8)
|
107
|
+
thor (0.18.1)
|
108
|
+
thread_safe (0.1.3)
|
109
|
+
atomic
|
110
|
+
tilt (1.4.1)
|
111
|
+
treetop (1.4.15)
|
112
|
+
polyglot
|
113
|
+
polyglot (>= 0.3.1)
|
114
|
+
tzinfo (0.3.38)
|
115
|
+
yard (0.8.7.3)
|
116
|
+
|
117
|
+
PLATFORMS
|
118
|
+
ruby
|
119
|
+
|
120
|
+
DEPENDENCIES
|
121
|
+
bundler (~> 1.0)
|
122
|
+
database_cleaner (~> 1.0)
|
123
|
+
kramdown
|
124
|
+
minitest (>= 4.7.5)
|
125
|
+
mocha (~> 1.0.0)
|
126
|
+
pg (~> 0.17.1)
|
127
|
+
pliable!
|
128
|
+
pry (~> 0.9.12)
|
129
|
+
pry-debugger (~> 0.2)
|
130
|
+
rails (>= 4.0)
|
131
|
+
rake (~> 0.8)
|
132
|
+
rspec (~> 2.14)
|
133
|
+
rubygems-tasks (~> 0.2)
|
134
|
+
yard (~> 0.8)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Mike Piccolo
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,287 @@
|
|
1
|
+
pliable
|
2
|
+
============
|
3
|
+
| Project | Gem Release |
|
4
|
+
|------------------------ | ----------------- |
|
5
|
+
| Gem name | pliable |
|
6
|
+
| License | [MIT](LICENSE.txt) |
|
7
|
+
| Version | [](http://badge.fury.io/rb/pliable) |
|
8
|
+
| Continuous Integration | [](https://travis-ci.org/mfpiccolo/pliable)
|
9
|
+
Z| Grade | [](https://codeclimate.com/github/mfpiccolo/pliable)
|
10
|
+
| Homepage | [http://mfpiccolo.github.io/pliable][homepage] |
|
11
|
+
| Documentation | [http://rdoc.info/github/mfpiccolo/pliable/frames][documentation] |
|
12
|
+
| Issues | [https://github.com/mfpiccolo/pliable/issues][issues] |
|
13
|
+
|
14
|
+
## Description
|
15
|
+
|
16
|
+
Pliable makes integrating a Rails project with Schemaless data not so painful.
|
17
|
+
|
18
|
+
Rolling your own integration with an external service where the schema can change from moment to moment can be tough. Pliable makes it a bit easier by giving you a familiar place to store this data (models backed by postgres) and familiar ways of interacting with it (Active Record objects, complete with associations).
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem "pliable"
|
26
|
+
```
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
$ bundle
|
31
|
+
|
32
|
+
Or install it yourself as:
|
33
|
+
|
34
|
+
$ gem install pliable
|
35
|
+
|
36
|
+
## Features
|
37
|
+
|
38
|
+
Pliable allows you to save individual records from external schemaless databases into your postgres
|
39
|
+
backed rails apps. We store all of the data in a plies table. The Ply model contains logic that allows
|
40
|
+
you to inherit from Ply and then act as if these iherited models are normal Active Record models.
|
41
|
+
|
42
|
+
Here is the Ply model your generator created:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class Ply < Pliable::Ply
|
46
|
+
# Define methods here that you want all you Ply backed models to have.
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
Now you can create a model that is backed by Ply.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class Foo < Ply
|
54
|
+
# This is redundant if it is the same name ass the class but required for now.
|
55
|
+
ply_name "Foo"
|
56
|
+
# Define methods that you only want Foo to have.
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
Now lets make another Ply Backed Model.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
class Bar < Ply
|
64
|
+
ply_name "Bar"
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
Now you should be able to treat these like any other Active Record object with the added bonus of a
|
69
|
+
few features. You can assign any json data to the data attribute.
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
foo = Foo.create(data: {"some" => "json", "other" => "data"})
|
73
|
+
```
|
74
|
+
The nice part is now these json keys are methods.
|
75
|
+
```ruby
|
76
|
+
foo.some => "json"
|
77
|
+
```
|
78
|
+
|
79
|
+
Another nicety is associations. You can associate a Ply inhereted class to another using parent
|
80
|
+
and child relationships and the PlyRelations model
|
81
|
+
```ruby
|
82
|
+
foo = Foo.create
|
83
|
+
bar = Bar.create
|
84
|
+
PlyRealation.create(parent_id: foo.id, parent_type: foo.class.name, child_id: bar, child_type: bar.class.name)
|
85
|
+
|
86
|
+
foo.bars => <#<ActiveRecord::AssociationRelation [#<Pliable::Ply id: 2 otype: "Bar" ...>
|
87
|
+
```
|
88
|
+
|
89
|
+
## Configuration
|
90
|
+
|
91
|
+
To use the generator run:
|
92
|
+
|
93
|
+
$ rails g pliable:model
|
94
|
+
|
95
|
+
This will set up pliable.rb initializer, create the migration and run it and add a Ply model and specs.
|
96
|
+
|
97
|
+
In the initializer, specify any aditional logic you need to prepare the ply_name for pluralization.
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
Pliable.configure do |config|
|
101
|
+
# Add logic to this bloc to change the names given by external services so we can pluralize.
|
102
|
+
# For instance if you ply names need to gsub __c of the end do:
|
103
|
+
config.added_scrubber {|name| name.gsub('__c', '') }
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
## Examples
|
108
|
+
|
109
|
+
An example of a salesforce integration:
|
110
|
+
|
111
|
+
Here is your Ply model:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# Notice it inherits from Pliable::Ply
|
115
|
+
class Ply < Pliable::Ply
|
116
|
+
|
117
|
+
# Define methods here that you want all of your models to have
|
118
|
+
def anything_you_like
|
119
|
+
puts "I can play disco all night"
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
This is an example salesforce Invoice model:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
# Notice it inherits from your apps Ply
|
129
|
+
class Invoice < Ply
|
130
|
+
|
131
|
+
# If you dont put this you will get all Ply records.
|
132
|
+
# This is the name that you have put into the otype attribute.
|
133
|
+
# In this example I just used the exact salesforce api name
|
134
|
+
ply_name "Invoice__c"
|
135
|
+
|
136
|
+
# Add Invoice specific methods here
|
137
|
+
def what_dosnt_gather_moss?
|
138
|
+
"A rolling stone!"
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
Here is an example associated salesforce model:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
class LineItem < Ply
|
148
|
+
|
149
|
+
ply_name "Line_Item__c"
|
150
|
+
|
151
|
+
# You guessed it. LineItem specific methods here.
|
152
|
+
def best_pliable_quote
|
153
|
+
"Facts are stubborn, but statistics are more pliable. - Mark Twain"
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
Here is your PlyRelation model:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
# This will probably not be needed in the future and will live in the gem
|
163
|
+
class PlyRelation < ActiveRecord::Base
|
164
|
+
belongs_to :parent, class_name: 'Ply'
|
165
|
+
belongs_to :child, class_name: 'Ply'
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
A service object for pulling salesforce data into your app:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
class SalesforceSynch
|
173
|
+
|
174
|
+
attr_reader :user, :client
|
175
|
+
|
176
|
+
def initialize(user)
|
177
|
+
@user = user
|
178
|
+
end
|
179
|
+
|
180
|
+
def call
|
181
|
+
set_clients # Connect to your Salesforce data (i.e. databsedotcom or restforce)
|
182
|
+
create_plys_from_salesforce_records # Create records in your PG database using Ply model
|
183
|
+
create_ply_relations # Dynamically create associations based on the data recieved
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.call
|
187
|
+
new.call
|
188
|
+
end
|
189
|
+
|
190
|
+
def set_clients
|
191
|
+
#Fake service object that sets up a client to connect to databasedotcom
|
192
|
+
@client = ConnectToDatabasedotcom.call(user.salesforce_credentials)
|
193
|
+
end
|
194
|
+
|
195
|
+
def create_plys_from_salesforce_records
|
196
|
+
data = []
|
197
|
+
|
198
|
+
# sf_api model names as strings in array
|
199
|
+
records = []
|
200
|
+
|
201
|
+
# User has_many :plies in this example (i.e. user.plies)
|
202
|
+
client.get_the_records_you_want.each do |record|
|
203
|
+
object = user.plies.find_or_create_by(oid: record.Id)
|
204
|
+
object.update_attributes(
|
205
|
+
# The data attribute is a json column. This is where you store all shcemaless data.
|
206
|
+
data: record.attributes,
|
207
|
+
# Whatever the service calls the object (i.e. Invoice__c for salesforce)
|
208
|
+
otype: record.salesforce_api_name,
|
209
|
+
# Use last_checked and last_modified to know when you need to update a record
|
210
|
+
last_checked: Time.zone.now
|
211
|
+
)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Dynamically deduce if there is a relationship with any of the plys that have been imported.
|
216
|
+
# In the case of saleforce the id of related object is stored by using the name of that
|
217
|
+
# object as a key. (ie "Invoice__c" => "long_uiniq_id"). In this app users choose a few models
|
218
|
+
# that they want to bring over but you could easily just get everything.
|
219
|
+
user.plies.each do |ply|
|
220
|
+
related_model_names = ply.instance_variables.map {|e| e.to_s.gsub("@", "") } & user.model_names
|
221
|
+
related_model_names.each do |name|
|
222
|
+
child = Ply.find_by_oid(ply.send(name.to_sym))
|
223
|
+
unless PlyRelation.where(parent_id: record.id, child_id: child.id).present?
|
224
|
+
ply.children.new(
|
225
|
+
parent_id: ply.id,
|
226
|
+
parent_type: ply.otype,
|
227
|
+
child_id: ply.id,
|
228
|
+
child_type: ply.otype
|
229
|
+
).save # #create does not work yet. Sorry
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
```
|
236
|
+
|
237
|
+
Now with this setup you can run something like this
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
SalesforceSynch.call(@user) # Awesome. You just imported all your salesforce data.
|
241
|
+
|
242
|
+
invoice = Invoice.first => #<Invoice id: 1, user_id: 1, oid: "randomnumber", otype: "Invoice__c",
|
243
|
+
# data: {"Id"=>"a00i000000BbWLvAAN", "OwnerId"=>"005i0000002NdyWAAS", "Owner"=>nil...}...>
|
244
|
+
|
245
|
+
invoice.line_items => #<ActiveRecord::AssociationRelation [#<Pliable::Ply id: 2 ...>
|
246
|
+
|
247
|
+
invoice.line_items.first.invoices.find(invoice.id) === invoice => true
|
248
|
+
|
249
|
+
invoice.SalesForceCustomAttribute__c => Whatever it is in salesforce.
|
250
|
+
|
251
|
+
Invoice.all => #<ActiveRecord::Relation [#<Invoice id: 136, user_id: 1...>
|
252
|
+
|
253
|
+
LineItem.first => #<LineItem id: 145, user_id: 1...>
|
254
|
+
|
255
|
+
Invoice.find_by_oid("random_oid_number") => #<Invoice id: 132, user_id: 1, oid: "rand...">
|
256
|
+
|
257
|
+
# All the normal active-recordy goodness
|
258
|
+
```
|
259
|
+
|
260
|
+
## Requirements
|
261
|
+
|
262
|
+
## Donating
|
263
|
+
Support this project and [others by mfpiccolo][gittip-mfpiccolo] via [gittip][gittip-mfpiccolo].
|
264
|
+
|
265
|
+
[gittip-mfpiccolo]: https://www.gittip.com/mfpiccolo/
|
266
|
+
|
267
|
+
## Copyright
|
268
|
+
|
269
|
+
Copyright (c) 2013 Mike Piccolo
|
270
|
+
|
271
|
+
See [LICENSE.txt](LICENSE.txt) for details.
|
272
|
+
|
273
|
+
## Contributing
|
274
|
+
|
275
|
+
1. Fork it ( http://github.com/<my-github-username>/pliable/fork )
|
276
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
277
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
278
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
279
|
+
5. Create new Pull Request
|
280
|
+
|
281
|
+
[](http://githalytics.com/mfpiccolo/pliable)
|
282
|
+
|
283
|
+
[license]: https://github.com/mfpiccolo/pliable/MIT-LICENSE
|
284
|
+
[homepage]: http://mfpiccolo.github.io/pliable
|
285
|
+
[documentation]: http://rdoc.info/github/mfpiccolo/pliable/frames
|
286
|
+
[issues]: https://github.com/mfpiccolo/pliable/issues
|
287
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
rescue LoadError => e
|
8
|
+
warn e.message
|
9
|
+
warn "Run `gem install bundler` to install Bundler."
|
10
|
+
exit -1
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Bundler.setup(:development)
|
15
|
+
rescue Bundler::BundlerError => e
|
16
|
+
warn e.message
|
17
|
+
warn "Run `bundle install` to install missing gems."
|
18
|
+
exit e.status_code
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake'
|
22
|
+
|
23
|
+
require 'rubygems/tasks'
|
24
|
+
Gem::Tasks.new
|
25
|
+
|
26
|
+
require 'rake/testtask'
|
27
|
+
|
28
|
+
Rake::TestTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'yard'
|
35
|
+
YARD::Rake::YardocTask.new
|
36
|
+
task :doc => :yard
|
37
|
+
|
38
|
+
task :default => :test
|
39
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rails/generators/active_record"
|
2
|
+
|
3
|
+
module Pliable
|
4
|
+
module Generators
|
5
|
+
class ModelGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
def self.next_migration_number(path)
|
9
|
+
@migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
source_root File.expand_path("../templates", __FILE__)
|
13
|
+
|
14
|
+
def add_initializer
|
15
|
+
create_file "config/initializers/pliable.rb", "Pliable.configure do |config|
|
16
|
+
# define extra scrubbing for ply_name here. This is for the purpose of making scopes.
|
17
|
+
# For instance, if your models ply name is something like 'Invoice__c'
|
18
|
+
# you will need to gsub '__c' off the end:
|
19
|
+
# config.added_scrubber {|name| name.gsub('__c', '') }
|
20
|
+
end"
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_ply_and_relation_model
|
24
|
+
Rails::Generators.invoke("active_record:model", ["Ply", "--parent", "pliable/ply", "--no-migration" ])
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_migration
|
28
|
+
# TODO only run if migration dosn't exist
|
29
|
+
migration_template "migration.rb", "db/migrate/create_plies_and_ply_relations"
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_migrations
|
33
|
+
unless (ActiveRecord::Base.connection.table_exists?('plies') && (ActiveRecord::Base.connection.table_exists? 'ply_relations'))
|
34
|
+
rake("db:migrate db:test:prepare")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class CreatePliesAndPlyRelations < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :plies do |t|
|
4
|
+
t.integer :user_id
|
5
|
+
t.string :oid
|
6
|
+
t.string :otype
|
7
|
+
t.json :data
|
8
|
+
t.hstore :ohash
|
9
|
+
t.datetime :last_modified
|
10
|
+
t.datetime :last_checked
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :ply_relations do |t|
|
16
|
+
t.integer :parent_id
|
17
|
+
t.string :parent_type
|
18
|
+
t.integer :child_id
|
19
|
+
t.string :child_type
|
20
|
+
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/pliable/ply.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module Pliable
|
4
|
+
class Ply < ActiveRecord::Base
|
5
|
+
# TODO move to Dummy
|
6
|
+
|
7
|
+
# Allows an ply to associate another ply as either a parent or child
|
8
|
+
has_many :ply_relations
|
9
|
+
has_many :parent_relations, class_name: "PlyRelation", foreign_key: "child_id"
|
10
|
+
has_many :parents, through: :parent_relations, source: :parent
|
11
|
+
has_many :child_relations, class_name: "PlyRelation", foreign_key: "parent_id"
|
12
|
+
has_many :children, through: :child_relations, source: :child
|
13
|
+
|
14
|
+
after_initialize :set_ply_attributes
|
15
|
+
after_initialize :define_ply_scopes
|
16
|
+
|
17
|
+
def self.oldest_last_checked_time
|
18
|
+
order('last_checked').first.last_checked
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.all
|
22
|
+
if current_scope
|
23
|
+
current_scope.clone
|
24
|
+
else
|
25
|
+
if self.name == "Pliable::Ply"
|
26
|
+
scope = relation
|
27
|
+
else
|
28
|
+
if self.try(:ply_type)
|
29
|
+
scope = relation.where(otype: self.try(:ply_type))
|
30
|
+
else
|
31
|
+
scope = relation
|
32
|
+
end
|
33
|
+
end
|
34
|
+
scope.default_scoped = true
|
35
|
+
scope
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.ply_name(name)
|
40
|
+
define_singleton_method(:ply_type) { name }
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_param
|
44
|
+
oid
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def set_ply_attributes
|
50
|
+
if data.present?
|
51
|
+
data.each do |key,value|
|
52
|
+
instance_variable_set(('@' + key.to_s).to_sym, value)
|
53
|
+
define_singleton_method(key.to_s) { instance_variable_get(('@' + key.to_s).to_sym) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def define_ply_scopes
|
59
|
+
# pluralize is not perfect. ie. Merchandise__c => merchandises
|
60
|
+
child_names = children.pluck(:child_type).uniq
|
61
|
+
parent_names = parents.pluck(:parent_type).uniq
|
62
|
+
|
63
|
+
add_scopes(child_names, parent_names)
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_scopes(child_names, parent_names)
|
67
|
+
child_names.each do |name|
|
68
|
+
define_singleton_method(scrub_for_scope(name)) do
|
69
|
+
children.where(otype: name)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
parent_names.each do |name|
|
74
|
+
define_singleton_method(scrub_for_scope(name)) do
|
75
|
+
parents.where(otype: name)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# user initializer to overwrite this method
|
81
|
+
def scrub_for_scope(name)
|
82
|
+
if respond_to? :added_scrubber
|
83
|
+
name = added_scrubber(name)
|
84
|
+
end
|
85
|
+
|
86
|
+
TextHelper.pluralize(name.downcase)
|
87
|
+
end
|
88
|
+
end # Ply
|
89
|
+
end # Pliable
|