mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-25 09:19:15 -04:00
Compare commits
841 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25070f73d7 | ||
|
|
2fe6012342 | ||
|
|
c80685bf0d | ||
|
|
511e89fa58 | ||
|
|
13d8a29773 | ||
|
|
dc09a6ea91 | ||
|
|
f8e83cdb9c | ||
|
|
64a8cfb59f | ||
|
|
2f81d6c78d | ||
|
|
ece7113c89 | ||
|
|
8941c577e4 | ||
|
|
1bf1076076 | ||
|
|
8906fd38ae | ||
|
|
d5be92ac8b | ||
|
|
40d71ae070 | ||
|
|
2f45041f8c | ||
|
|
f9d4e1d6ae | ||
|
|
80203ad215 | ||
|
|
8c51d7bdd5 | ||
|
|
64e3b3b4d4 | ||
|
|
978523fd94 | ||
|
|
97f63d1147 | ||
|
|
1a9ff5eb4c | ||
|
|
72b1494db9 | ||
|
|
83bba916a4 | ||
|
|
64179850ff | ||
|
|
9cdf2e899f | ||
|
|
2eacf346a8 | ||
|
|
aab91e2fa9 | ||
|
|
2217d87b5a | ||
|
|
44b2251896 | ||
|
|
f5e20cd2d2 | ||
|
|
42de726405 | ||
|
|
65b4c7dbc1 | ||
|
|
6b6ed7e31c | ||
|
|
564901f93f | ||
|
|
f8127eeec3 | ||
|
|
33fb2408d6 | ||
|
|
ab56a3c6b3 | ||
|
|
ef4b4d0c46 | ||
|
|
44ba29137d | ||
|
|
ed2fcc2ada | ||
|
|
0b5c45d08f | ||
|
|
54c880a66c | ||
|
|
0699677d05 | ||
|
|
3c411f15b8 | ||
|
|
dcf5286275 | ||
|
|
7e1e2bef91 | ||
|
|
5cda14f87a | ||
|
|
88da0da616 | ||
|
|
a69afd87e7 | ||
|
|
57bfafccc7 | ||
|
|
24931381af | ||
|
|
742c7e30a9 | ||
|
|
2f3e368ede | ||
|
|
502a20cf10 | ||
|
|
b2cf022522 | ||
|
|
3d4598e4e1 | ||
|
|
94db28b3a9 | ||
|
|
767477df66 | ||
|
|
7f87ee1018 | ||
|
|
093a948922 | ||
|
|
be1553410f | ||
|
|
caf41e80c9 | ||
|
|
2eff7d6716 | ||
|
|
df5006c61c | ||
|
|
1e0565dd9b | ||
|
|
3453a648fa | ||
|
|
e8d9314cff | ||
|
|
92e56cfc1e | ||
|
|
3dc356eb29 | ||
|
|
56ed61b2b3 | ||
|
|
16ad5384d1 | ||
|
|
cf93489af7 | ||
|
|
86b9a3650b | ||
|
|
625a035f44 | ||
|
|
196d37dee9 | ||
|
|
6f613635d2 | ||
|
|
46267b2a1d | ||
|
|
8c8d549cb1 | ||
|
|
d9cc1dcae8 | ||
|
|
9db4736229 | ||
|
|
04a7c41f63 | ||
|
|
78b8be1fc2 | ||
|
|
b6e9539127 | ||
|
|
33276874a8 | ||
|
|
9adb3d1e4e | ||
|
|
3391f6c392 | ||
|
|
a13acbe087 | ||
|
|
0c8240d4e8 | ||
|
|
5ea7ae8af0 | ||
|
|
eb1b890eec | ||
|
|
4821bf3b07 | ||
|
|
b83db1d4e7 | ||
|
|
dea7ce241b | ||
|
|
8d166f1fe6 | ||
|
|
49253b59ee | ||
|
|
ca16b2c407 | ||
|
|
427a0187e1 | ||
|
|
b1096cd021 | ||
|
|
020d024e4b | ||
|
|
070a4605d0 | ||
|
|
1f8413bf3e | ||
|
|
59c3edcf82 | ||
|
|
1bf05f81ab | ||
|
|
e6e88fa0c7 | ||
|
|
308e834713 | ||
|
|
121deb4f48 | ||
|
|
4b65f28461 | ||
|
|
87708f1765 | ||
|
|
2cf335a064 | ||
|
|
b205f35399 | ||
|
|
a4c06207e3 | ||
|
|
580fac6aa1 | ||
|
|
4a696af9ba | ||
|
|
d2a060ad49 | ||
|
|
d898f926d5 | ||
|
|
49444edc61 | ||
|
|
a4c7de381e | ||
|
|
61408d7e4e | ||
|
|
9906036faf | ||
|
|
e009d9532a | ||
|
|
9f1c064900 | ||
|
|
8d3d8f3c91 | ||
|
|
0feed2d359 | ||
|
|
c08beb5291 | ||
|
|
269f3164b7 | ||
|
|
9d47b2b029 | ||
|
|
81e26aaf02 | ||
|
|
d62322b498 | ||
|
|
ebb2f90294 | ||
|
|
72922a160d | ||
|
|
0699716c19 | ||
|
|
4c35987cb7 | ||
|
|
54e0806f38 | ||
|
|
99828c7c01 | ||
|
|
25d2773464 | ||
|
|
233a30a045 | ||
|
|
01a7463ae8 | ||
|
|
8a446ecbfc | ||
|
|
efc60e6313 | ||
|
|
406b2dbf04 | ||
|
|
ac056b4eb5 | ||
|
|
2394f4d237 | ||
|
|
2a4fd57050 | ||
|
|
5c885d675e | ||
|
|
0b08691160 | ||
|
|
b1052a2dbf | ||
|
|
95a923ff5f | ||
|
|
c8e34b640e | ||
|
|
6f2b4b13cd | ||
|
|
970da53873 | ||
|
|
3aa2f0bd3b | ||
|
|
8c1514bc04 | ||
|
|
d2e39f58d5 | ||
|
|
3d3a3b669e | ||
|
|
8697e56323 | ||
|
|
472b2eb192 | ||
|
|
1698157d8d | ||
|
|
ff652ae234 | ||
|
|
b4fcf004cd | ||
|
|
25e9ef892d | ||
|
|
82149d4565 | ||
|
|
fcd3e36e36 | ||
|
|
b783448ec3 | ||
|
|
e8b5850bfe | ||
|
|
bca72680c4 | ||
|
|
358eb96684 | ||
|
|
05807d41f4 | ||
|
|
96f1995768 | ||
|
|
deefbbce71 | ||
|
|
f8e51f4b45 | ||
|
|
1c8b75b7cb | ||
|
|
1f66df0743 | ||
|
|
72f52230a1 | ||
|
|
700fba58e8 | ||
|
|
acecbe93c1 | ||
|
|
bafe2b7fd1 | ||
|
|
3be1454a69 | ||
|
|
232e5d50ac | ||
|
|
7207a39039 | ||
|
|
291f865f7f | ||
|
|
47e82eb0a7 | ||
|
|
08dc2a0513 | ||
|
|
4f64698403 | ||
|
|
14551b6ec8 | ||
|
|
5d44196947 | ||
|
|
1c41cb7b66 | ||
|
|
c3c55eaa09 | ||
|
|
98aa9521fe | ||
|
|
96bb345ca9 | ||
|
|
9de991a075 | ||
|
|
f76619864c | ||
|
|
ec149997b0 | ||
|
|
ac07f18b2d | ||
|
|
4ce3e8968f | ||
|
|
cd35538b83 | ||
|
|
0f317f624e | ||
|
|
35d8461811 | ||
|
|
8d03a3ffa8 | ||
|
|
2b0f199329 | ||
|
|
24f18f78da | ||
|
|
3cc637017d | ||
|
|
37f9dcd7dd | ||
|
|
f516ef2e57 | ||
|
|
182c626226 | ||
|
|
ea2fcd09dc | ||
|
|
878fc7ebcd | ||
|
|
3fdc23098e | ||
|
|
515325c44e | ||
|
|
25b1836146 | ||
|
|
c925c95398 | ||
|
|
2d00206abf | ||
|
|
f499f245de | ||
|
|
35414391c3 | ||
|
|
1f7f4f52d1 | ||
|
|
b4efeee904 | ||
|
|
5b588bf9e9 | ||
|
|
07611e9a5d | ||
|
|
baa89a790f | ||
|
|
00395ae68b | ||
|
|
41e26a2c74 | ||
|
|
d7eb5e0014 | ||
|
|
f08d02f4c5 | ||
|
|
a6a7f789a3 | ||
|
|
c4c659f159 | ||
|
|
14b16b0d8c | ||
|
|
697779e67c | ||
|
|
35ce8b84db | ||
|
|
ccbdfe51f6 | ||
|
|
ff57176d60 | ||
|
|
12700d6268 | ||
|
|
6ed7376c58 | ||
|
|
c8a200bf19 | ||
|
|
a62b668781 | ||
|
|
09f82d5995 | ||
|
|
8d0bec34a6 | ||
|
|
d7ec4eacee | ||
|
|
3a3f4eefbf | ||
|
|
9e00023332 | ||
|
|
3058160a59 | ||
|
|
e3886ce5b5 | ||
|
|
48f8085e2d | ||
|
|
594e466fd4 | ||
|
|
b813669d71 | ||
|
|
d0b8ae3411 | ||
|
|
9c993e9f47 | ||
|
|
d5fece87d0 | ||
|
|
33816d4312 | ||
|
|
00e6aeed5c | ||
|
|
0205b345d0 | ||
|
|
9f8a32e9f8 | ||
|
|
7357524271 | ||
|
|
6aa689b0d8 | ||
|
|
962b3b574d | ||
|
|
d77d1d9a77 | ||
|
|
4bed6da422 | ||
|
|
023c870d49 | ||
|
|
9aff65e956 | ||
|
|
0446c0a405 | ||
|
|
7719d1f896 | ||
|
|
17fd6f61a5 | ||
|
|
c9558e4d51 | ||
|
|
8a679670e4 | ||
|
|
a7586e9d2b | ||
|
|
811011419e | ||
|
|
dae3ea2789 | ||
|
|
49aa6bb846 | ||
|
|
c154a9b845 | ||
|
|
ad14ecb41b | ||
|
|
08a73ce503 | ||
|
|
6663f3ade4 | ||
|
|
451030761d | ||
|
|
d4e4ca6519 | ||
|
|
284f0cddcd | ||
|
|
4ced7ee7e9 | ||
|
|
b60510e016 | ||
|
|
e85daa405b | ||
|
|
9ab83f7bbc | ||
|
|
329e0106bb | ||
|
|
4743fc7a63 | ||
|
|
c66a5b12a5 | ||
|
|
f98bb32612 | ||
|
|
771443ee85 | ||
|
|
1322ebd8e3 | ||
|
|
9e98db423e | ||
|
|
305702f644 | ||
|
|
1f4f806bb0 | ||
|
|
6a17ba547f | ||
|
|
3ffdc61428 | ||
|
|
bb6e9e32e6 | ||
|
|
229396d9e7 | ||
|
|
0fa02272b0 | ||
|
|
a91b8605c4 | ||
|
|
f1107b518c | ||
|
|
52d93a7ccd | ||
|
|
d3df1e8f33 | ||
|
|
eb77715b70 | ||
|
|
252910694d | ||
|
|
bd063be3a9 | ||
|
|
9a6d827b6d | ||
|
|
ede5187369 | ||
|
|
610a5b7a48 | ||
|
|
1fa71c1546 | ||
|
|
7574eb554e | ||
|
|
eea7e19ecf | ||
|
|
dafaffcd73 | ||
|
|
3e149a5d1d | ||
|
|
c20d9b78c5 | ||
|
|
7906d50d13 | ||
|
|
faed8b019e | ||
|
|
c8974a869f | ||
|
|
3969f410f1 | ||
|
|
6304f230a5 | ||
|
|
73524b8883 | ||
|
|
fd9fc38def | ||
|
|
949c7c31d8 | ||
|
|
e34dcd03bc | ||
|
|
929229379e | ||
|
|
d8bf8ae4df | ||
|
|
ac0e52f15d | ||
|
|
c7f427fe8a | ||
|
|
50fd9ae9b0 | ||
|
|
efe39da878 | ||
|
|
1cd3c64590 | ||
|
|
b591529892 | ||
|
|
cef236d8f8 | ||
|
|
5ad0068142 | ||
|
|
b8a5ff9acf | ||
|
|
90653aa57f | ||
|
|
80a4739f34 | ||
|
|
a6cc530fe5 | ||
|
|
97f19a0038 | ||
|
|
140611d868 | ||
|
|
3fa5ffc544 | ||
|
|
eeafb4caa5 | ||
|
|
c747d46ab5 | ||
|
|
3009ed2d83 | ||
|
|
f68a15c04e | ||
|
|
06295fb9e9 | ||
|
|
c4a1806a87 | ||
|
|
fa5e151af0 | ||
|
|
13e8d1f5a5 | ||
|
|
c1836cc78e | ||
|
|
28fdb6e1ed | ||
|
|
67dec795a0 | ||
|
|
86ea8eab39 | ||
|
|
8884056ff3 | ||
|
|
d01a5d7976 | ||
|
|
66c08650ba | ||
|
|
521873b656 | ||
|
|
83fea92353 | ||
|
|
2060d9ab9e | ||
|
|
1795b27152 | ||
|
|
1317ce9db0 | ||
|
|
93193c9fba | ||
|
|
e489831241 | ||
|
|
df7a9448b0 | ||
|
|
3d4f5c6917 | ||
|
|
1b4d77e017 | ||
|
|
08e7c7dd29 | ||
|
|
5fec826c41 | ||
|
|
c29234c40c | ||
|
|
2f0cb5c815 | ||
|
|
b6cbfe25d2 | ||
|
|
d8f5876518 | ||
|
|
ae2cbf0bec | ||
|
|
c9d5c42814 | ||
|
|
b60f47f502 | ||
|
|
d86e0af5ad | ||
|
|
fc0058d677 | ||
|
|
41110f96d1 | ||
|
|
33822abf93 | ||
|
|
5877da95a6 | ||
|
|
88a119deac | ||
|
|
d07fd1a277 | ||
|
|
c96695258b | ||
|
|
2fbba52bc8 | ||
|
|
a2ae429f6b | ||
|
|
7ca76e69cf | ||
|
|
c6824b1262 | ||
|
|
2a74c057f3 | ||
|
|
85cc9e77c6 | ||
|
|
a0d6d154f0 | ||
|
|
7e9707e3b0 | ||
|
|
4fe0dba28e | ||
|
|
328037b8c0 | ||
|
|
e4ab5114f7 | ||
|
|
a5d43af899 | ||
|
|
2d384c9851 | ||
|
|
205247b136 | ||
|
|
963a182981 | ||
|
|
1090db6d21 | ||
|
|
f187b31653 | ||
|
|
809dd81378 | ||
|
|
62beec23a1 | ||
|
|
a60a5ce591 | ||
|
|
7206f99510 | ||
|
|
bf6475da57 | ||
|
|
c5971cd46b | ||
|
|
2f47eeb7b3 | ||
|
|
928749aaad | ||
|
|
1d64d4d1d2 | ||
|
|
1e86cf7662 | ||
|
|
941dab7c4a | ||
|
|
2696d12771 | ||
|
|
6bf87c243c | ||
|
|
ec63d8086f | ||
|
|
470aacd1c9 | ||
|
|
9997c365d8 | ||
|
|
eaa65fda9e | ||
|
|
e844eade94 | ||
|
|
35e7b6a5b4 | ||
|
|
6c33c335d8 | ||
|
|
26537943e3 | ||
|
|
136c082003 | ||
|
|
b3fd8e32d0 | ||
|
|
9b1b22afa8 | ||
|
|
996706ee78 | ||
|
|
9d11bcc039 | ||
|
|
b1684b0156 | ||
|
|
ec00baef0d | ||
|
|
eb585611fe | ||
|
|
9280049a84 | ||
|
|
53d7fd1479 | ||
|
|
4cc0f77bd0 | ||
|
|
5e5773aa59 | ||
|
|
2422939b4f | ||
|
|
d5a1a683f1 | ||
|
|
f5bb47e4ca | ||
|
|
8359218294 | ||
|
|
285e6edc61 | ||
|
|
f2fac9db1b | ||
|
|
cabc4fd030 | ||
|
|
20fe9e4cc5 | ||
|
|
fac24c5e99 | ||
|
|
fa600bea9e | ||
|
|
99e7fb1f7d | ||
|
|
81e22d6fb2 | ||
|
|
10a8df47a9 | ||
|
|
b0a4df629c | ||
|
|
0b8357a261 | ||
|
|
7104781d1b | ||
|
|
267f27b3f8 | ||
|
|
31a37bf6dc | ||
|
|
d1951b8b65 | ||
|
|
cd99769311 | ||
|
|
71cb3f8c5f | ||
|
|
d540342acb | ||
|
|
f66b0768e2 | ||
|
|
251f44b0c7 | ||
|
|
adf07ce5ef | ||
|
|
54b204c636 | ||
|
|
ff446c2139 | ||
|
|
62d84d7af4 | ||
|
|
92fa09a1d3 | ||
|
|
b09c4aa7a1 | ||
|
|
692ab59285 | ||
|
|
302bdba150 | ||
|
|
aa3057056f | ||
|
|
9ef40532ad | ||
|
|
70ba8c8421 | ||
|
|
3b81affbe5 | ||
|
|
9703d4e983 | ||
|
|
22e1718ce6 | ||
|
|
b90b4e42fb | ||
|
|
202f557e32 | ||
|
|
8364c8c688 | ||
|
|
3a286cd240 | ||
|
|
41dbf6fb1b | ||
|
|
47ecbe3f94 | ||
|
|
0f066ab302 | ||
|
|
8e94ddb4bf | ||
|
|
a9cedc6a8a | ||
|
|
7d6c49d8c3 | ||
|
|
0d8ca89b14 | ||
|
|
82a37d69bc | ||
|
|
4533d35fe7 | ||
|
|
4b445907d5 | ||
|
|
fce6395d21 | ||
|
|
2946dca5fc | ||
|
|
9eb6d84e5a | ||
|
|
96b4742e78 | ||
|
|
68c14a695e | ||
|
|
e42ec4f8ae | ||
|
|
f7b75157e7 | ||
|
|
7e27b6deef | ||
|
|
cd69ea1487 | ||
|
|
4cdcbdac19 | ||
|
|
f3d03c00b2 | ||
|
|
d9aa9df7fe | ||
|
|
494dd1d658 | ||
|
|
af60489459 | ||
|
|
a3a88c48de | ||
|
|
f3e88f5aae | ||
|
|
1e81dac699 | ||
|
|
705240014a | ||
|
|
78aa049502 | ||
|
|
97c7277a73 | ||
|
|
0e0af44d9f | ||
|
|
45af8d5f4f | ||
|
|
a1ee822690 | ||
|
|
b6e61eba7a | ||
|
|
b356d86931 | ||
|
|
34f91c2189 | ||
|
|
faf3f60e78 | ||
|
|
e3113fd53d | ||
|
|
68768c4d6a | ||
|
|
da7fec5e0b | ||
|
|
191c8e4e9d | ||
|
|
edc763cd8c | ||
|
|
fdfb116d92 | ||
|
|
888e0669a0 | ||
|
|
d55e2b5466 | ||
|
|
c28ae4fe83 | ||
|
|
4b4669459e | ||
|
|
77902ad300 | ||
|
|
f314fcd919 | ||
|
|
8171c12a5b | ||
|
|
08b6b46bfa | ||
|
|
bf189092ea | ||
|
|
a539a47044 | ||
|
|
e29ac8827a | ||
|
|
c5cee72ae5 | ||
|
|
5396550a8a | ||
|
|
76d2fb37b4 | ||
|
|
a42676ef0a | ||
|
|
3f3fc6a4ca | ||
|
|
c710a8b1ef | ||
|
|
6ada0c4f84 | ||
|
|
2e5a2455d6 | ||
|
|
d89d4cea39 | ||
|
|
01a82ac993 | ||
|
|
68660b3c65 | ||
|
|
67b4f3558e | ||
|
|
63b973833f | ||
|
|
4b19f73eb8 | ||
|
|
9b4d6044ef | ||
|
|
9c70ffe051 | ||
|
|
e7a1d2345d | ||
|
|
c7585f1931 | ||
|
|
63b76d175d | ||
|
|
1d5d2cc704 | ||
|
|
9477b4f9c7 | ||
|
|
a42a61abdd | ||
|
|
62eaa07862 | ||
|
|
f26420c380 | ||
|
|
6de13b7327 | ||
|
|
310f631f40 | ||
|
|
bec59aba43 | ||
|
|
c86902967d | ||
|
|
361e3df692 | ||
|
|
ce18bef892 | ||
|
|
ac2b0d04ea | ||
|
|
9d0b48b84c | ||
|
|
d8f41b5ca0 | ||
|
|
b5fbfaf515 | ||
|
|
ce9a71cdad | ||
|
|
42a10bcd80 | ||
|
|
33656a4104 | ||
|
|
ca200c1f30 | ||
|
|
ccd352d699 | ||
|
|
bd396dabfe | ||
|
|
1279dd63bc | ||
|
|
8d681dd90a | ||
|
|
dfd0e1dbf5 | ||
|
|
0d555542c2 | ||
|
|
99a69cd492 | ||
|
|
e8e9aa23b2 | ||
|
|
2fafc6f3e4 | ||
|
|
6b1714eb05 | ||
|
|
8e5c52e85e | ||
|
|
c749f4a813 | ||
|
|
5ed5f9ec7e | ||
|
|
f4658df208 | ||
|
|
e21b95a353 | ||
|
|
19205cb6a8 | ||
|
|
17521cde17 | ||
|
|
5f7d48c633 | ||
|
|
94bb3bf6bb | ||
|
|
c9aacd0110 | ||
|
|
ca5f14bb37 | ||
|
|
43da004ccb | ||
|
|
2704fcf515 | ||
|
|
1af19335d7 | ||
|
|
4d33c32866 | ||
|
|
01333bf965 | ||
|
|
a145abff56 | ||
|
|
9732553d3b | ||
|
|
9142613b6b | ||
|
|
7132c95b61 | ||
|
|
38b2d8de0f | ||
|
|
8f76ccf836 | ||
|
|
491982f8f3 | ||
|
|
58e6411bbe | ||
|
|
ac7e116eae | ||
|
|
1526257a0d | ||
|
|
7234848c5f | ||
|
|
6e453707b9 | ||
|
|
dac2d1d4c2 | ||
|
|
6c4c1ff8ba | ||
|
|
93d1bfd05d | ||
|
|
6167e291d2 | ||
|
|
deb1e27176 | ||
|
|
82bb6c2f3b | ||
|
|
24d9d714b2 | ||
|
|
d9ad55db7c | ||
|
|
4dad76e635 | ||
|
|
252791b47d | ||
|
|
aa849c2e1f | ||
|
|
b03a59cb93 | ||
|
|
e761ecc356 | ||
|
|
69b887ba79 | ||
|
|
e4c2f777ac | ||
|
|
f0cb635b42 | ||
|
|
4116e201b5 | ||
|
|
5788c0c5ce | ||
|
|
6f6e0a58a3 | ||
|
|
15b49672ee | ||
|
|
9e2c7d0748 | ||
|
|
5ff0e2c565 | ||
|
|
d40665fb08 | ||
|
|
3b6a42b8f2 | ||
|
|
cf3772af43 | ||
|
|
2f30343303 | ||
|
|
8de7e7a00b | ||
|
|
46efcf3e61 | ||
|
|
7968f3e407 | ||
|
|
639232bc87 | ||
|
|
e0bc1c936a | ||
|
|
910a9673dc | ||
|
|
582dd2c9fd | ||
|
|
1a743d7592 | ||
|
|
174040aa1b | ||
|
|
4471ac64e4 | ||
|
|
a8bb6c9f58 | ||
|
|
7d7053f558 | ||
|
|
6f59896d96 | ||
|
|
e8cf19cc9d | ||
|
|
5a3d15d101 | ||
|
|
142be72fc3 | ||
|
|
c6331fc449 | ||
|
|
e8455cd943 | ||
|
|
080e728905 | ||
|
|
db532fcc8f | ||
|
|
e78d899528 | ||
|
|
1f916b8314 | ||
|
|
1cdcaac41e | ||
|
|
bbc3ca749f | ||
|
|
20b980a162 | ||
|
|
37897ad595 | ||
|
|
72f805e135 | ||
|
|
1bb4f601a1 | ||
|
|
fb012c073e | ||
|
|
3dfd9eedca | ||
|
|
75d2c0ea3c | ||
|
|
08996c4157 | ||
|
|
c48e24845e | ||
|
|
61f93df6ff | ||
|
|
34e5e0850d | ||
|
|
7621da7041 | ||
|
|
8bcc5104e5 | ||
|
|
4bbeca9f3e | ||
|
|
54748ac20d | ||
|
|
3c8b80574a | ||
|
|
288ad782d2 | ||
|
|
5d97a7edac | ||
|
|
ff1c835cda | ||
|
|
479f61b224 | ||
|
|
ac94b8b6d3 | ||
|
|
fedc7a7fb6 | ||
|
|
9f9df07390 | ||
|
|
e5a705aeae | ||
|
|
fb0f74c4d8 | ||
|
|
963aafd5ab | ||
|
|
efe2f49e7f | ||
|
|
a7178b6da7 | ||
|
|
2da3cd6673 | ||
|
|
5d50f9aec2 | ||
|
|
cfb43ba07c | ||
|
|
3811417d70 | ||
|
|
9b3c77a0e6 | ||
|
|
b5636af3be | ||
|
|
3e93fb5e2e | ||
|
|
c22c92e9f4 | ||
|
|
97a763660a | ||
|
|
8a25ecc635 | ||
|
|
ee52c14b95 | ||
|
|
3178e44843 | ||
|
|
f510dfd8d9 | ||
|
|
7ad13d3eff | ||
|
|
e0c7e8c15b | ||
|
|
5e91effb12 | ||
|
|
9560da90ab | ||
|
|
9e1ed8e9eb | ||
|
|
15592d01d0 | ||
|
|
15d93e6c33 | ||
|
|
484e20ba83 | ||
|
|
d341e977c3 | ||
|
|
e7d7ee396e | ||
|
|
adf585852a | ||
|
|
de5b56235e | ||
|
|
318ef46ae8 | ||
|
|
b982793fbf | ||
|
|
6fef56ebb7 | ||
|
|
fa586c15aa | ||
|
|
51be3db970 | ||
|
|
5d654175a9 | ||
|
|
e9003ad32a | ||
|
|
38c3320731 | ||
|
|
e6b556851a | ||
|
|
a30285ef49 | ||
|
|
4f67f17551 | ||
|
|
8f66009d0d | ||
|
|
cdd12fd4dd | ||
|
|
3ff0018a12 | ||
|
|
0b0f479752 | ||
|
|
b7148b99dc | ||
|
|
8076e58a97 | ||
|
|
251d390c23 | ||
|
|
f380f222a9 | ||
|
|
eacdab45ba | ||
|
|
dcb949e7b3 | ||
|
|
ebf1468652 | ||
|
|
4726b57c47 | ||
|
|
f422336aff | ||
|
|
2c622af49c | ||
|
|
0b684eb05e | ||
|
|
9c4a45bc29 | ||
|
|
00f7551c0a | ||
|
|
ee7b915313 | ||
|
|
af0871cd0a | ||
|
|
60d074a0de | ||
|
|
c2ba2719de | ||
|
|
afb0b9e387 | ||
|
|
e4e4d9600e | ||
|
|
a6964a8f4e | ||
|
|
b538330785 | ||
|
|
aee90268a5 | ||
|
|
cabf849774 | ||
|
|
7251f3308b | ||
|
|
121d3d99fe | ||
|
|
c4938e00e4 | ||
|
|
4a926415db | ||
|
|
7d4c9011aa | ||
|
|
b5b2582335 | ||
|
|
7d2a2b96a2 | ||
|
|
e2fe04a44b | ||
|
|
18c02bf33b | ||
|
|
6c0b1018bc | ||
|
|
15e2b4f595 | ||
|
|
6e7bf11ec8 | ||
|
|
5f9b0890fb | ||
|
|
4841c52bcd | ||
|
|
7baabca827 | ||
|
|
9a19007b85 | ||
|
|
677f850e21 | ||
|
|
d771bc3688 | ||
|
|
1dfc58c120 | ||
|
|
6f58f266a6 | ||
|
|
e38395b6ba | ||
|
|
944d477dbc | ||
|
|
cbf7ac0126 | ||
|
|
1ad171f37b | ||
|
|
06896dacd5 | ||
|
|
ff90d6430d | ||
|
|
4dbdbc4285 | ||
|
|
0f1c8e0658 | ||
|
|
c9ec116c9b | ||
|
|
e61c3f619b | ||
|
|
93b25a5c32 | ||
|
|
f7ca32d888 | ||
|
|
d57c46066a | ||
|
|
1e6cb2cfe4 | ||
|
|
67ae80056a | ||
|
|
13fa3c69e8 | ||
|
|
caad557b2a | ||
|
|
0f030d2c27 | ||
|
|
66d548c9c1 | ||
|
|
c168bb4a2b | ||
|
|
811db2961a | ||
|
|
42cea2ebde | ||
|
|
ccc5eb3c9a | ||
|
|
0e71a4fcb3 | ||
|
|
4ea681984f | ||
|
|
dadc5b96c6 | ||
|
|
787fca138b | ||
|
|
b820615081 | ||
|
|
812a6f5ec2 | ||
|
|
f1b42ec699 | ||
|
|
26c17ea19c | ||
|
|
acc75f80bf | ||
|
|
a79a65363f | ||
|
|
dfeb4804e7 | ||
|
|
9d20fafd06 | ||
|
|
e9a5ef0058 | ||
|
|
d2378376e4 | ||
|
|
4b1b5d8a18 | ||
|
|
a0770e80e7 | ||
|
|
f08e740ac6 | ||
|
|
5f23bcfad6 | ||
|
|
3cd7b86c99 | ||
|
|
bd53d4a271 | ||
|
|
16d88730ec | ||
|
|
5ad88eb6bd | ||
|
|
b617eb3140 | ||
|
|
acc4d3ad84 | ||
|
|
beb3f7e2b8 | ||
|
|
ab46a8dd96 | ||
|
|
b6b578d7e6 | ||
|
|
4d07cf80fa | ||
|
|
93f6b65d8d | ||
|
|
b40a6723e1 | ||
|
|
2058d28b37 | ||
|
|
682c6d6f5e | ||
|
|
2be55acc92 | ||
|
|
a9a040182c | ||
|
|
f39fe94173 | ||
|
|
976bdbbd96 | ||
|
|
cff1d8bfb9 | ||
|
|
bbdc54cb69 | ||
|
|
36f1f7b71a | ||
|
|
e1731793dd | ||
|
|
8ea3b27612 | ||
|
|
9469a69078 | ||
|
|
cc46fe3336 | ||
|
|
52b4064a2d | ||
|
|
d367b9ef21 | ||
|
|
4711e4669f | ||
|
|
3e55024f98 | ||
|
|
ccf56bf944 | ||
|
|
42e51a9cf6 | ||
|
|
667e0cc0e3 | ||
|
|
339830b3bb | ||
|
|
7770164672 | ||
|
|
f333607572 | ||
|
|
87d3764eeb | ||
|
|
faf487b719 | ||
|
|
3dbf0df19e | ||
|
|
cd272a6443 | ||
|
|
936a8778ed |
@@ -42,3 +42,4 @@ exclude_paths:
|
||||
- spec/
|
||||
- public/
|
||||
- app/assets/stylesheets/bootstrap-accessibility.css
|
||||
- app/assets/javascripts/bootstrap*
|
||||
|
||||
1
.esignore
Normal file
1
.esignore
Normal file
@@ -0,0 +1 @@
|
||||
app/assets/javascripts/bootstrap-accessibility.min.js
|
||||
11
.eslintrc.json
Normal file
11
.eslintrc.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "google",
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"no-var": "off",
|
||||
"max-len": ["error", { "code": 120, "tabWidth": 4 }]
|
||||
}
|
||||
}
|
||||
13
.hound.yml
Normal file
13
.hound.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
fail_on_violations: true
|
||||
ruby:
|
||||
config_file: .rubocop.yml
|
||||
haml:
|
||||
config_file: .haml-lint.yml
|
||||
scss:
|
||||
config_file: .scss-lint.yml
|
||||
eslint:
|
||||
config_file: .eslintrc
|
||||
ignore_file: .esignore
|
||||
jshint:
|
||||
enabled: false
|
||||
@@ -31,6 +31,7 @@ PreCommit:
|
||||
enabled: true
|
||||
on_fail: warn
|
||||
command: ['npm', 'run', 'coffeelint']
|
||||
required_executable: 'npm'
|
||||
HardTabs:
|
||||
enabled: true
|
||||
AuthorEmail:
|
||||
@@ -42,11 +43,12 @@ PreCommit:
|
||||
exclude:
|
||||
- '**/bootstrap.min.css'
|
||||
command: ['npm', 'run', 'csslint']
|
||||
required_executable: 'npm'
|
||||
HamlLint:
|
||||
enabled: true
|
||||
requires_files: true
|
||||
on_warn: fail
|
||||
command: ['bundle', 'exec', 'haml-lint', 'app/views']
|
||||
include:
|
||||
- 'app/views/**'
|
||||
command: ['bundle', 'exec', 'haml-lint']
|
||||
JsonSyntax:
|
||||
enabled: true
|
||||
BundleOutdated:
|
||||
@@ -55,13 +57,14 @@ PreCommit:
|
||||
BundleAudit:
|
||||
enabled: true
|
||||
on_warn: warn
|
||||
JsHint:
|
||||
EsLint:
|
||||
enabled: true
|
||||
exclude:
|
||||
- 'app/assets/**'
|
||||
- 'spec/javascripts/support/vendor/**'
|
||||
- '**/bootstrap*'
|
||||
command: ['npm', 'run', 'jshint']
|
||||
command: ['./node_modules/.bin/eslint']
|
||||
required_executable: 'npm'
|
||||
ScssLint:
|
||||
enabled: true
|
||||
RailsSchemaUpToDate:
|
||||
@@ -71,11 +74,6 @@ PreCommit:
|
||||
YamlLint:
|
||||
enabled: true
|
||||
|
||||
PostCommit:
|
||||
GitGuilt:
|
||||
enabled: true
|
||||
command: ['npm', 'run', 'git-guilt']
|
||||
|
||||
PostCheckout:
|
||||
ALL:
|
||||
quiet: false
|
||||
|
||||
22
.rubocop.yml
22
.rubocop.yml
@@ -7,32 +7,40 @@ AllCops:
|
||||
Exclude:
|
||||
- 'db/schema.rb'
|
||||
- 'vendor/**/*'
|
||||
TargetRailsVersion: 4.0
|
||||
|
||||
Rails:
|
||||
Enabled: true
|
||||
|
||||
Style/FileName:
|
||||
Exclude:
|
||||
- 'Guardfile'
|
||||
- 'Gemfile'
|
||||
- 'Gemfile.lock'
|
||||
|
||||
Style/StringLiterals:
|
||||
Enabled: false
|
||||
|
||||
Style/MultilineMethodCallIndentation:
|
||||
# Stop hound and codeclimate fighting
|
||||
Style/PercentLiteralDelimiters:
|
||||
PreferredDelimiters:
|
||||
default: ()
|
||||
'%i': ()
|
||||
'%w': ()
|
||||
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
EnforcedStyle: indented
|
||||
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: with_first_parameter, with_fixed_indentation
|
||||
Style/AlignParameters:
|
||||
Layout/AlignParameters:
|
||||
EnforcedStyle: with_fixed_indentation
|
||||
|
||||
Metrics/LineLength:
|
||||
Max: 120
|
||||
|
||||
|
||||
# See https://github.com/bbatsov/rubocop/issues/3629
|
||||
Rails/HttpPositionalArguments:
|
||||
# turn these back on in Rails 5
|
||||
Rails/HttpPositionalArguments: # See https://github.com/bbatsov/rubocop/issues/3629
|
||||
Enabled: false
|
||||
|
||||
Style/Documentation:
|
||||
@@ -58,9 +66,9 @@ Metrics/BlockLength:
|
||||
Metrics/MethodLength:
|
||||
Max: 34
|
||||
Metrics/AbcSize:
|
||||
Max: 32
|
||||
Max: 31
|
||||
Metrics/ClassLength:
|
||||
Max: 207
|
||||
Max: 179
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 11
|
||||
Metrics/PerceivedComplexity:
|
||||
|
||||
@@ -1,36 +1,15 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --no-offense-counts`
|
||||
# on 2017-03-01 11:18:11 +1300 using RuboCop version 0.47.1.
|
||||
# on 2018-02-05 14:37:22 +1300 using RuboCop version 0.49.1.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
Lint/AmbiguousRegexpLiteral:
|
||||
Exclude:
|
||||
- 'spec/controllers/admin/orders_controller_spec.rb'
|
||||
- 'spec/controllers/orders_controller_spec.rb'
|
||||
- 'spec/features/cms_spec.rb'
|
||||
- 'spec/lib/haml/filters/escaped_markdown_spec.rb'
|
||||
- 'spec/lib/haml/filters/growstuff_markdown_spec.rb'
|
||||
- 'spec/models/comment_spec.rb'
|
||||
- 'spec/models/planting_spec.rb'
|
||||
- 'spec/views/members/show.rss.haml_spec.rb'
|
||||
- 'spec/views/posts/show.html.haml_spec.rb'
|
||||
|
||||
Lint/HandleExceptions:
|
||||
Exclude:
|
||||
- 'lib/tasks/testing.rake'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
|
||||
Lint/UnusedBlockArgument:
|
||||
Exclude:
|
||||
- 'app/controllers/crops_controller.rb'
|
||||
- 'app/controllers/sessions_controller.rb'
|
||||
- 'config/unicorn.rb'
|
||||
- 'lib/haml/filters/growstuff_markdown.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
|
||||
Lint/UnusedMethodArgument:
|
||||
@@ -39,18 +18,6 @@ Lint/UnusedMethodArgument:
|
||||
- 'app/controllers/passwords_controller.rb'
|
||||
- 'app/controllers/registrations_controller.rb'
|
||||
- 'app/validators/approved_validator.rb'
|
||||
- 'spec/views/plantings/show.html.haml_spec.rb'
|
||||
|
||||
Lint/Void:
|
||||
Exclude:
|
||||
- 'spec/models/crop_spec.rb'
|
||||
- 'spec/models/garden_spec.rb'
|
||||
- 'spec/models/post_spec.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Performance/StringReplacement:
|
||||
Exclude:
|
||||
- 'spec/rails_helper.rb'
|
||||
|
||||
Rails/FilePath:
|
||||
Exclude:
|
||||
@@ -63,23 +30,18 @@ Rails/OutputSafety:
|
||||
- 'app/helpers/gardens_helper.rb'
|
||||
|
||||
# Configuration parameters: Blacklist.
|
||||
# Blacklist: decrement!, decrement_counter, increment!, increment_counter
|
||||
# toggle!, touch, update_all, update_attribute, update_column, update_columns,
|
||||
# update_counters
|
||||
# Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters
|
||||
Rails/SkipsModelValidations:
|
||||
Exclude:
|
||||
- 'app/controllers/plantings_controller.rb'
|
||||
- 'db/seeds.rb'
|
||||
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: strict, flexible
|
||||
Rails/TimeZone:
|
||||
Exclude:
|
||||
- 'spec/controllers/accounts_controller_spec.rb'
|
||||
- 'spec/factories/member.rb'
|
||||
- 'spec/factories/post.rb'
|
||||
- 'spec/models/post_spec.rb'
|
||||
- 'spec/views/plantings/index.html.haml_spec.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
@@ -93,160 +55,22 @@ Style/AsciiComments:
|
||||
Exclude:
|
||||
- 'config/initializers/comfortable_mexican_sofa.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: percent_q, bare_percent
|
||||
Style/BarePercentLiterals:
|
||||
Exclude:
|
||||
- 'app/helpers/auto_suggest_helper.rb'
|
||||
- 'spec/support/feature_helpers.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
|
||||
# SupportedStyles: line_count_based, semantic, braces_for_chaining
|
||||
# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
|
||||
# FunctionalMethods: let, let!, subject, watch
|
||||
# IgnoredMethods: lambda, proc, it
|
||||
Style/BlockDelimiters:
|
||||
Exclude:
|
||||
- 'app/controllers/members_controller.rb'
|
||||
- 'spec/controllers/order_items_controller_spec.rb'
|
||||
- 'spec/features/notifications_spec.rb'
|
||||
- 'spec/models/ability_spec.rb'
|
||||
- 'spec/models/comment_spec.rb'
|
||||
- 'spec/models/follow_spec.rb'
|
||||
- 'spec/models/member_spec.rb'
|
||||
- 'spec/models/planting_spec.rb'
|
||||
- 'spec/models/post_spec.rb'
|
||||
- 'spec/views/crops/edit.html.haml_spec.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/BlockEndNewline:
|
||||
Exclude:
|
||||
- 'app/controllers/members_controller.rb'
|
||||
- 'spec/models/ability_spec.rb'
|
||||
- 'spec/models/member_spec.rb'
|
||||
- 'spec/models/planting_spec.rb'
|
||||
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: nested, compact
|
||||
Style/ClassAndModuleChildren:
|
||||
Exclude:
|
||||
- 'app/controllers/admin/orders_controller.rb'
|
||||
- 'lib/actions/oauth_signup_action.rb'
|
||||
- 'lib/haml/filters/escaped_markdown.rb'
|
||||
- 'lib/haml/filters/growstuff_markdown.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/ClassMethods:
|
||||
Exclude:
|
||||
- 'app/models/planting.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/ColonMethodCall:
|
||||
Exclude:
|
||||
- 'spec/lib/haml/filters/escaped_markdown_spec.rb'
|
||||
- 'spec/lib/haml/filters/growstuff_markdown_spec.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: Keywords.
|
||||
# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW
|
||||
Style/CommentAnnotation:
|
||||
Exclude:
|
||||
- 'app/controllers/crops_controller.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/EachForSimpleLoop:
|
||||
Exclude:
|
||||
- 'spec/models/crop_spec.rb'
|
||||
- 'spec/views/home/_crops.html.haml_spec.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: compact, expanded
|
||||
Style/EmptyMethod:
|
||||
Exclude:
|
||||
- 'app/controllers/accounts_controller.rb'
|
||||
- 'app/controllers/photos_controller.rb'
|
||||
- 'app/controllers/plant_parts_controller.rb'
|
||||
- 'app/controllers/posts_controller.rb'
|
||||
- 'app/controllers/products_controller.rb'
|
||||
- 'app/controllers/roles_controller.rb'
|
||||
- 'app/controllers/scientific_names_controller.rb'
|
||||
- 'app/controllers/seeds_controller.rb'
|
||||
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: format, sprintf, percent
|
||||
Style/FormatString:
|
||||
Exclude:
|
||||
- 'app/helpers/application_helper.rb'
|
||||
- 'spec/helpers/application_helper_spec.rb'
|
||||
- 'spec/views/shop/index_spec.rb'
|
||||
|
||||
Style/IdenticalConditionalBranches:
|
||||
Exclude:
|
||||
- 'app/controllers/follows_controller.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: MaxLineLength.
|
||||
Style/IfUnlessModifier:
|
||||
Exclude:
|
||||
- 'app/controllers/shop_controller.rb'
|
||||
- 'app/helpers/crops_helper.rb'
|
||||
- 'app/models/planting.rb'
|
||||
- 'config/initializers/geocoder.rb'
|
||||
- 'lib/tasks/growstuff.rake'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: line_count_dependent, lambda, literal
|
||||
Style/Lambda:
|
||||
Exclude:
|
||||
- 'spec/controllers/member_controller_spec.rb'
|
||||
- 'spec/models/photo_spec.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/MultilineIfModifier:
|
||||
Exclude:
|
||||
- 'spec/rails_helper.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/MultilineIfThen:
|
||||
Exclude:
|
||||
- 'script/check_contributors_md'
|
||||
|
||||
Style/MultilineTernaryOperator:
|
||||
Exclude:
|
||||
- 'app/controllers/notifications_controller.rb'
|
||||
- 'app/controllers/order_items_controller.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/MutableConstant:
|
||||
Exclude:
|
||||
- 'app/controllers/members_controller.rb'
|
||||
- 'app/models/planting.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/NegatedIf:
|
||||
Exclude:
|
||||
- 'app/helpers/crops_helper.rb'
|
||||
|
||||
Style/NestedTernaryOperator:
|
||||
Exclude:
|
||||
- 'app/controllers/plantings_controller.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
||||
# SupportedStyles: skip_modifier_ifs, always
|
||||
Style/Next:
|
||||
Exclude:
|
||||
- 'lib/tasks/growstuff.rake'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/NilComparison:
|
||||
Exclude:
|
||||
- 'lib/tasks/growstuff.rake'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: predicate, comparison
|
||||
@@ -256,26 +80,6 @@ Style/NumericPredicate:
|
||||
- 'app/helpers/harvests_helper.rb'
|
||||
- 'app/helpers/plantings_helper.rb'
|
||||
- 'lib/tasks/growstuff.rake'
|
||||
- 'script/check_contributors_md'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/ParallelAssignment:
|
||||
Exclude:
|
||||
- 'app/mailers/notifier.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: PreferredDelimiters.
|
||||
Style/PercentLiteralDelimiters:
|
||||
Exclude:
|
||||
- 'app/helpers/auto_suggest_helper.rb'
|
||||
- 'script/check_contributors_md'
|
||||
- 'spec/features/signin_spec.rb'
|
||||
- 'spec/features/signout_spec.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/PerlBackrefs:
|
||||
Exclude:
|
||||
- 'lib/haml/filters/growstuff_markdown.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
|
||||
@@ -287,30 +91,3 @@ Style/RegexpLiteral:
|
||||
- 'spec/views/devise/registrations/edit_spec.rb'
|
||||
- 'spec/views/members/index.html.haml_spec.rb'
|
||||
- 'spec/views/posts/index.html.haml_spec.rb'
|
||||
- 'spec/views/posts/show.html.haml_spec.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: SupportedStyles.
|
||||
# SupportedStyles: use_perl_names, use_english_names
|
||||
Style/SpecialGlobalVars:
|
||||
EnforcedStyle: use_perl_names
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: IgnoredMethods.
|
||||
# IgnoredMethods: respond_to, define_method
|
||||
Style/SymbolProc:
|
||||
Exclude:
|
||||
- 'app/controllers/crops_controller.rb'
|
||||
- 'lib/tasks/growstuff.rake'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
Style/UnlessElse:
|
||||
Exclude:
|
||||
- 'app/controllers/omniauth_callbacks_controller.rb'
|
||||
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: SupportedStyles, WordRegex.
|
||||
# SupportedStyles: percent, brackets
|
||||
Style/WordArray:
|
||||
EnforcedStyle: percent
|
||||
MinSize: 5
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.3.4
|
||||
2.4.1
|
||||
|
||||
20
.travis.yml
20
.travis.yml
@@ -1,4 +1,4 @@
|
||||
sudo: false
|
||||
sudo: required
|
||||
language: ruby
|
||||
cache:
|
||||
bundler: true
|
||||
@@ -14,18 +14,26 @@ env:
|
||||
- GROWSTUFF_SITE_NAME="Growstuff (travis)"
|
||||
- RAILS_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
||||
- secure: "Z5TpM2jEX4UCvNePnk/LwltQX48U2u9BRc+Iypr1x9QW2o228QJhPIOH39a8RMUrepGnkQIq9q3ZRUn98RfrJz1yThtlNFL3NmzdQ57gKgjGwfpa0e4Dwj/ZJqV2D84tDGjvdVYLP7zzaYZxQcwk/cgNpzKf/jq97HLNP7CYuf4="
|
||||
rvm:
|
||||
- 2.3.4
|
||||
before_install:
|
||||
- ./script/install_phantomjs;
|
||||
- export PATH=$PWD/travis_phantomjs/phantomjs-2.1.1-linux-x86_64/bin:$PATH
|
||||
# Force Travis to use Elastic Search 2.4.0
|
||||
- >
|
||||
if [ "${GROWSTUFF_ELASTICSEARCH}" = "true" ]; then
|
||||
sudo dpkg -r elasticsearch;
|
||||
curl -O https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/2.4.0/elasticsearch-2.4.0.deb;
|
||||
sudo dpkg -i --force-confnew elasticsearch-2.4.0.deb;
|
||||
sudo service elasticsearch start;
|
||||
sleep 10;
|
||||
curl localhost:9200;
|
||||
fi
|
||||
before_script:
|
||||
- bundle exec rake db:create db:migrate db:test:prepare
|
||||
- set -e
|
||||
- >
|
||||
if [ "${STATIC_CHECKS}" = "true" ]; then
|
||||
./script/install_linters;
|
||||
else
|
||||
RAILS_ENV=test bundle exec rake db:create db:migrate;
|
||||
bundle exec rake assets:precompile;
|
||||
fi
|
||||
- set +e
|
||||
@@ -40,9 +48,6 @@ script:
|
||||
bundle exec rake jasmine:ci;
|
||||
fi;
|
||||
- set +e
|
||||
|
||||
services:
|
||||
- elasticsearch
|
||||
before_deploy:
|
||||
- bundle exec script/heroku_maintenance.rb on
|
||||
deploy:
|
||||
@@ -51,6 +56,7 @@ deploy:
|
||||
secure: "WrQxf0fEKkCdXrjcejurobOnNNz3he4dDwjBbToXbQTQNDObPp7NetJrLsfM8FiUFEeOuvhIHHiDQtMvY720zGGAGxDptvgFS+0QHCUqoTRZA/yFfUmHlG2jROXTzk5uVK0AE4k6Ion5kX8+mM0EnMT/7u+MTFiukrJctSiEXfg="
|
||||
on:
|
||||
repo: Growstuff/growstuff
|
||||
condition: "$RSPEC_TAG = elasticsearch"
|
||||
app:
|
||||
dev: growstuff-staging
|
||||
master: growstuff-prod
|
||||
|
||||
@@ -8,14 +8,15 @@ submit the change with your pull request.
|
||||
|
||||
## Committers
|
||||
|
||||
- Alex Bayley / [Skud](https://github.com/Skud)
|
||||
- Cesy / [cesy](https://github.com/cesy)
|
||||
- Miles Gould / [pozorvlak](https://github.com/pozorvlak)
|
||||
- Taylor Griffin / [tygriffin](https://github.com/tygriffin)
|
||||
- Mackenzie Morgan / [maco](https://github.com/maco)
|
||||
- Brenda Wallace / [br3nda](https://github.com/br3nda)
|
||||
|
||||
## Contributors
|
||||
|
||||
- Alex Bayley / [Skud](https://github.com/Skud)
|
||||
- Taylor Griffin / [tygriffin](https://github.com/tygriffin)
|
||||
- Joseph Caudle / [jcaudle](https://github.com/jcaudle)
|
||||
- Ricky Amianym / [amianym](https://github.com/amianym)
|
||||
- Juliet Kemp / [julietk](https://github.com/julietk)
|
||||
@@ -73,13 +74,15 @@ submit the change with your pull request.
|
||||
- Lucas Nogueira / [lucasnogueira](https://github.com/lucasnogueira)
|
||||
- Charley Lewittes / [ctlewitt](https://github.com/ctlewitt)
|
||||
- Kristine Nicole Polvoriza / [polveenomials](https://github.com/polveenomials)
|
||||
- Brenda Wallace / [br3nda](https://github.com/br3nda)
|
||||
- Jim Stallings / [jestallin](https://github.com/jestallin)
|
||||
- Alyssa Ransbury / [alran](https://github.com/alran)
|
||||
- Thomas Countz / [thomascountz](https://github.com/thomascountz)
|
||||
- Megan Talbot / [meganft](https://github.com/meganft)
|
||||
- Arun Kumar / [arun1595](https://github.com/arun1595)
|
||||
- Harry Brodsky / [hbrodsk1](https://github.com/hbrodsk1)
|
||||
- Jeff Kingswood / [ancyentmariner](https://github.com/ancyentmariner)
|
||||
- Logan Gingerich / [logangingerich](https://github.com/logangingerich)
|
||||
- Mark Taffman / [mftaff](https://github.com/mftaff)
|
||||
|
||||
## Bots
|
||||
|
||||
|
||||
72
Gemfile
72
Gemfile
@@ -1,43 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby '2.3.4'
|
||||
ruby '2.4.1'
|
||||
|
||||
gem 'rails', '~> 4.2.7'
|
||||
gem 'rails', '~> 4.2.8'
|
||||
|
||||
gem 'bundler', '>=1.1.5'
|
||||
|
||||
gem 'coffee-rails', '~> 4.1.0'
|
||||
gem 'coffee-rails'
|
||||
gem 'haml'
|
||||
gem 'sass-rails', '~> 5.0.4'
|
||||
gem 'sass-rails'
|
||||
|
||||
# API data
|
||||
gem 'jsonapi-resources'
|
||||
|
||||
# CSS framework
|
||||
gem 'bootstrap-sass', '~> 3.3.6'
|
||||
gem 'bootstrap-sass'
|
||||
gem 'font-awesome-sass'
|
||||
|
||||
gem 'uglifier', '~> 2.7.2' # JavaScript compressor
|
||||
gem 'uglifier' # JavaScript compressor
|
||||
|
||||
# planting and harvest predictions
|
||||
gem 'active_median'
|
||||
|
||||
gem 'flickraw'
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-ui-rails', '~> 5.0.2'
|
||||
gem 'jquery-ui-rails', '~> 5.0.2' # needs careful upgrade with change of location
|
||||
gem 'js-routes' # provides access to Rails routes in Javascript
|
||||
|
||||
gem 'cancancan', '~> 1.9' # for checking member privileges
|
||||
gem 'cancancan' # for checking member privileges
|
||||
gem 'csv_shaper' # CSV export
|
||||
gem 'figaro' # for handling config via ENV variables
|
||||
gem 'gibbon', '~>1.2.0' # for Mailchimp newsletter subscriptions
|
||||
gem 'leaflet-markercluster-rails'
|
||||
gem 'leaflet-rails', '~> 0.7.7' # Newer versions break tests - see https://travis-ci.org/CloCkWeRX/growstuff/builds/200984350
|
||||
gem 'pg'
|
||||
|
||||
# Maps
|
||||
gem 'leaflet-rails'
|
||||
gem 'rails-assets-leaflet.markercluster', source: 'https://rails-assets.org'
|
||||
|
||||
gem 'pg', '< 1.0.0' # Upstream bug, see https://github.com/Growstuff/growstuff/pull/1539
|
||||
gem 'ruby-units' # for unit conversion
|
||||
gem 'unicorn' # http server
|
||||
|
||||
gem 'comfortable_mexican_sofa', '~> 1.12.0' # content management system
|
||||
gem 'comfortable_mexican_sofa' # content management system
|
||||
|
||||
gem 'bootstrap-kaminari-views' # bootstrap views for kaminari
|
||||
gem 'kaminari', '~> 0.17.0' # pagination
|
||||
gem 'kaminari' # pagination
|
||||
|
||||
gem 'activemerchant'
|
||||
gem 'active_utils'
|
||||
gem 'sidekiq'
|
||||
|
||||
@@ -45,13 +54,13 @@ gem 'sidekiq'
|
||||
gem 'bluecloth'
|
||||
|
||||
# Pagination
|
||||
gem 'will_paginate', '~> 3.0'
|
||||
gem 'will_paginate'
|
||||
|
||||
# user signup/login/etc
|
||||
gem 'devise', '>= 4.0.0'
|
||||
gem 'devise'
|
||||
|
||||
# nicely formatted URLs
|
||||
gem 'friendly_id', '~> 5.0.4'
|
||||
gem 'friendly_id'
|
||||
|
||||
# gravatars
|
||||
gem 'gravatar-ultimate'
|
||||
@@ -66,10 +75,9 @@ gem 'bootstrap-datepicker-rails'
|
||||
gem 'omniauth', '~> 1.3'
|
||||
gem 'omniauth-facebook'
|
||||
gem 'omniauth-flickr', '>= 0.0.15'
|
||||
gem 'omniauth-twitter', '~> 1.2'
|
||||
gem 'omniauth-twitter'
|
||||
|
||||
# For charting data
|
||||
gem 'd3-rails', '~> 3.5' # 4.* produces Error: <spyOn> : could not find an object to spy upon for linear() - see https://travis-ci.org/Growstuff/growstuff/jobs/204461482
|
||||
gem "chartkick"
|
||||
|
||||
# client for Elasticsearch. Elasticsearch is a flexible
|
||||
# and powerful, distributed, real-time search and analytics engine.
|
||||
@@ -85,8 +93,13 @@ gem "hashie", ">= 3.5.3"
|
||||
|
||||
gem 'rake', '>= 10.0.0'
|
||||
|
||||
# # CMS
|
||||
# gem 'comfortable_mexican_sofa', '~> 1.12.0'
|
||||
# locale based flash notices for controllers
|
||||
gem "responders"
|
||||
|
||||
# allows soft delete. Used for members.
|
||||
gem 'acts_as_paranoid', '~> 0.5.0'
|
||||
|
||||
gem 'xmlrpc' # fixes rake error - can be removed if not needed later
|
||||
|
||||
group :production, :staging do
|
||||
gem 'bonsai-elasticsearch-rails' # Integration with Bonsa-Elasticsearch on heroku
|
||||
@@ -101,7 +114,7 @@ group :development do
|
||||
# A debugger and irb alternative. Pry doesn't play nice
|
||||
# with unicorn, so start a Webrick server when debugging
|
||||
# with Pry
|
||||
gem 'better_errors'
|
||||
gem 'better_errors', '~> 2.2.0'
|
||||
gem 'binding_of_caller'
|
||||
gem 'guard'
|
||||
gem 'guard-rspec'
|
||||
@@ -111,15 +124,15 @@ group :development do
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
gem "active_merchant-paypal-bogus-gateway"
|
||||
gem 'bullet' # performance tuning by finding unnecesary queries
|
||||
gem 'byebug' # debugging
|
||||
gem 'capybara' # integration tests
|
||||
gem 'capybara-email' # integration tests for email
|
||||
gem 'capybara-screenshot' # for test debugging
|
||||
gem 'coveralls', require: false # coverage analysis
|
||||
gem 'database_cleaner', '~> 1.5.0'
|
||||
gem 'factory_girl_rails' # for creating test data
|
||||
gem 'database_cleaner'
|
||||
gem 'factory_bot_rails' # for creating test data
|
||||
gem 'faker'
|
||||
gem 'haml-i18n-extractor'
|
||||
gem 'haml-rails' # HTML templating language
|
||||
gem 'haml_lint' # Checks haml files for goodness
|
||||
@@ -129,15 +142,18 @@ group :development, :test do
|
||||
gem 'rainbow', '< 2.2.0' # See https://github.com/sickill/rainbow/issues/44
|
||||
gem 'rspec-activemodel-mocks'
|
||||
gem 'rspec-rails' # unit testing framework
|
||||
gem 'rubocop', '<= 0.47.1', require: false # Pin to rubocop (0.47.1) as 0.48.0 is buggy
|
||||
gem 'rubocop'
|
||||
gem 'selenium-webdriver'
|
||||
gem 'webrat' # provides HTML matchers for view tests
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'codeclimate-test-reporter', require: false
|
||||
gem 'timecop'
|
||||
end
|
||||
|
||||
group :travis do
|
||||
gem 'heroku-api'
|
||||
gem 'platform-api'
|
||||
end
|
||||
gem 'loofah', '>= 2.2.1'
|
||||
gem 'rack-protection', '>= 2.0.1'
|
||||
|
||||
476
Gemfile.lock
476
Gemfile.lock
@@ -1,71 +1,70 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
remote: https://rails-assets.org/
|
||||
specs:
|
||||
actionmailer (4.2.8)
|
||||
actionpack (= 4.2.8)
|
||||
actionview (= 4.2.8)
|
||||
activejob (= 4.2.8)
|
||||
actionmailer (4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activejob (= 4.2.10)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
actionpack (4.2.8)
|
||||
actionview (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
actionpack (4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
actionview (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
active_link_to (1.0.4)
|
||||
active_link_to (1.0.5)
|
||||
actionpack
|
||||
addressable
|
||||
active_merchant-paypal-bogus-gateway (0.1.0)
|
||||
activemerchant
|
||||
active_utils (3.3.3)
|
||||
active_median (0.1.4)
|
||||
activerecord
|
||||
active_utils (3.3.9)
|
||||
activesupport (>= 3.2, < 5.2.0)
|
||||
i18n
|
||||
activejob (4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
activejob (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
globalid (>= 0.3.0)
|
||||
activemerchant (1.64.0)
|
||||
activesupport (>= 3.2.14, < 5.1)
|
||||
builder (>= 2.1.2, < 4.0.0)
|
||||
i18n (>= 0.6.9)
|
||||
nokogiri (~> 1.4)
|
||||
activemodel (4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
activemodel (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.8)
|
||||
activemodel (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
activerecord (4.2.10)
|
||||
activemodel (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
arel (~> 6.0)
|
||||
activesupport (4.2.8)
|
||||
activesupport (4.2.10)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.1)
|
||||
public_suffix (~> 2.0, >= 2.0.2)
|
||||
acts_as_paranoid (0.5.0)
|
||||
activerecord (>= 4.0, < 5.1)
|
||||
activesupport (>= 4.0, < 5.1)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
arel (6.0.4)
|
||||
ast (2.3.0)
|
||||
autoprefixer-rails (6.7.7.2)
|
||||
ast (2.4.0)
|
||||
autoprefixer-rails (7.2.5)
|
||||
execjs
|
||||
bcrypt (3.1.11)
|
||||
better_errors (2.1.1)
|
||||
better_errors (2.2.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
binding_of_caller (0.8.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bluecloth (2.2.0)
|
||||
bonsai-elasticsearch-rails (0.2.0)
|
||||
elasticsearch-model (~> 0)
|
||||
elasticsearch-rails (~> 0)
|
||||
bootstrap-datepicker-rails (1.6.4.1)
|
||||
bootstrap-datepicker-rails (1.7.1.1)
|
||||
railties (>= 3.0)
|
||||
bootstrap-kaminari-views (0.0.5)
|
||||
kaminari (>= 0.13)
|
||||
@@ -73,29 +72,30 @@ GEM
|
||||
bootstrap-sass (3.3.7)
|
||||
autoprefixer-rails (>= 5.2.1)
|
||||
sass (>= 3.3.4)
|
||||
bootstrap_form (2.6.0)
|
||||
bootstrap_form (2.7.0)
|
||||
builder (3.2.3)
|
||||
bullet (5.5.1)
|
||||
bullet (5.7.2)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.10.0)
|
||||
byebug (9.0.6)
|
||||
cancancan (1.16.0)
|
||||
capybara (2.13.0)
|
||||
uniform_notifier (~> 1.11.0)
|
||||
byebug (10.0.0)
|
||||
cancancan (2.1.3)
|
||||
capybara (2.17.0)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
xpath (>= 2.0, < 4.0)
|
||||
capybara-email (2.5.0)
|
||||
capybara (~> 2.4)
|
||||
mail
|
||||
capybara-screenshot (1.0.14)
|
||||
capybara-screenshot (1.0.18)
|
||||
capybara (>= 1.0, < 3)
|
||||
launchy
|
||||
childprocess (0.6.3)
|
||||
chartkick (2.2.5)
|
||||
childprocess (0.8.0)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
climate_control (0.1.0)
|
||||
climate_control (0.2.0)
|
||||
cliver (0.3.2)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
@@ -103,10 +103,10 @@ GEM
|
||||
simplecov (<= 0.13)
|
||||
codemirror-rails (5.16.0)
|
||||
railties (>= 3.0, < 6.0)
|
||||
coderay (1.1.1)
|
||||
coffee-rails (4.1.1)
|
||||
coderay (1.1.2)
|
||||
coffee-rails (4.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0, < 5.1.x)
|
||||
railties (>= 4.0.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
@@ -134,23 +134,21 @@ GEM
|
||||
term-ansicolor (~> 1.3)
|
||||
thor (~> 0.19.1)
|
||||
tins (~> 1.6)
|
||||
crass (1.0.3)
|
||||
csv_shaper (1.3.0)
|
||||
activesupport (>= 3.0.0)
|
||||
d3-rails (3.5.17)
|
||||
railties (>= 3.1)
|
||||
dalli (2.7.6)
|
||||
database_cleaner (1.5.3)
|
||||
debug_inspector (0.0.2)
|
||||
devise (4.2.1)
|
||||
database_cleaner (1.6.2)
|
||||
debug_inspector (0.0.3)
|
||||
devise (4.4.1)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0, < 5.1)
|
||||
railties (>= 4.1.0, < 5.2)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
diff-lcs (1.3)
|
||||
docile (1.1.5)
|
||||
easy_translate (0.5.0)
|
||||
json
|
||||
easy_translate (0.5.1)
|
||||
thread
|
||||
thread_safe
|
||||
elasticsearch (2.0.2)
|
||||
@@ -167,37 +165,39 @@ GEM
|
||||
faraday
|
||||
multi_json
|
||||
erubis (2.7.0)
|
||||
excon (0.55.0)
|
||||
excon (0.60.0)
|
||||
execjs (2.7.0)
|
||||
factory_girl (4.8.0)
|
||||
factory_bot (4.8.2)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.8.0)
|
||||
factory_girl (~> 4.8.0)
|
||||
factory_bot_rails (4.8.2)
|
||||
factory_bot (~> 4.8.2)
|
||||
railties (>= 3.0.0)
|
||||
faraday (0.11.0)
|
||||
faker (1.8.7)
|
||||
i18n (>= 0.7)
|
||||
faraday (0.12.2)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.18)
|
||||
ffi (1.9.21)
|
||||
figaro (1.1.1)
|
||||
thor (~> 0.14)
|
||||
flickraw (0.9.9)
|
||||
font-awesome-sass (4.7.0)
|
||||
sass (>= 3.2)
|
||||
formatador (0.2.5)
|
||||
friendly_id (5.0.5)
|
||||
friendly_id (5.2.3)
|
||||
activerecord (>= 4.0.0)
|
||||
geocoder (1.4.3)
|
||||
geocoder (1.4.5)
|
||||
gibbon (1.2.1)
|
||||
httparty
|
||||
multi_json (>= 1.9.0)
|
||||
globalid (0.4.0)
|
||||
globalid (0.4.1)
|
||||
activesupport (>= 4.2.0)
|
||||
gravatar-ultimate (2.0.0)
|
||||
activesupport (>= 2.3.14)
|
||||
rack
|
||||
guard (2.14.1)
|
||||
guard (2.14.2)
|
||||
formatador (>= 0.2.4)
|
||||
listen (>= 2.7, < 4.0)
|
||||
lumberjack (~> 1.0)
|
||||
lumberjack (>= 1.0.12, < 2.0)
|
||||
nenv (~> 0.1)
|
||||
notiffany (~> 0.0)
|
||||
pry (>= 0.9.12)
|
||||
@@ -208,7 +208,8 @@ GEM
|
||||
guard (~> 2.1)
|
||||
guard-compat (~> 1.1)
|
||||
rspec (>= 2.99.0, < 4.0)
|
||||
haml (4.0.7)
|
||||
haml (5.0.4)
|
||||
temple (>= 0.8.0)
|
||||
tilt
|
||||
haml-i18n-extractor (0.5.9)
|
||||
activesupport
|
||||
@@ -216,31 +217,34 @@ GEM
|
||||
highline
|
||||
tilt
|
||||
trollop (= 1.16.2)
|
||||
haml-rails (0.9.0)
|
||||
haml-rails (1.0.0)
|
||||
actionpack (>= 4.0.1)
|
||||
activesupport (>= 4.0.1)
|
||||
haml (>= 4.0.6, < 5.0)
|
||||
haml (>= 4.0.6, < 6.0)
|
||||
html2haml (>= 1.0.1)
|
||||
railties (>= 4.0.1)
|
||||
haml_lint (0.24.0)
|
||||
haml_lint (0.26.0)
|
||||
haml (>= 4.0, < 5.1)
|
||||
rainbow
|
||||
rake (>= 10, < 13)
|
||||
rubocop (>= 0.47.0)
|
||||
rubocop (>= 0.49.0)
|
||||
sysexits (~> 1.1)
|
||||
hashie (3.5.5)
|
||||
heroku-api (0.4.2)
|
||||
excon (~> 0.45)
|
||||
multi_json (~> 1.8)
|
||||
highline (1.7.8)
|
||||
html2haml (2.1.0)
|
||||
hashie (3.5.7)
|
||||
heroics (0.0.24)
|
||||
erubis (~> 2.0)
|
||||
excon
|
||||
moneta
|
||||
multi_json (>= 1.9.2)
|
||||
highline (1.7.10)
|
||||
html2haml (2.2.0)
|
||||
erubis (~> 2.7.0)
|
||||
haml (~> 4.0)
|
||||
haml (>= 4.0, < 6)
|
||||
nokogiri (>= 1.6.0)
|
||||
ruby_parser (~> 3.5)
|
||||
httparty (0.14.0)
|
||||
httparty (0.15.6)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (0.8.1)
|
||||
i18n (0.9.3)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (0.9.12)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
@@ -251,70 +255,85 @@ GEM
|
||||
parser (>= 2.2.3.0)
|
||||
term-ansicolor (>= 1.3.2)
|
||||
terminal-table (>= 1.5.1)
|
||||
jasmine (2.5.2)
|
||||
jasmine-core (>= 2.5.1, < 3.0.0)
|
||||
jasmine (2.9.0)
|
||||
jasmine-core (>= 2.9.0, < 3.0.0)
|
||||
phantomjs
|
||||
rack (>= 1.2.1)
|
||||
rake
|
||||
jasmine-core (2.5.2)
|
||||
jasmine-core (2.99.0)
|
||||
jquery-rails (4.3.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (5.0.5)
|
||||
railties (>= 3.2.16)
|
||||
js-routes (1.3.3)
|
||||
js-routes (1.4.3)
|
||||
railties (>= 3.2)
|
||||
sprockets-rails
|
||||
json (2.0.4)
|
||||
json (2.1.0)
|
||||
jsonapi-resources (0.9.0)
|
||||
activerecord (>= 4.1)
|
||||
concurrent-ruby
|
||||
railties (>= 4.1)
|
||||
jwt (1.5.6)
|
||||
kaminari (0.17.0)
|
||||
actionpack (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
kgio (2.11.0)
|
||||
kramdown (1.13.2)
|
||||
kaminari (1.1.1)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.1.1)
|
||||
kaminari-activerecord (= 1.1.1)
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-actionview (1.1.1)
|
||||
actionview
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-activerecord (1.1.1)
|
||||
activerecord
|
||||
kaminari-core (= 1.1.1)
|
||||
kaminari-core (1.1.1)
|
||||
kgio (2.11.2)
|
||||
kramdown (1.16.2)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
leaflet-markercluster-rails (0.7.0)
|
||||
railties (>= 3.1)
|
||||
leaflet-rails (0.7.7)
|
||||
letter_opener (1.4.1)
|
||||
leaflet-rails (1.3.1)
|
||||
rails (>= 4.2.0)
|
||||
letter_opener (1.6.0)
|
||||
launchy (~> 2.2)
|
||||
listen (3.1.5)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
loofah (2.0.3)
|
||||
loofah (2.2.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
lumberjack (1.0.11)
|
||||
mail (2.6.4)
|
||||
mime-types (>= 1.16, < 4)
|
||||
lumberjack (1.0.12)
|
||||
mail (2.7.0)
|
||||
mini_mime (>= 0.1.1)
|
||||
memcachier (0.0.2)
|
||||
method_source (0.8.2)
|
||||
method_source (0.9.0)
|
||||
mime-types (3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mimemagic (0.3.2)
|
||||
mini_portile2 (2.1.0)
|
||||
minitest (5.10.1)
|
||||
mini_mime (1.0.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.3)
|
||||
moneta (0.8.1)
|
||||
multi_json (1.11.3)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nenv (0.3.0)
|
||||
newrelic_rpm (4.1.0.333)
|
||||
nokogiri (1.7.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
newrelic_rpm (4.8.0.341)
|
||||
nokogiri (1.8.2)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
notiffany (0.1.1)
|
||||
nenv (~> 0.1)
|
||||
shellany (~> 0.0)
|
||||
oauth (0.5.1)
|
||||
oauth2 (1.3.1)
|
||||
faraday (>= 0.8, < 0.12)
|
||||
oauth (0.5.4)
|
||||
oauth2 (1.4.0)
|
||||
faraday (>= 0.8, < 0.13)
|
||||
jwt (~> 1.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
omniauth (1.6.1)
|
||||
omniauth (1.8.1)
|
||||
hashie (>= 3.4.6, < 3.6.0)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-facebook (4.0.0)
|
||||
@@ -325,57 +344,63 @@ GEM
|
||||
omniauth-oauth (1.1.0)
|
||||
oauth
|
||||
omniauth (~> 1.0)
|
||||
omniauth-oauth2 (1.4.0)
|
||||
oauth2 (~> 1.0)
|
||||
omniauth-oauth2 (1.5.0)
|
||||
oauth2 (~> 1.1)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-twitter (1.4.0)
|
||||
omniauth-oauth (~> 1.1)
|
||||
rack
|
||||
orm_adapter (0.5.0)
|
||||
paperclip (5.1.0)
|
||||
paperclip (5.2.1)
|
||||
activemodel (>= 4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
cocaine (~> 0.5.5)
|
||||
mime-types
|
||||
mimemagic (~> 0.3.0)
|
||||
parser (2.4.0.0)
|
||||
ast (~> 2.2)
|
||||
pg (0.20.0)
|
||||
parallel (1.12.1)
|
||||
parser (2.4.0.2)
|
||||
ast (~> 2.3)
|
||||
pg (0.21.0)
|
||||
phantomjs (2.1.1.0)
|
||||
platform-api (2.1.0)
|
||||
heroics (~> 0.0.23)
|
||||
moneta (~> 0.8.1)
|
||||
plupload-rails (1.2.1)
|
||||
rails (>= 3.1)
|
||||
poltergeist (1.14.0)
|
||||
poltergeist (1.17.0)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
websocket-driver (>= 0.2.0)
|
||||
powerpack (0.1.1)
|
||||
pry (0.10.4)
|
||||
pry (0.11.3)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
public_suffix (2.0.5)
|
||||
method_source (~> 0.9.0)
|
||||
public_suffix (3.0.1)
|
||||
quiet_assets (1.1.0)
|
||||
railties (>= 3.1, < 5.0)
|
||||
rack (1.6.5)
|
||||
rack-protection (1.5.3)
|
||||
rack (1.6.9)
|
||||
rack-protection (2.0.1)
|
||||
rack
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.2.8)
|
||||
actionmailer (= 4.2.8)
|
||||
actionpack (= 4.2.8)
|
||||
actionview (= 4.2.8)
|
||||
activejob (= 4.2.8)
|
||||
activemodel (= 4.2.8)
|
||||
activerecord (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
rails (4.2.10)
|
||||
actionmailer (= 4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activejob (= 4.2.10)
|
||||
activemodel (= 4.2.10)
|
||||
activerecord (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.2.8)
|
||||
railties (= 4.2.10)
|
||||
sprockets-rails
|
||||
rails-assets-leaflet (1.3.1)
|
||||
rails-assets-leaflet.markercluster (1.3.0)
|
||||
rails-assets-leaflet (>= 1.0.3)
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
rails-dom-testing (1.0.8)
|
||||
activesupport (>= 4.2.0.beta, < 5.0)
|
||||
rails-dom-testing (1.0.9)
|
||||
activesupport (>= 4.2.0, < 5.0)
|
||||
nokogiri (~> 1.6)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
@@ -388,163 +413,169 @@ GEM
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.5)
|
||||
rails_stdout_logging (0.0.5)
|
||||
railties (4.2.8)
|
||||
actionpack (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
railties (4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.1.0)
|
||||
raindrops (0.18.0)
|
||||
rake (12.0.0)
|
||||
rb-fsevent (0.9.8)
|
||||
rb-inotify (0.9.8)
|
||||
ffi (>= 0.5.0)
|
||||
redis (3.3.3)
|
||||
responders (2.3.0)
|
||||
railties (>= 4.2.0, < 5.1)
|
||||
rspec (3.5.0)
|
||||
rspec-core (~> 3.5.0)
|
||||
rspec-expectations (~> 3.5.0)
|
||||
rspec-mocks (~> 3.5.0)
|
||||
raindrops (0.19.0)
|
||||
rake (12.3.0)
|
||||
rb-fsevent (0.10.2)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
redis (4.0.1)
|
||||
responders (2.4.0)
|
||||
actionpack (>= 4.2.0, < 5.3)
|
||||
railties (>= 4.2.0, < 5.3)
|
||||
rspec (3.7.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-activemodel-mocks (1.0.3)
|
||||
activemodel (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
rspec-mocks (>= 2.99, < 4.0)
|
||||
rspec-core (3.5.4)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-expectations (3.5.0)
|
||||
rspec-core (3.7.1)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-expectations (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-mocks (3.5.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-mocks (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-rails (3.5.2)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-rails (3.7.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.5.0)
|
||||
rspec-expectations (~> 3.5.0)
|
||||
rspec-mocks (~> 3.5.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-support (3.5.0)
|
||||
rubocop (0.47.1)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-support (3.7.1)
|
||||
rubocop (0.49.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.3.3.1, < 3.0)
|
||||
powerpack (~> 0.1)
|
||||
rainbow (>= 1.99.1, < 3.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
ruby-progressbar (1.8.1)
|
||||
ruby-units (2.1.0)
|
||||
ruby-progressbar (1.9.0)
|
||||
ruby-units (2.2.1)
|
||||
ruby_dep (1.5.0)
|
||||
ruby_parser (3.9.0)
|
||||
sexp_processor (~> 4.1)
|
||||
ruby_parser (3.10.1)
|
||||
sexp_processor (~> 4.9)
|
||||
rubyzip (1.2.1)
|
||||
sass (3.4.23)
|
||||
sass-rails (5.0.6)
|
||||
sass (3.5.5)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sass-rails (5.0.7)
|
||||
railties (>= 4.0.0, < 6)
|
||||
sass (~> 3.1)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
selenium-webdriver (3.3.0)
|
||||
selenium-webdriver (3.9.0)
|
||||
childprocess (~> 0.5)
|
||||
rubyzip (~> 1.0)
|
||||
websocket (~> 1.0)
|
||||
sexp_processor (4.9.0)
|
||||
rubyzip (~> 1.2)
|
||||
sexp_processor (4.10.0)
|
||||
shellany (0.0.1)
|
||||
sidekiq (4.2.10)
|
||||
sidekiq (5.1.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (~> 3.2, >= 3.2.1)
|
||||
redis (>= 3.3.4, < 5)
|
||||
simplecov (0.12.0)
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.0)
|
||||
slop (3.6.0)
|
||||
sparkpost_rails (1.4.0)
|
||||
rails (>= 4.0, < 5.1)
|
||||
simplecov-html (0.10.2)
|
||||
sparkpost_rails (1.5.0)
|
||||
rails (>= 4.0, < 5.2)
|
||||
sprockets (3.7.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.0)
|
||||
sprockets-rails (3.2.1)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sysexits (1.2.0)
|
||||
temple (0.8.0)
|
||||
term-ansicolor (1.6.0)
|
||||
tins (~> 1.0)
|
||||
terminal-table (1.7.3)
|
||||
unicode-display_width (~> 1.1.1)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thor (0.19.4)
|
||||
thread (0.2.2)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.7)
|
||||
tins (1.13.2)
|
||||
tilt (2.0.8)
|
||||
timecop (0.9.1)
|
||||
tins (1.16.3)
|
||||
trollop (1.16.2)
|
||||
tzinfo (1.2.3)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (2.7.2)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
unicode-display_width (1.1.3)
|
||||
unicorn (5.3.0)
|
||||
uglifier (4.1.5)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (1.3.0)
|
||||
unicorn (5.4.0)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
uniform_notifier (1.10.0)
|
||||
uniform_notifier (1.11.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
webrat (0.7.3)
|
||||
nokogiri (>= 1.2.0)
|
||||
rack (>= 1.0)
|
||||
rack-test (>= 0.5.3)
|
||||
websocket (1.2.4)
|
||||
websocket-driver (0.6.5)
|
||||
websocket-driver (0.7.0)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.2)
|
||||
will_paginate (3.1.5)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
websocket-extensions (0.1.3)
|
||||
will_paginate (3.1.6)
|
||||
xmlrpc (0.3.0)
|
||||
xpath (3.0.0)
|
||||
nokogiri (~> 1.8)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
active_merchant-paypal-bogus-gateway
|
||||
active_median
|
||||
active_utils
|
||||
activemerchant
|
||||
better_errors
|
||||
acts_as_paranoid (~> 0.5.0)
|
||||
better_errors (~> 2.2.0)
|
||||
binding_of_caller
|
||||
bluecloth
|
||||
bonsai-elasticsearch-rails
|
||||
bootstrap-datepicker-rails
|
||||
bootstrap-kaminari-views
|
||||
bootstrap-sass (~> 3.3.6)
|
||||
bootstrap-sass
|
||||
bullet
|
||||
bundler (>= 1.1.5)
|
||||
byebug
|
||||
cancancan (~> 1.9)
|
||||
cancancan
|
||||
capybara
|
||||
capybara-email
|
||||
capybara-screenshot
|
||||
chartkick
|
||||
codeclimate-test-reporter
|
||||
coffee-rails (~> 4.1.0)
|
||||
comfortable_mexican_sofa (~> 1.12.0)
|
||||
coffee-rails
|
||||
comfortable_mexican_sofa
|
||||
coveralls
|
||||
csv_shaper
|
||||
d3-rails (~> 3.5)
|
||||
dalli
|
||||
database_cleaner (~> 1.5.0)
|
||||
devise (>= 4.0.0)
|
||||
database_cleaner
|
||||
devise
|
||||
elasticsearch-api (~> 2.0.0)
|
||||
elasticsearch-model
|
||||
elasticsearch-rails
|
||||
factory_girl_rails
|
||||
factory_bot_rails
|
||||
faker
|
||||
figaro
|
||||
flickraw
|
||||
font-awesome-sass
|
||||
friendly_id (~> 5.0.4)
|
||||
friendly_id
|
||||
geocoder
|
||||
gibbon (~> 1.2.0)
|
||||
gravatar-ultimate
|
||||
@@ -555,45 +586,50 @@ DEPENDENCIES
|
||||
haml-rails
|
||||
haml_lint
|
||||
hashie (>= 3.5.3)
|
||||
heroku-api
|
||||
i18n-tasks
|
||||
jasmine
|
||||
jquery-rails
|
||||
jquery-ui-rails (~> 5.0.2)
|
||||
js-routes
|
||||
kaminari (~> 0.17.0)
|
||||
leaflet-markercluster-rails
|
||||
leaflet-rails (~> 0.7.7)
|
||||
jsonapi-resources
|
||||
kaminari
|
||||
leaflet-rails
|
||||
letter_opener
|
||||
loofah (>= 2.2.1)
|
||||
memcachier
|
||||
newrelic_rpm
|
||||
omniauth (~> 1.3)
|
||||
omniauth-facebook
|
||||
omniauth-flickr (>= 0.0.15)
|
||||
omniauth-twitter (~> 1.2)
|
||||
pg
|
||||
omniauth-twitter
|
||||
pg (< 1.0.0)
|
||||
platform-api
|
||||
poltergeist
|
||||
pry
|
||||
quiet_assets
|
||||
rails (~> 4.2.7)
|
||||
rack-protection (>= 2.0.1)
|
||||
rails (~> 4.2.8)
|
||||
rails-assets-leaflet.markercluster!
|
||||
rails_12factor
|
||||
rainbow (< 2.2.0)
|
||||
rake (>= 10.0.0)
|
||||
responders
|
||||
rspec-activemodel-mocks
|
||||
rspec-rails
|
||||
rubocop (<= 0.47.1)
|
||||
rubocop
|
||||
ruby-units
|
||||
sass-rails (~> 5.0.4)
|
||||
sass-rails
|
||||
selenium-webdriver
|
||||
sidekiq
|
||||
sparkpost_rails
|
||||
uglifier (~> 2.7.2)
|
||||
timecop
|
||||
uglifier
|
||||
unicorn
|
||||
webrat
|
||||
will_paginate (~> 3.0)
|
||||
will_paginate
|
||||
xmlrpc
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.3.4p301
|
||||
|
||||
ruby 2.4.1p111
|
||||
BUNDLED WITH
|
||||
1.14.6
|
||||
1.16.1
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
guard :rspec,
|
||||
cmd: 'bundle exec rspec --format documentation',
|
||||
failed_mode: :keep do
|
||||
cmd: 'bundle exec rspec --format documentation',
|
||||
failed_mode: :keep do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/libs/#{m[1]}_spec.rb" }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
|
||||
# Rails example
|
||||
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
||||
watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
||||
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
||||
watch('config/routes.rb') { "spec/routing" }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -57,5 +57,7 @@ Feel free to comment on any of the issues on [Github](https://github.com/Growstu
|
||||
|
||||
For more information about this project, contact [info@growstuff.org](mailto:info@growstuff.org).
|
||||
|
||||
Security Issues: If you find an authorization bypass or data breach, please contact our maintainers directly at [maintainers@growstuff.org](mailto:maintainers@growstuff.org).
|
||||
|
||||
You can also contact us on [Twitter](http://twitter.com/growstufforg/) or
|
||||
[Facebook](https://www.facebook.com/pages/Growstuff/1531133417099494) or [Github](https://github.com/Growstuff/growstuff/issues)..
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
||||
// GO AFTER THE REQUIRES BELOW.
|
||||
//
|
||||
//= require leaflet
|
||||
//= require leaflet.markercluster
|
||||
//= require js-routes
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require jquery-ui/autocomplete
|
||||
//= require bootstrap-sprockets
|
||||
//= require bootstrap-datepicker
|
||||
//= require_tree .
|
||||
// = require leaflet
|
||||
// = require leaflet.markercluster
|
||||
// = require js-routes
|
||||
// = require jquery
|
||||
// = require jquery_ujs
|
||||
// = require jquery-ui/autocomplete
|
||||
// = require bootstrap-sprockets
|
||||
// = require bootstrap-datepicker
|
||||
// = require_tree .
|
||||
|
||||
File diff suppressed because one or more lines are too long
2
app/assets/javascripts/charts.js
Normal file
2
app/assets/javascripts/charts.js
Normal file
@@ -0,0 +1,2 @@
|
||||
// = require Chart.bundle
|
||||
// = require chartkick
|
||||
39
app/assets/javascripts/crops.js.coffee
Normal file
39
app/assets/javascripts/crops.js.coffee
Normal file
@@ -0,0 +1,39 @@
|
||||
jQuery ->
|
||||
$('#add-sci_name-row').css("display", "inline-block")
|
||||
$('#remove-sci_name-row').css("display", "inline-block")
|
||||
$("#add-alt_name-row").css("display", "inline-block")
|
||||
$("#remove-alt_name-row").css("display", "inline-block")
|
||||
|
||||
-$ ->
|
||||
sci_template = "<div id='sci_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Scientific name INDEX:</label></div><div class='col-md-8'><input name='sci_name[INDEX]' class='form-control', id='sci_name[INDEX]')'></input><span class='help-block'>Scientific name of crop.</span></div><div class='col-md-2'></div></div>"
|
||||
|
||||
sci_index = $('#scientific_names .template').length + 1
|
||||
|
||||
$('#add-sci_name-row').click ->
|
||||
compiled_input = $(sci_template.split("INDEX").join(sci_index))
|
||||
$('#scientific_names').append(compiled_input)
|
||||
sci_index = sci_index + 1
|
||||
|
||||
$('#remove-sci_name-row').click ->
|
||||
if (sci_index > 2)
|
||||
sci_index = sci_index - 1
|
||||
tmp = 'sci_template[' + sci_index + ']'
|
||||
element = document.getElementById(tmp)
|
||||
element.remove()
|
||||
|
||||
alt_template = "<div id='alt_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Alternate name INDEX:</label></div><div class='col-md-8'><input name='alt_name[INDEX]' class='form-control', id='alt_name[INDEX]')'></input><span class='help-block'>Alternate name of crop.</span></div><div class='col-md-2'></div></div>"
|
||||
|
||||
alt_index = $('#alternate_names .template').length + 1
|
||||
|
||||
$('#add-alt_name-row').click ->
|
||||
compiled_input = $(alt_template.split("INDEX").join(alt_index))
|
||||
$('#alternate_names').append(compiled_input)
|
||||
alt_index = alt_index + 1
|
||||
|
||||
$('#remove-alt_name-row').click ->
|
||||
if (alt_index > 2)
|
||||
alt_index = alt_index - 1
|
||||
tmp = 'alt_template[' + alt_index + ']'
|
||||
element = document.getElementById(tmp)
|
||||
console.log("%s",tmp)
|
||||
element.remove()
|
||||
@@ -1,37 +1,27 @@
|
||||
//= require graphs/horizontal_bar_graph
|
||||
|
||||
if (document.getElementById("cropmap") !== null) {
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>";
|
||||
mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token;
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
|
||||
cropmap = L.map('cropmap').setView([0.0, -0.0], 2);
|
||||
showCropMap(cropmap);
|
||||
}
|
||||
|
||||
function showCropMap(cropmap) {
|
||||
var mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
var mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>";
|
||||
var mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token;
|
||||
|
||||
L.tileLayer(mapbox_base_url, {
|
||||
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors under <a href="http://www.openstreetmap.org/copyright">ODbL</a> | Map imagery © <a href="http://mapbox.com">Mapbox</a>',
|
||||
maxZoom: 18
|
||||
}).addTo(cropmap);
|
||||
markers = new L.MarkerClusterGroup({showCoverageOnHover: false, maxClusterRadius: 20 });
|
||||
var markers = new L.MarkerClusterGroup({showCoverageOnHover: false, maxClusterRadius: 20 });
|
||||
|
||||
things_to_map = location.pathname + '.json';
|
||||
var things_to_map = location.pathname + '.json';
|
||||
$.getJSON(things_to_map, function(crop) {
|
||||
$.each(crop.plantings, function(i, planting) {
|
||||
owner = planting.owner;
|
||||
var owner = planting.owner;
|
||||
if (owner.latitude && owner.longitude) {
|
||||
marker = new L.Marker(new L.LatLng(owner.latitude, owner.longitude));
|
||||
var marker = new L.Marker(new L.LatLng(owner.latitude, owner.longitude));
|
||||
|
||||
planting_url = "/plantings/" + planting.id;
|
||||
planting_link = "<a href='" + planting_url + "'>" + owner.login_name + "'s " + crop.name + "</a>";
|
||||
var planting_url = "/plantings/" + planting.id;
|
||||
var planting_link = "<a href='" + planting_url + "'>" + owner.login_name + "'s " + crop.name + "</a>";
|
||||
|
||||
where = "<p><i>" + owner.location + "</i></p>";
|
||||
var where = "<p><i>" + owner.location + "</i></p>";
|
||||
|
||||
details = "<p>";
|
||||
var details = "<p>";
|
||||
if (planting.quantity) {
|
||||
details = details + "Quantity: " + planting.quantity + "<br/>";
|
||||
}
|
||||
@@ -51,41 +41,17 @@ function showCropMap(cropmap) {
|
||||
cropmap.addLayer(markers);
|
||||
}
|
||||
|
||||
function plantingStats(crop) {
|
||||
var sunniness_counts = { 'empty': 0, 'sun': 0, 'semi-shade': 0, 'shade': 0 };
|
||||
$.each(crop.plantings, function(i, planting) {
|
||||
if (planting.sunniness) {
|
||||
sunniness_counts[planting.sunniness]++;
|
||||
} else {
|
||||
sunniness_counts['Empty']++;
|
||||
}
|
||||
$(document).ready(function() {
|
||||
|
||||
if (document.getElementById("cropmap") !== null) {
|
||||
|
||||
L.Icon.Default.imagePath = '/assets';
|
||||
|
||||
var cropmap = L.map('cropmap').setView([0.0, -0.0], 2);
|
||||
showCropMap(cropmap);
|
||||
}
|
||||
|
||||
$('.btn.toggle.crop-hierarchy').click(function () {
|
||||
$('.toggle.crop-hierarchy').toggleClass('hide');
|
||||
});
|
||||
return [
|
||||
{name: 'Empty', value: sunniness_counts['empty']},
|
||||
{name: 'Sun', value: sunniness_counts['sun']},
|
||||
{name: 'Semi-shade', value: sunniness_counts['semi-shade']},
|
||||
{name: 'Shade', value: sunniness_counts['shade']}
|
||||
];
|
||||
}
|
||||
|
||||
if ($("#sunchart")[0] !== null) {
|
||||
var HorizontalBarGraph = growstuff.HorizontalBarGraph;
|
||||
$.getJSON(location.pathname + '.json', function (crop) {
|
||||
data = {
|
||||
bars: plantingStats(crop),
|
||||
bar_color: 'steelblue',
|
||||
width: {size: 300, scale: 'linear'},
|
||||
height: {size: 100, scale: 'ordinal'},
|
||||
//left is used to shift the bars over so that there is
|
||||
//room for the labels
|
||||
margin: {top: 0, right: 0, bottom: 0, left: 100}
|
||||
};
|
||||
|
||||
var graph = new HorizontalBarGraph(data);
|
||||
graph.render(d3.select($('#sunchart')[0]));
|
||||
});
|
||||
}
|
||||
|
||||
$('.btn.toggle.crop-hierarchy').click(function () {
|
||||
$('.toggle.crop-hierarchy').toggleClass('hide');
|
||||
});
|
||||
});
|
||||
19
app/assets/javascripts/finish_seed.js.coffee
Normal file
19
app/assets/javascripts/finish_seed.js.coffee
Normal file
@@ -0,0 +1,19 @@
|
||||
# Clears the finished at date field when
|
||||
# a seed is marked unfinished, and
|
||||
# repopulates the field with a cached value
|
||||
# marking unfinished is undone.
|
||||
|
||||
jQuery ->
|
||||
previousValue = ''
|
||||
$('#seed_finished').on('click', ->
|
||||
finished = $('#seed_finished_at')
|
||||
if @checked
|
||||
if previousValue.length
|
||||
date = previousValue
|
||||
finished.val(date)
|
||||
else
|
||||
finished.trigger('focus')
|
||||
else
|
||||
previousValue = finished.val()
|
||||
finished.val('')
|
||||
)
|
||||
@@ -1,50 +0,0 @@
|
||||
//= require graphs/width_scale
|
||||
//= require graphs/height_scale
|
||||
|
||||
(function(){
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
This represents bars for a bar graph.
|
||||
Currently these are used for HorizontalBarGraph.
|
||||
*/
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
var WidthScale = growstuff.WidthScale;
|
||||
var HeightScale = growstuff.HeightScale;
|
||||
|
||||
function BarGroup(data) {
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
BarGroup.prototype.render = function(root){
|
||||
|
||||
var data = this._data;
|
||||
var bars = this._data.bars;
|
||||
var widthScale = new WidthScale(data).render();
|
||||
var heightScale = new HeightScale(data).render();
|
||||
|
||||
return root.append('g')
|
||||
.attr("class", "bar")
|
||||
.selectAll("rect")
|
||||
.data(bars.map(function(bar) { return bar.value; }))
|
||||
.enter()
|
||||
.append("rect")
|
||||
.attr("y", function(d, i){
|
||||
return heightScale(i);
|
||||
|
||||
})
|
||||
.attr("height", heightScale.rangeBand())
|
||||
.attr("fill", data.bar_color)
|
||||
.attr("width", function(d){
|
||||
return widthScale(d);
|
||||
})
|
||||
.append("title")
|
||||
.text(function(d){
|
||||
return 'This value is ' + d + '.';
|
||||
});
|
||||
};
|
||||
|
||||
growstuff.BarGroup = BarGroup;
|
||||
|
||||
}());
|
||||
@@ -1,40 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
This file draws the labels to the left of each bar.
|
||||
*/
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
|
||||
function BarLabelGroup(data) {
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
BarLabelGroup.prototype.render = function(d3){
|
||||
var bars = this._data.bars;
|
||||
//vvcopy pasta from spike vv -- this is a good candidate for refactor
|
||||
var barHeight = 40;
|
||||
|
||||
return d3.append('g')
|
||||
.attr("class", "bar-label")
|
||||
.selectAll("text")
|
||||
.data(bars.map(function(bar){ return bar.name;}))
|
||||
.enter()
|
||||
.append("text")
|
||||
.attr('x', -80)
|
||||
.attr('y', function(d, i){
|
||||
//shrink the margin between each label to give them an even spread with
|
||||
//bars
|
||||
var barLabelSpread = 2/3;
|
||||
//move them downward to line up with bars
|
||||
var barLabelTopEdge = 17;
|
||||
return i * barHeight * (barLabelSpread) + barLabelTopEdge;
|
||||
})
|
||||
.text(function(d){return d;});
|
||||
|
||||
};
|
||||
|
||||
growstuff.BarLabelGroup = BarLabelGroup;
|
||||
|
||||
}());
|
||||
@@ -1,29 +0,0 @@
|
||||
//=require d3
|
||||
|
||||
/*
|
||||
Height Scale is used to map the number of bars to the display size of
|
||||
the svg
|
||||
*/
|
||||
|
||||
(function(){
|
||||
'use strict';
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
|
||||
function HeightScale(data){
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
HeightScale.prototype.render = function(){
|
||||
var data = this._data;
|
||||
var scaleType = data.height.scale;
|
||||
var axisSize = data.height.size;
|
||||
|
||||
return d3.scale[scaleType]()
|
||||
.domain(d3.range(data.bars.length))
|
||||
.rangeRoundBands([0, data.height.size], 0.05, 0);
|
||||
};
|
||||
|
||||
growstuff.HeightScale = HeightScale;
|
||||
|
||||
}());
|
||||
@@ -1,51 +0,0 @@
|
||||
//= require d3
|
||||
//= require graphs/bar_group
|
||||
//= require graphs/bar_label_group
|
||||
|
||||
/*
|
||||
Horizontal Bar Graph represents sum total of the graph including all of the parts:
|
||||
Bars
|
||||
Bar Labels
|
||||
|
||||
The main dimensions of the graph are rendered here.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
var BarGroup = growstuff.BarGroup;
|
||||
var BarLabelGroup = growstuff.BarLabelGroup;
|
||||
|
||||
function HorizontalBarGraph(data) {
|
||||
this._data = data;
|
||||
this._d3 = d3;
|
||||
}
|
||||
|
||||
HorizontalBarGraph.prototype.render = function(root) {
|
||||
var bars = this._data.bars;
|
||||
var width = this._data.width;
|
||||
var height = this._data.height;
|
||||
|
||||
var barLabelGroup = new BarLabelGroup(this._data);
|
||||
var margin = this._data.margin;
|
||||
|
||||
var barGroup = new BarGroup(this._data);
|
||||
|
||||
var svg = root
|
||||
.append("svg")
|
||||
.attr("width", width.size + margin.left + margin.right)
|
||||
.attr("height", height.size + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("class","bar-graph")
|
||||
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
|
||||
barGroup.render(svg);
|
||||
barLabelGroup.render(svg);
|
||||
|
||||
return svg;
|
||||
};
|
||||
|
||||
growstuff.HorizontalBarGraph = HorizontalBarGraph;
|
||||
}());
|
||||
@@ -1,33 +0,0 @@
|
||||
//=require d3
|
||||
|
||||
/*
|
||||
Width scale is used to map the value for the length of each bar
|
||||
to the display size of the svg
|
||||
*/
|
||||
|
||||
(function(){
|
||||
'use strict';
|
||||
|
||||
var growstuff = (window.growstuff = window.growstuff || {});
|
||||
|
||||
function WidthScale (data){
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
WidthScale.prototype.render = function() {
|
||||
var data = this._data;
|
||||
var scaleType = data.width.scale;
|
||||
var axisSize = data.width.size;
|
||||
|
||||
return d3.scale[scaleType]()
|
||||
.domain([0, this.getMaxValue()])
|
||||
.range([0, axisSize]);
|
||||
};
|
||||
|
||||
WidthScale.prototype.getMaxValue = function(){
|
||||
return d3.max(this._data.bars.map(function(bar) { return bar.value; }));
|
||||
};
|
||||
|
||||
growstuff.WidthScale = WidthScale;
|
||||
|
||||
}());
|
||||
@@ -1,25 +1,24 @@
|
||||
if (document.getElementById("membermap") !== null) {
|
||||
mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>";
|
||||
mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token;
|
||||
|
||||
L.Icon.Default.imagePath = '/assets'
|
||||
var mapbox_map_id = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_map_id %>";
|
||||
var mapbox_access_token = "<%= Rails.env == 'test' ? 0 : Growstuff::Application.config.mapbox_access_token %>";
|
||||
var mapbox_base_url = "http://a.tiles.mapbox.com/v4/" + mapbox_map_id + "/{z}/{x}/{y}.png?access_token=" + mapbox_access_token;
|
||||
|
||||
L.Icon.Default.imagePath = '/assets';
|
||||
|
||||
$.getJSON(location.pathname + '.json', function(member) {
|
||||
if (member.latitude && member.longitude) {
|
||||
membermap = L.map('membermap').setView([member.latitude, member.longitude], 4);
|
||||
var membermap = L.map('membermap').setView([member.latitude, member.longitude], 4);
|
||||
|
||||
L.tileLayer(mapbox_base_url, {
|
||||
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors under <a href="http://www.openstreetmap.org/copyright">ODbL</a> | Map imagery © <a href="http://mapbox.com">Mapbox</a>',
|
||||
maxZoom: 18
|
||||
}).addTo(membermap);
|
||||
marker = new L.Marker(new L.LatLng(member.latitude, member.longitude));
|
||||
var marker = new L.Marker(new L.LatLng(member.latitude, member.longitude));
|
||||
|
||||
member_url = "/members/" + member.slug;
|
||||
member_link = "<a href='" + member_url + "'>" + member.login_name + "</a>";
|
||||
var member_url = "/members/" + member.slug;
|
||||
var member_link = "<a href='" + member_url + "'>" + member.login_name + "</a>";
|
||||
|
||||
where = "<p><i>" + member.location + "</i></p>";
|
||||
var where = "<p><i>" + member.location + "</i></p>";
|
||||
|
||||
marker.bindPopup(member_link + where).openPopup();
|
||||
marker.addTo(membermap);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
$(document).ready(function () {
|
||||
$(document).ready(function() {
|
||||
$('.post-like').show();
|
||||
|
||||
$('.post-like').on('ajax:success', function(event, data) {
|
||||
var like_control = $('#post-' + data.id + ' .post-like');
|
||||
var likeControl = $('#post-' + data.id + ' .post-like');
|
||||
|
||||
$('#post-' + data.id + ' .like-count').text(data.description);
|
||||
if (data.liked_by_member) {
|
||||
like_control.data("method", "delete");
|
||||
like_control.attr("href", data.url);
|
||||
like_control.text("Unlike");
|
||||
likeControl.data('method', 'delete');
|
||||
likeControl.attr('href', data.url);
|
||||
likeControl.text('Unlike');
|
||||
} else {
|
||||
like_control.data("method", "post");
|
||||
like_control.attr("href", '/likes.json?post_id=' + data.id);
|
||||
like_control.text("Like");
|
||||
likeControl.data('method', 'post');
|
||||
likeControl.attr('href', '/likes.json?post_id=' + data.id);
|
||||
likeControl.text('Like');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,41 +4,3 @@
|
||||
|
||||
jQuery ->
|
||||
$('.add-datepicker').datepicker('format' : 'yyyy-mm-dd')
|
||||
$('#add-sci_name-row').css("display", "inline-block")
|
||||
$('#remove-sci_name-row').css("display", "inline-block")
|
||||
$("#add-alt_name-row").css("display", "inline-block")
|
||||
$("#remove-alt_name-row").css("display", "inline-block")
|
||||
|
||||
$ ->
|
||||
sci_template = "<div id='sci_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Scientific name INDEX:</label></div><div class='col-md-8'><input name='sci_name[INDEX]' class='form-control', id='sci_name[INDEX]')'></input><span class='help-block'>Scientific name of crop.</span></div><div class='col-md-2'></div></div>"
|
||||
|
||||
sci_index = $('#scientific_names .template').length + 1
|
||||
|
||||
$('#add-sci_name-row').click ->
|
||||
compiled_input = $(sci_template.split("INDEX").join(sci_index))
|
||||
$('#scientific_names').append(compiled_input)
|
||||
sci_index = sci_index + 1
|
||||
|
||||
$('#remove-sci_name-row').click ->
|
||||
if (sci_index > 2)
|
||||
sci_index = sci_index - 1
|
||||
tmp = 'sci_template[' + sci_index + ']'
|
||||
element = document.getElementById(tmp)
|
||||
element.remove()
|
||||
|
||||
alt_template = "<div id='alt_template[INDEX]' class='template col-md-12'><div class='col-md-2'><label>Alternate name INDEX:</label></div><div class='col-md-8'><input name='alt_name[INDEX]' class='form-control', id='alt_name[INDEX]')'></input><span class='help-block'>Alternate name of crop.</span></div><div class='col-md-2'></div></div>"
|
||||
|
||||
alt_index = $('#alternate_names .template').length + 1
|
||||
|
||||
$('#add-alt_name-row').click ->
|
||||
compiled_input = $(alt_template.split("INDEX").join(alt_index))
|
||||
$('#alternate_names').append(compiled_input)
|
||||
alt_index = alt_index + 1
|
||||
|
||||
$('#remove-alt_name-row').click ->
|
||||
if (alt_index > 2)
|
||||
alt_index = alt_index - 1
|
||||
tmp = 'alt_template[' + alt_index + ']'
|
||||
element = document.getElementById(tmp)
|
||||
console.log("%s",tmp)
|
||||
element.remove()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import 'bootstrap-datepicker'
|
||||
@import 'leaflet'
|
||||
@import 'leaflet.markercluster'
|
||||
@import 'leaflet.markercluster.default'
|
||||
@import 'custom_bootstrap/custom_bootstrap'
|
||||
@import 'overrides'
|
||||
@import 'graphs'
|
||||
@import 'predictions'
|
||||
|
||||
@@ -1 +1 @@
|
||||
.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-inner>.item{position:absolute;top:-999999em;display:block;-moz-transition:ease-in-out 0.6s left;-o-transition:ease-in-out 0.6s left;-webkit-transition:ease-in-out 0.6s left;transition:ease-in-out 0.6s left}.carousel-inner>.active{top:0}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{position:relative}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}
|
||||
.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0px 0px 0px 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0px 0px 0px 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0px, rgba(0,0,0,0.5) 100%);box-shadow:0px 0px 0px 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:0.5em;left:0.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}
|
||||
|
||||
@@ -13,6 +13,7 @@ $blue: #2f4365
|
||||
$red: #8e4d43
|
||||
$orange: #ffa500
|
||||
$yellow: #b2935c
|
||||
$white: #ffffff
|
||||
|
||||
$body-bg: $beige
|
||||
$text-color: $brown
|
||||
|
||||
@@ -3,37 +3,18 @@
|
||||
@import "custom_bootstrap/variables"
|
||||
// this padding needs to be done before the responsive stuff is imported
|
||||
body
|
||||
// modifying this for our promotional banner. can be replaced after if
|
||||
// needed.
|
||||
// padding-top: $navbar-height + 15px
|
||||
padding-top: $navbar-height
|
||||
|
||||
|
||||
//@import "bootstrap/responsive"
|
||||
|
||||
// Font Awesome
|
||||
@import "font-awesome-sprockets"
|
||||
@import "font-awesome"
|
||||
|
||||
// Glyphicons
|
||||
//@import "bootstrap/glyphicons"
|
||||
|
||||
|
||||
.list-inline > li.first
|
||||
padding-left: 0px
|
||||
|
||||
h2
|
||||
font-size: 150%
|
||||
|
||||
|
||||
//#subtitle
|
||||
// color: lighten($brown, 30%)
|
||||
// font-style: italic
|
||||
// font-weight: normal
|
||||
// margin-top: 0px
|
||||
// padding-left: 1em
|
||||
// padding-top: 0px
|
||||
|
||||
h3
|
||||
font-size: 120%
|
||||
|
||||
@@ -108,29 +89,11 @@ p.stats
|
||||
|
||||
.planting
|
||||
dl.planting-attributes
|
||||
font-size: 85%
|
||||
|
||||
dt
|
||||
text-align: left
|
||||
dd
|
||||
margin-left: auto
|
||||
|
||||
@media (min-width: $screen-md-min)
|
||||
.planting-thumbnail
|
||||
dl.planting-attributes
|
||||
font-size: 85%
|
||||
width: 100%
|
||||
|
||||
dt
|
||||
text-align: left
|
||||
width: 80px
|
||||
dd
|
||||
padding-left: 80px
|
||||
margin-left: auto
|
||||
|
||||
.navbar .navbar-form
|
||||
width: 250px
|
||||
|
||||
|
||||
#placesmap, #cropmap
|
||||
height: 500px
|
||||
@@ -276,24 +239,6 @@ html, body
|
||||
a
|
||||
font-weight: 800
|
||||
|
||||
// Overrides applying only to mobile view. This must be at the end of the overrides file.
|
||||
|
||||
@media only screen and (max-width: 767px)
|
||||
.sidebar
|
||||
margin-left: 0
|
||||
border-left: none
|
||||
padding-left: 0
|
||||
|
||||
#map
|
||||
height: 300px
|
||||
|
||||
.navbar .nav > li
|
||||
display: block
|
||||
|
||||
.navbar .navbar-form
|
||||
width: 185px
|
||||
padding-left: 0
|
||||
padding-right: 0
|
||||
|
||||
/* override "info" alert boxes to be green, not blue, on Growstuff */
|
||||
$state-info-text: darken($green, 10%)
|
||||
@@ -336,3 +281,56 @@ $state-success-bg: lighten($green, 50%)
|
||||
|
||||
ul.plantings
|
||||
list-style-type: none
|
||||
|
||||
ul.thumbnail-buttons
|
||||
list-style-type: none
|
||||
text-align: right
|
||||
|
||||
|
||||
.hover-wrapper .text
|
||||
position: absolute
|
||||
visibility: hidden
|
||||
|
||||
.hover-wrapper:hover .text
|
||||
visibility: visible
|
||||
|
||||
.homepage-listing
|
||||
padding-bottom: 6px
|
||||
|
||||
@media (min-width: $screen-md-min)
|
||||
.planting-thumbnail
|
||||
dl.planting-attributes
|
||||
width: 100%
|
||||
|
||||
dt
|
||||
text-align: left
|
||||
width: 120px
|
||||
dd
|
||||
padding-left: 120px
|
||||
margin-left: auto
|
||||
|
||||
.navbar .navbar-form
|
||||
width: 250px
|
||||
|
||||
// Overrides applying only to mobile view. This must be at the end of the overrides file.
|
||||
@media only screen and (max-width: 767px)
|
||||
.sidebar
|
||||
margin-left: 0
|
||||
border-left: none
|
||||
padding-left: 0
|
||||
|
||||
#map
|
||||
height: 300px
|
||||
|
||||
.navbar .nav > li
|
||||
display: block
|
||||
|
||||
.navbar .navbar-form
|
||||
width: 185px
|
||||
padding-left: 0
|
||||
padding-right: 0
|
||||
.homepage
|
||||
.thumbnail
|
||||
height: 180px
|
||||
.seed-thumbnail
|
||||
height: 220px
|
||||
|
||||
11
app/assets/stylesheets/predictions.sass
Normal file
11
app/assets/stylesheets/predictions.sass
Normal file
@@ -0,0 +1,11 @@
|
||||
.predictions
|
||||
.metric
|
||||
height: 180px
|
||||
border: 1px solid lighten($green, 20%)
|
||||
background: $white
|
||||
margin: 4px
|
||||
strong
|
||||
font-size: 250%
|
||||
font-align: center
|
||||
h3
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
module Growstuff
|
||||
module Constants
|
||||
class PhotoModels
|
||||
PLANTING = { type: 'planting', class: 'Planting', relation: 'plantings' }.freeze
|
||||
HARVEST = { type: 'harvest', class: 'Harvest', relation: 'harvests' }.freeze
|
||||
GARDEN = { type: 'garden', class: 'Garden', relation: 'gardens' }.freeze
|
||||
SEED = { type: 'seed', class: 'Seed', relation: 'seeds' }.freeze
|
||||
|
||||
ALL = [PLANTING, HARVEST, GARDEN, SEED].freeze
|
||||
|
||||
def self.types
|
||||
ALL.map do |model|
|
||||
model[:type]
|
||||
end
|
||||
end
|
||||
|
||||
def self.relations
|
||||
ALL.map do |model|
|
||||
model[:relation]
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_relation(object, type)
|
||||
relation = ALL.select do |model|
|
||||
model[:type] == type
|
||||
end[0][:relation]
|
||||
object.send(relation)
|
||||
end
|
||||
|
||||
def self.get_item(type)
|
||||
class_name = ALL.select do |model|
|
||||
model[:type] == type
|
||||
end[0][:class]
|
||||
class_name.constantize
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,53 +0,0 @@
|
||||
class AccountTypesController < ApplicationController
|
||||
before_action :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
respond_to :html
|
||||
|
||||
# GET /account_types
|
||||
def index
|
||||
@account_types = AccountType.all
|
||||
respond_with(@account_types)
|
||||
end
|
||||
|
||||
# GET /account_types/1
|
||||
def show
|
||||
respond_with(@account_types)
|
||||
end
|
||||
|
||||
# GET /account_types/new
|
||||
def new
|
||||
@account_type = AccountType.new
|
||||
respond_with(@account_type)
|
||||
end
|
||||
|
||||
# GET /account_types/1/edit
|
||||
def edit
|
||||
respond_with(@account_type)
|
||||
end
|
||||
|
||||
# POST /account_types
|
||||
def create
|
||||
@account_type = AccountType.new(account_type_params)
|
||||
flash[:notice] = I18n.t('account_types.created') if @account_type.save
|
||||
respond_with(@account_type)
|
||||
end
|
||||
|
||||
# PUT /account_types/1
|
||||
def update
|
||||
flash[:notice] = I18n.t('account_types.updated') if @account_type.update(account_type_params)
|
||||
respond_with(@account_type)
|
||||
end
|
||||
|
||||
# DELETE /account_types/1
|
||||
def destroy
|
||||
@account_type.destroy
|
||||
flash[:notice] = I18n.t('account_types.deleted')
|
||||
respond_with(@account_type)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_type_params
|
||||
params.require(:account_type).permit(:is_paid, :is_permanent_paid, :name)
|
||||
end
|
||||
end
|
||||
@@ -1,32 +0,0 @@
|
||||
class AccountsController < ApplicationController
|
||||
before_action :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
respond_to :html
|
||||
|
||||
# GET /accounts
|
||||
def index
|
||||
@accounts = Account.all
|
||||
respond_with(@accounts)
|
||||
end
|
||||
|
||||
# GET /accounts/1
|
||||
def show
|
||||
respond_with(@account)
|
||||
end
|
||||
|
||||
# GET /accounts/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# PUT /accounts/1
|
||||
def update
|
||||
flash[:notice] = I18n.t('account.update') if @account.update(params[:account])
|
||||
respond_with(@account)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account_params
|
||||
params.require(:account).permit(:account_type_id, :member_id, :paid_until)
|
||||
end
|
||||
end
|
||||
14
app/controllers/admin/members_controller.rb
Normal file
14
app/controllers/admin/members_controller.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
module Admin
|
||||
class MembersController < ApplicationController
|
||||
before_action :auth!
|
||||
def index
|
||||
@members = Member.order(:login_name).paginate(page: params[:page])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def auth!
|
||||
authorize! :manage, :all
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
class Admin::OrdersController < ApplicationController
|
||||
def index
|
||||
authorize! :manage, :all
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
authorize! :manage, :all
|
||||
@orders = Order.search(by: params[:search_by], for: params[:search_text])
|
||||
|
||||
if @orders.empty?
|
||||
flash[:alert] = "Couldn't find order with #{params[:search_by]} = #{params[:search_text]}"
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,12 @@
|
||||
class AdminController < ApplicationController
|
||||
respond_to :html
|
||||
def index
|
||||
authorize! :manage, :all
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
end
|
||||
end
|
||||
|
||||
def newsletter
|
||||
authorize! :manage, :all
|
||||
@members = Member.confirmed.wants_newsletter.all
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
end
|
||||
respond_with @members
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
class AlternateNamesController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
responders :flash
|
||||
|
||||
# GET /alternate_names
|
||||
# GET /alternate_names.json
|
||||
def index
|
||||
@alternate_names = AlternateName.all
|
||||
@alternate_names = AlternateName.all.order(:name)
|
||||
respond_with(@alternate_names)
|
||||
end
|
||||
|
||||
|
||||
7
app/controllers/api/v1/base_controller.rb
Normal file
7
app/controllers/api/v1/base_controller.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Api
|
||||
module V1
|
||||
class BaseController < JSONAPI::ResourceController
|
||||
abstract
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/controllers/api/v1/crops_controller.rb
Normal file
6
app/controllers/api/v1/crops_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Api
|
||||
module V1
|
||||
class CropsController < BaseController
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/controllers/api/v1/gardens_controller.rb
Normal file
6
app/controllers/api/v1/gardens_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Api
|
||||
module V1
|
||||
class GardensController < BaseController
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/controllers/api/v1/harvests_controller.rb
Normal file
6
app/controllers/api/v1/harvests_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Api
|
||||
module V1
|
||||
class HarvestsController < BaseController
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/controllers/api/v1/members_controller.rb
Normal file
6
app/controllers/api/v1/members_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Api
|
||||
module V1
|
||||
class MembersController < BaseController
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/controllers/api/v1/photos_controller.rb
Normal file
6
app/controllers/api/v1/photos_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Api
|
||||
module V1
|
||||
class PhotosController < BaseController
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/controllers/api/v1/plantings_controller.rb
Normal file
6
app/controllers/api/v1/plantings_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Api
|
||||
module V1
|
||||
class PlantingsController < BaseController
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/controllers/api/v1/seeds_controller.rb
Normal file
6
app/controllers/api/v1/seeds_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Api
|
||||
module V1
|
||||
class SeedsController < BaseController
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,4 @@
|
||||
require './lib/actions/oauth_signup_action'
|
||||
class AuthenticationsController < ApplicationController
|
||||
before_action :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
30
app/controllers/charts/crops_controller.rb
Normal file
30
app/controllers/charts/crops_controller.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
module Charts
|
||||
class CropsController < ApplicationController
|
||||
respond_to :json
|
||||
|
||||
def sunniness
|
||||
pie_chart_query 'sunniness'
|
||||
end
|
||||
|
||||
def planted_from
|
||||
pie_chart_query 'planted_from'
|
||||
end
|
||||
|
||||
def harvested_for
|
||||
@crop = Crop.find(params[:crop_id])
|
||||
render json: Harvest.joins(:plant_part)
|
||||
.where(crop: @crop)
|
||||
.group("plant_parts.name").count(:id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pie_chart_query(field)
|
||||
@crop = Crop.find(params[:crop_id])
|
||||
render json: Planting.where(crop: @crop)
|
||||
.where.not(field.to_sym => nil)
|
||||
.where.not(field.to_sym => '')
|
||||
.group(field.to_sym).count(:id)
|
||||
end
|
||||
end
|
||||
end
|
||||
16
app/controllers/charts/gardens_controller.rb
Normal file
16
app/controllers/charts/gardens_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
module Charts
|
||||
class GardensController < ApplicationController
|
||||
respond_to :json
|
||||
def timeline
|
||||
@data = []
|
||||
@garden = Garden.find(params[:garden_id])
|
||||
@garden.plantings.where.not(planted_at: nil)
|
||||
.order(finished_at: :desc).each do |p|
|
||||
# use finished_at if we have it, otherwise use predictions
|
||||
finish = p.finished_at.presence || p.finish_predicted_at
|
||||
@data << [p.crop.name, p.planted_at, finish] if finish.present?
|
||||
end
|
||||
render json: @data
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,58 +1,44 @@
|
||||
class CommentsController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
respond_to :rss, only: :index
|
||||
responders :flash
|
||||
|
||||
# GET /comments
|
||||
# GET /comments.json
|
||||
# GET /comments.rss
|
||||
def index
|
||||
@comments = Comment.paginate(page: params[:page])
|
||||
@comments = Comment.order(created_at: :desc).paginate(page: params[:page])
|
||||
respond_with(@comments)
|
||||
end
|
||||
|
||||
# GET /comments/new
|
||||
# GET /comments/new.json
|
||||
def new
|
||||
@comment = Comment.new
|
||||
@post = Post.find_by(id: params[:post_id])
|
||||
|
||||
if @post
|
||||
@comments = @post.comments
|
||||
respond_with(@comment)
|
||||
respond_with(@comments)
|
||||
else
|
||||
redirect_to(request.referer || root_url,
|
||||
alert: "Can't post a comment on a non-existent post")
|
||||
end
|
||||
end
|
||||
|
||||
# GET /comments/1/edit
|
||||
def edit
|
||||
@comments = @comment.post.comments
|
||||
end
|
||||
|
||||
# POST /comments
|
||||
# POST /comments.json
|
||||
def create
|
||||
@comment = Comment.new(comment_params)
|
||||
@comment.author = current_member
|
||||
flash[:notice] = "Comment was successfully created." if @comment.save
|
||||
respond_with(@comment.post)
|
||||
@comment.save
|
||||
respond_with @comment, location: @comment.post
|
||||
end
|
||||
|
||||
# PUT /comments/1
|
||||
# PUT /comments/1.json
|
||||
def update
|
||||
# only body can be updated
|
||||
if @comment.update(body: comment_params['body'])
|
||||
flash[:notice] = 'Comment was successfully updated.'
|
||||
end
|
||||
respond_with(@comment.post)
|
||||
@comment.update(body: comment_params['body'])
|
||||
respond_with @comment, location: @comment.post
|
||||
end
|
||||
|
||||
# DELETE /comments/1
|
||||
# DELETE /comments/1.json
|
||||
def destroy
|
||||
@post = @comment.post
|
||||
@comment.destroy
|
||||
|
||||
@@ -1,43 +1,25 @@
|
||||
require 'will_paginate/array'
|
||||
|
||||
class CropsController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :hierarchy, :search, :show]
|
||||
before_action :authenticate_member!, except: %i(index hierarchy search show)
|
||||
load_and_authorize_resource
|
||||
skip_authorize_resource only: [:hierarchy, :search]
|
||||
skip_authorize_resource only: %i(hierarchy search)
|
||||
respond_to :html, :json, :rss, :csv
|
||||
responders :flash
|
||||
|
||||
# GET /crops
|
||||
# GET /crops.json
|
||||
def index
|
||||
@sort = params[:sort]
|
||||
@crops = if @sort == 'alpha'
|
||||
Crop.includes(:scientific_names, plantings: :photos)
|
||||
else
|
||||
popular_crops
|
||||
end
|
||||
@paginated_crops = @crops.approved.paginate(page: params[:page])
|
||||
|
||||
@has_requested_pending = Crop.pending_approval.where(requester: current_member).count if current_member
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render json: @crops }
|
||||
format.rss do
|
||||
@crops = Crop.recent.includes(:scientific_names, :creator)
|
||||
render rss: @crops
|
||||
end
|
||||
format.csv do
|
||||
@filename = "Growstuff-Crops-#{Time.zone.now.to_s(:number)}.csv"
|
||||
@crops = Crop.includes(:scientific_names, :plantings, :seeds, :creator)
|
||||
render csv: @crops
|
||||
end
|
||||
end
|
||||
@crops = crops
|
||||
@num_requested_crops = requested_crops.size if current_member
|
||||
@filename = filename
|
||||
respond_with @crops
|
||||
end
|
||||
|
||||
def requested
|
||||
@requested = Crop.pending_approval.where(requester: current_member).paginate(page: params[:page])
|
||||
@requested = requested_crops.paginate(page: params[:page])
|
||||
respond_with @requested
|
||||
end
|
||||
|
||||
# GET /crops/wrangle
|
||||
def wrangle
|
||||
@approval_status = params[:approval_status]
|
||||
@crops = case @approval_status
|
||||
@@ -47,176 +29,124 @@ class CropsController < ApplicationController
|
||||
Crop.rejected
|
||||
else
|
||||
Crop.recent
|
||||
end
|
||||
|
||||
@crops = @crops.paginate(page: params[:page])
|
||||
end.paginate(page: params[:page])
|
||||
|
||||
@crop_wranglers = Role.crop_wranglers
|
||||
respond_to do |format|
|
||||
format.html
|
||||
end
|
||||
respond_with @crops
|
||||
end
|
||||
|
||||
# GET /crops/hierarchy
|
||||
def hierarchy
|
||||
@crops = Crop.toplevel
|
||||
respond_to do |format|
|
||||
format.html
|
||||
end
|
||||
respond_with @crops
|
||||
end
|
||||
|
||||
# GET /crops/search
|
||||
def search
|
||||
@term = params[:term]
|
||||
@matches = Crop.search(@term)
|
||||
@paginated_matches = @matches.paginate(page: params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render json: @matches }
|
||||
end
|
||||
respond_with @matches
|
||||
end
|
||||
|
||||
# GET /crops/1
|
||||
# GET /crops/1.json
|
||||
def show
|
||||
@crop = Crop.includes(:scientific_names, plantings: :photos).find(params[:id])
|
||||
@posts = @crop.posts.paginate(page: params[:page])
|
||||
|
||||
@posts = @crop.posts.order(created_at: :desc).paginate(page: params[:page])
|
||||
# respond_with(@crop)
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json do
|
||||
# TODO RABL or similar one day to avoid presentation logic here
|
||||
owner_structure = {
|
||||
owner: {
|
||||
only: [:id, :login_name, :location, :latitude, :longitude]
|
||||
}
|
||||
}
|
||||
render json: @crop.to_json(include: {
|
||||
plantings: {
|
||||
include: owner_structure
|
||||
}
|
||||
})
|
||||
end
|
||||
format.html
|
||||
format.json { render json: @crop.to_json(crop_json_fields) }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /crops/new
|
||||
# GET /crops/new.json
|
||||
def new
|
||||
@crop = Crop.new
|
||||
@crop.alternate_names.build
|
||||
@crop.scientific_names.build
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.haml
|
||||
format.json { render json: @crop }
|
||||
end
|
||||
respond_with @crop
|
||||
end
|
||||
|
||||
# GET /crops/1/edit
|
||||
def edit
|
||||
@crop.alternate_names.build if @crop.alternate_names.blank?
|
||||
@crop.scientific_names.build if @crop.scientific_names.blank?
|
||||
end
|
||||
|
||||
# POST /crops
|
||||
# POST /crops.json
|
||||
def create
|
||||
@crop = Crop.new(crop_params)
|
||||
|
||||
if current_member.role? :crop_wrangler
|
||||
@crop.creator = current_member
|
||||
success_msg = "Crop was successfully created."
|
||||
else
|
||||
@crop.requester = current_member
|
||||
@crop.approval_status = "pending"
|
||||
success_msg = "Crop was successfully requested."
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.save
|
||||
params[:alt_name].each do |index, value|
|
||||
create_name('alternate', value)
|
||||
end
|
||||
params[:sci_name].each do |index, value|
|
||||
create_name('scientific', value)
|
||||
end
|
||||
unless current_member.role? :crop_wrangler
|
||||
Role.crop_wranglers.each do |w|
|
||||
Notifier.new_crop_request(w, @crop).deliver_now!
|
||||
end
|
||||
end
|
||||
notify_wranglers if Crop.transaction { @crop.save && save_crop_names }
|
||||
|
||||
format.html { redirect_to @crop, notice: success_msg }
|
||||
format.json { render json: @crop, status: :created, location: @crop }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @crop.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
respond_with @crop
|
||||
end
|
||||
|
||||
# PUT /crops/1
|
||||
# PUT /crops/1.json
|
||||
def update
|
||||
previous_status = @crop.approval_status
|
||||
|
||||
@crop.creator = current_member if previous_status == "pending"
|
||||
|
||||
respond_to do |format|
|
||||
if @crop.update(crop_params)
|
||||
recreate_names('alt_name', 'alternate')
|
||||
recreate_names('sci_name', 'scientific')
|
||||
if @crop.update(crop_params)
|
||||
recreate_names('alt_name', 'alternate')
|
||||
recreate_names('sci_name', 'scientific')
|
||||
|
||||
if previous_status == "pending"
|
||||
requester = @crop.requester
|
||||
new_status = @crop.approval_status
|
||||
Notifier.crop_request_approved(requester, @crop).deliver_now! if new_status == "approved"
|
||||
Notifier.crop_request_rejected(requester, @crop).deliver_now! if new_status == "rejected"
|
||||
end
|
||||
format.html { redirect_to @crop, notice: 'Crop was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
format.json { render json: @crop.errors, status: :unprocessable_entity }
|
||||
end
|
||||
notifier.deliver_now! if previous_status == "pending"
|
||||
end
|
||||
|
||||
respond_with @crop
|
||||
end
|
||||
|
||||
# DELETE /crops/1
|
||||
# DELETE /crops/1.json
|
||||
def destroy
|
||||
@crop.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to crops_url }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
respond_with @crop
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def popular_crops
|
||||
Crop.popular.includes(:scientific_names, plantings: :photos)
|
||||
def notifier
|
||||
case @crop.approval_status
|
||||
when "approved"
|
||||
Notifier.crop_request_approved(@crop.requester, @crop)
|
||||
when "rejected"
|
||||
Notifier.crop_request_rejected(@crop.requester, @crop)
|
||||
end
|
||||
end
|
||||
|
||||
def save_crop_names
|
||||
params[:alt_name]&.values&.each do |value|
|
||||
create_name!('alternate', value) unless value.empty?
|
||||
end
|
||||
params[:sci_name]&.values&.each do |value|
|
||||
create_name!('scientific', value) unless value.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def notify_wranglers
|
||||
return if current_member.role? :crop_wrangler
|
||||
Role.crop_wranglers.each do |w|
|
||||
Notifier.new_crop_request(w, @crop).deliver_now!
|
||||
end
|
||||
end
|
||||
|
||||
def recreate_names(param_name, name_type)
|
||||
return unless params[param_name].present?
|
||||
return if params[param_name].blank?
|
||||
destroy_names(name_type)
|
||||
params[param_name].each do |index, value|
|
||||
create_name(name_type, value)
|
||||
params[param_name].each do |_i, value|
|
||||
create_name!(name_type, value) unless value.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_names(name_type)
|
||||
@crop.send("#{name_type}_names").each do |alt_name|
|
||||
alt_name.destroy
|
||||
end
|
||||
@crop.send("#{name_type}_names").each(&:destroy)
|
||||
end
|
||||
|
||||
def create_name(name_type, value)
|
||||
@crop.send("#{name_type}_names").create(name: value, creator_id: current_member.id)
|
||||
def create_name!(name_type, value)
|
||||
@crop.send("#{name_type}_names").create!(name: value, creator_id: current_member.id)
|
||||
end
|
||||
|
||||
def crop_params
|
||||
@@ -224,11 +154,41 @@ class CropsController < ApplicationController
|
||||
:name,
|
||||
:parent_id,
|
||||
:creator_id,
|
||||
:perennial,
|
||||
:approval_status,
|
||||
:request_notes,
|
||||
:reason_for_rejection,
|
||||
:rejection_notes, scientific_names_attributes: [:scientific_name,
|
||||
:_destroy,
|
||||
:id])
|
||||
:rejection_notes,
|
||||
scientific_names_attributes: %i(scientific_name
|
||||
_destroy
|
||||
id))
|
||||
end
|
||||
|
||||
def filename
|
||||
"Growstuff-Crops-#{Time.zone.now.to_s(:number)}.csv"
|
||||
end
|
||||
|
||||
def crop_json_fields
|
||||
{
|
||||
include: {
|
||||
plantings: {
|
||||
include: {
|
||||
owner: { only: %i(id login_name location latitude longitude) }
|
||||
}
|
||||
},
|
||||
scientific_names: { only: [:name] },
|
||||
alternate_names: { only: [:name] }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def crops
|
||||
q = Crop.approved.includes(:scientific_names, plantings: :photos)
|
||||
q = q.popular unless @sort == 'alpha'
|
||||
q.order("LOWER(crops.name)").includes(:photos).paginate(page: params[:page])
|
||||
end
|
||||
|
||||
def requested_crops
|
||||
current_member.requested_crops.pending_approval
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ class ForumsController < ApplicationController
|
||||
# GET /forums
|
||||
# GET /forums.json
|
||||
def index
|
||||
@forums = Forum.all
|
||||
@forums = Forum.all.order(:name)
|
||||
respond_with(@forums)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class GardensController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
after_action :expire_homepage, only: [:create, :delete]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
after_action :expire_homepage, only: %i(create delete)
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
|
||||
@@ -10,13 +10,18 @@ class GardensController < ApplicationController
|
||||
@owner = Member.find_by(slug: params[:owner])
|
||||
@show_all = params[:all] == '1'
|
||||
@gardens = gardens
|
||||
|
||||
respond_with(@gardens)
|
||||
end
|
||||
|
||||
# GET /gardens/1
|
||||
# GET /gardens/1.json
|
||||
def show
|
||||
@current_plantings = @garden.plantings.current
|
||||
.includes(:crop, :owner)
|
||||
.order(planted_at: :desc)
|
||||
@finished_plantings = @garden.plantings.finished
|
||||
.includes(:crop)
|
||||
.order(finished_at: :desc)
|
||||
respond_with(@garden)
|
||||
end
|
||||
|
||||
@@ -63,6 +68,6 @@ class GardensController < ApplicationController
|
||||
def gardens
|
||||
g = @owner ? @owner.gardens : Garden.all
|
||||
g = g.active unless @show_all
|
||||
g.includes(:owner).order(:updated_at).paginate(page: params[:page])
|
||||
g.joins(:owner).order(:name).paginate(page: params[:page])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,60 +1,50 @@
|
||||
class HarvestsController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
after_action :update_crop_medians, only: %i(create update destroy)
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
respond_to :csv, only: :index
|
||||
responders :flash
|
||||
|
||||
# GET /harvests
|
||||
# GET /harvests.json
|
||||
def index
|
||||
@owner = Member.find_by(slug: params[:owner])
|
||||
@crop = Crop.find_by(slug: params[:crop])
|
||||
@planting = Planting.find_by(slug: params['planting_id'])
|
||||
@owner = Member.find_by(slug: params[:owner]) if params[:owner]
|
||||
@crop = Crop.find_by(slug: params[:crop]) if params[:crop]
|
||||
@planting = Planting.find_by(slug: params[:planting_id]) if params[:planting_id]
|
||||
|
||||
@harvests = harvests
|
||||
@filename = csv_filename
|
||||
respond_with(@harvests)
|
||||
end
|
||||
|
||||
# GET /harvests/1
|
||||
# GET /harvests/1.json
|
||||
def show
|
||||
@matching_plantings = matching_plantings if @harvest.owner == current_member
|
||||
@photos = @harvest.photos.order(created_at: :desc).paginate(page: params[:page])
|
||||
respond_with(@harvest)
|
||||
end
|
||||
|
||||
# GET /harvests/new
|
||||
# GET /harvests/new.json
|
||||
def new
|
||||
@harvest = Harvest.new(harvested_at: Time.zone.today)
|
||||
@planting = Planting.find_by(slug: params[:planting_id]) if params[:planting_id]
|
||||
# using find_by_id here because it returns nil, unlike find
|
||||
@crop = Crop.find_by(id: params[:crop_id])
|
||||
respond_with(@harvest)
|
||||
end
|
||||
|
||||
# GET /harvests/1/edit
|
||||
def edit
|
||||
@planting = @harvest.planting if @harvest.planting_id
|
||||
end
|
||||
|
||||
# POST /harvests
|
||||
# POST /harvests.json
|
||||
def create
|
||||
@harvest.crop_id = @harvest.planting.crop_id if @harvest.planting_id
|
||||
flash[:notice] = I18n.t('harvests.created') if @harvest.save
|
||||
@harvest.harvested_at = Time.zone.now if @harvest.harvested_at.blank?
|
||||
@harvest.save
|
||||
respond_with(@harvest)
|
||||
end
|
||||
|
||||
# PUT /harvests/1
|
||||
# PUT /harvests/1.json
|
||||
def update
|
||||
flash[:notice] = I18n.t('harvests.updated') if @harvest.update(harvest_params)
|
||||
@harvest.update(harvest_params)
|
||||
respond_with(@harvest)
|
||||
end
|
||||
|
||||
# DELETE /harvests/1
|
||||
# DELETE /harvests/1.json
|
||||
def destroy
|
||||
@harvest.destroy
|
||||
respond_with(@harvest)
|
||||
@@ -81,11 +71,11 @@ class HarvestsController < ApplicationController
|
||||
@owner.harvests
|
||||
elsif @crop
|
||||
@crop.harvests
|
||||
elsif @planting_id
|
||||
elsif @planting
|
||||
@planting.harvests
|
||||
else
|
||||
Harvest
|
||||
end.includes(:owner, :crop).paginate(page: params[:page])
|
||||
Harvest.all
|
||||
end.order(harvested_at: :desc).joins(:owner, :crop).paginate(page: params[:page])
|
||||
end
|
||||
|
||||
def csv_filename
|
||||
@@ -96,4 +86,13 @@ class HarvestsController < ApplicationController
|
||||
end
|
||||
"Growstuff-#{specifics}Harvests-#{Time.zone.now.to_s(:number)}.csv"
|
||||
end
|
||||
|
||||
def update_crop_medians
|
||||
# We only update medians to predict plantings
|
||||
# if this harvest is not linked to a planting, then do nothing
|
||||
return if @harvest.planting.nil?
|
||||
|
||||
@harvest.planting.update_harvest_days
|
||||
@harvest.crop.update_harvest_medians
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
class HomeController < ApplicationController
|
||||
skip_authorize_resource
|
||||
respond_to :html
|
||||
|
||||
def index
|
||||
# we were previously generating a lot of instance variables like
|
||||
# @members_count and @interesting_crops in here, but now we call
|
||||
# the relevant class methods directly in the view, so that fragment
|
||||
# caching will be effective.
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
class MembersController < ApplicationController
|
||||
load_and_authorize_resource except: [:finish_signup, :unsubscribe, :view_follows, :view_followers, :show]
|
||||
skip_authorize_resource only: [:nearby, :unsubscribe, :finish_signup]
|
||||
|
||||
after_action :expire_cache_fragments, only: :create
|
||||
load_and_authorize_resource except: %i(finish_signup unsubscribe view_follows view_followers show)
|
||||
skip_authorize_resource only: %i(nearby unsubscribe finish_signup)
|
||||
respond_to :html, :json, :rss
|
||||
after_action :expire_homepage, only: :create
|
||||
|
||||
def index
|
||||
@sort = params[:sort]
|
||||
@members = if @sort == 'recently_joined'
|
||||
Member.confirmed.recently_joined.paginate(page: params[:page])
|
||||
else
|
||||
Member.confirmed.paginate(page: params[:page])
|
||||
end
|
||||
|
||||
@members = members
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
format.json {
|
||||
render json: @members.to_json(only: [
|
||||
:id, :login_name,
|
||||
:slug, :bio, :created_at,
|
||||
:location, :latitude, :longitude
|
||||
])
|
||||
}
|
||||
format.json { render json: @members.to_json(only: member_json_fields) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,6 +20,7 @@ class MembersController < ApplicationController
|
||||
@facebook_auth = @member.auth('facebook')
|
||||
@posts = @member.posts
|
||||
@gardens = @member.gardens.active.order(:name)
|
||||
|
||||
# The garden form partial is called from the "New Garden" tab;
|
||||
# it requires a garden to be passed in @garden.
|
||||
# The new garden is not persisted unless Garden#save is called.
|
||||
@@ -38,18 +28,13 @@ class MembersController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json {
|
||||
render json: @member.to_json(only: [
|
||||
:id, :login_name, :bio,
|
||||
:created_at, :slug, :location,
|
||||
:latitude, :longitude
|
||||
])
|
||||
}
|
||||
format.rss {
|
||||
format.json { render json: @member.to_json(only: member_json_fields) }
|
||||
format.rss do
|
||||
render(
|
||||
layout: false,
|
||||
locals: { member: @member }
|
||||
)}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,7 +51,7 @@ class MembersController < ApplicationController
|
||||
EMAIL_TYPE_STRING = {
|
||||
send_notification_email: "direct message notifications",
|
||||
send_planting_reminder: "planting reminders"
|
||||
}
|
||||
}.freeze
|
||||
|
||||
def unsubscribe
|
||||
verifier = ActiveSupport::MessageVerifier.new(ENV['RAILS_SECRET_TOKEN'])
|
||||
@@ -77,7 +62,6 @@ class MembersController < ApplicationController
|
||||
@member.update(@type => false)
|
||||
|
||||
flash.now[:notice] = I18n.t('members.unsubscribed', email_type: EMAIL_TYPE_STRING[@type])
|
||||
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
flash.now[:alert] = I18n.t('members.unsubscribe.error')
|
||||
end
|
||||
@@ -98,11 +82,23 @@ class MembersController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def expire_cache_fragments
|
||||
expire_fragment("homepage_stats")
|
||||
end
|
||||
|
||||
def member_params
|
||||
params.require(:member).permit(:login_name, :tos_agreement, :email, :newsletter)
|
||||
end
|
||||
|
||||
def member_json_fields
|
||||
%i(
|
||||
id login_name
|
||||
slug bio created_at
|
||||
location latitude longitude
|
||||
)
|
||||
end
|
||||
|
||||
def members
|
||||
if @sort == 'recently_joined'
|
||||
Member.recently_joined
|
||||
else
|
||||
Member.order(:login_name)
|
||||
end.confirmed.paginate(page: params[:page])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ class NotificationsController < ApplicationController
|
||||
|
||||
# GET /notifications
|
||||
def index
|
||||
@notifications = Notification.by_recipient(current_member).page(params[:page])
|
||||
@notifications = Notification.by_recipient(current_member).order(:created_at).page(params[:page])
|
||||
end
|
||||
|
||||
# GET /notifications/1
|
||||
@@ -31,9 +31,11 @@ class NotificationsController < ApplicationController
|
||||
@sender_notification.read = true
|
||||
@sender_notification.save
|
||||
@recipient = @sender_notification.sender
|
||||
@subject = @sender_notification.subject =~ /^Re: / ?
|
||||
@sender_notification.subject :
|
||||
"Re: " + @sender_notification.subject
|
||||
@subject = if @sender_notification.subject.start_with? 'Re: '
|
||||
@sender_notification.subject
|
||||
else
|
||||
"Re: #{@sender_notification.subject}"
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /notifications/1
|
||||
|
||||
@@ -29,15 +29,15 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
member = action.find_or_create_from_authorization(auth)
|
||||
@authentication = action.establish_authentication(auth, member)
|
||||
|
||||
unless action.member_created?
|
||||
sign_in_and_redirect member, event: :authentication # this will throw if @user is not activated
|
||||
set_flash_message(:notice, :success, kind: auth['provider']) if is_navigational_format?
|
||||
else
|
||||
raise "Invalid provider" unless ['facebook', 'twitter', 'flickr'].index(auth['provider'].to_s)
|
||||
if action.member_created?
|
||||
raise "Invalid provider" unless %w(facebook twitter flickr).index(auth['provider'].to_s)
|
||||
|
||||
session["devise.#{auth['provider']}_data"] = request.env["omniauth.auth"]
|
||||
sign_in member
|
||||
redirect_to finish_signup_url(member)
|
||||
else
|
||||
sign_in_and_redirect member, event: :authentication # this will throw if @user is not activated
|
||||
set_flash_message(:notice, :success, kind: auth['provider']) if is_navigational_format?
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
class OrderItemsController < ApplicationController
|
||||
before_action :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# POST /order_items
|
||||
def create
|
||||
if params[:order_item][:price]
|
||||
params[:order_item][:price] = params[:order_item][:price].to_f * 100 # convert to cents
|
||||
end
|
||||
@order_item = OrderItem.new(order_item_params)
|
||||
@order_item.order = current_member.current_order || Order.create(member_id: current_member.id)
|
||||
|
||||
respond_to do |format|
|
||||
if @order_item.save
|
||||
format.html { redirect_to @order_item.order, notice: 'Added item to your order.' }
|
||||
else
|
||||
errors = @order_item.errors.empty? ?
|
||||
"There was a problem with your order." : @order_item.errors.full_messages.to_sentence
|
||||
format.html { redirect_to shop_path, alert: errors }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def order_item_params
|
||||
params.require(:order_item).permit(:order_id, :price, :product_id, :quantity)
|
||||
end
|
||||
end
|
||||
@@ -1,90 +0,0 @@
|
||||
class OrdersController < ApplicationController
|
||||
before_action :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /orders
|
||||
def index
|
||||
@orders = Order.by_member(current_member)
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
end
|
||||
end
|
||||
|
||||
# GET /orders/1
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
end
|
||||
end
|
||||
|
||||
# GET /orders/new
|
||||
def new
|
||||
@order = Order.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
end
|
||||
end
|
||||
|
||||
# checkout with PayPal
|
||||
def checkout
|
||||
respond_to do |format|
|
||||
if @order.update_attributes(referral_code: params[:referral_code])
|
||||
response = EXPRESS_GATEWAY.setup_purchase(
|
||||
@order.total,
|
||||
items: @order.activemerchant_items,
|
||||
currency: Growstuff::Application.config.currency,
|
||||
no_shipping: true,
|
||||
ip: request.remote_ip,
|
||||
return_url: complete_order_url,
|
||||
cancel_return_url: shop_url
|
||||
)
|
||||
format.html { redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token) }
|
||||
else
|
||||
format.html { render action: "show" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def complete
|
||||
if params[:token] && params['PayerID']
|
||||
purchase = EXPRESS_GATEWAY.purchase(
|
||||
@order.total,
|
||||
currency: Growstuff::Application.config.currency,
|
||||
ip: request.remote_ip,
|
||||
payer_id: params['PayerID'],
|
||||
token: params[:token]
|
||||
)
|
||||
if purchase.success?
|
||||
@order.completed_at = Time.zone.now
|
||||
@order.record_paypal_details(params[:token])
|
||||
else
|
||||
flash[:alert] = "Could not complete your order. Please notify support."
|
||||
end
|
||||
else
|
||||
flash[:alert] = "PayPal didn't return a token or payer_id for your order. Please notify support."
|
||||
end
|
||||
|
||||
@order.update_account # apply paid account benefits, etc.
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
end
|
||||
end
|
||||
|
||||
def cancel
|
||||
respond_to do |format|
|
||||
format.html { redirect_to shop_url, notice: 'Order was cancelled.' }
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /orders/1
|
||||
def destroy
|
||||
@order.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to shop_url, notice: 'Order was deleted.' }
|
||||
end
|
||||
end
|
||||
end
|
||||
21
app/controllers/photo_associations_controller.rb
Normal file
21
app/controllers/photo_associations_controller.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
class PhotoAssociationsController < ApplicationController
|
||||
before_action :authenticate_member!
|
||||
respond_to :json, :html
|
||||
|
||||
def destroy
|
||||
raise "Photos not supported" unless Photo::PHOTO_CAPABLE.include? item_class
|
||||
@photo = Photo.find_by!(id: params[:photo_id], owner: current_member)
|
||||
@item = Photographing.item(item_id, item_class)
|
||||
@item.photos.delete(@photo)
|
||||
# @photo.destroy_if_unused
|
||||
respond_with(@photo)
|
||||
end
|
||||
|
||||
def item_class
|
||||
params[:type].capitalize
|
||||
end
|
||||
|
||||
def item_id
|
||||
params[:id]
|
||||
end
|
||||
end
|
||||
@@ -1,59 +1,67 @@
|
||||
class PhotosController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
after_action :expire_homepage, only: [:create, :delete]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
after_action :expire_homepage, only: %i(create delete)
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
responders :flash
|
||||
|
||||
# GET /photos
|
||||
# GET /photos.json
|
||||
def index
|
||||
@photos = Photo.paginate(page: params[:page])
|
||||
if params[:crop_id]
|
||||
@crop = Crop.find params[:crop_id]
|
||||
@photos = @crop.photos
|
||||
else
|
||||
@photos = Photo.all
|
||||
end
|
||||
@photos = @photos.order(created_at: :desc)
|
||||
.includes(:owner)
|
||||
.paginate(page: params[:page])
|
||||
respond_with(@photos)
|
||||
end
|
||||
|
||||
# GET /photos/new
|
||||
# GET /photos/new.json
|
||||
def new
|
||||
@type = params[:type]
|
||||
@id = params[:id]
|
||||
|
||||
@photo = Photo.new
|
||||
@item = item_to_link_to
|
||||
@type = item_type
|
||||
@id = item_id
|
||||
retrieve_from_flickr
|
||||
respond_with(@photo)
|
||||
respond_with @photo
|
||||
end
|
||||
|
||||
# GET /photos/1/edit
|
||||
def edit
|
||||
respond_with @photo
|
||||
end
|
||||
|
||||
# POST /photos
|
||||
# POST /photos.json
|
||||
def create
|
||||
find_or_create_photo_from_flickr_photo
|
||||
add_photo_to_collection
|
||||
flash[:notice] = 'Photo was successfully added.' if @photo.present? && @photo.save
|
||||
respond_with(@photo)
|
||||
ActiveRecord::Base.transaction do
|
||||
@photo = find_or_create_photo_from_flickr_photo
|
||||
@item = item_to_link_to
|
||||
raise "Could not find this #{type} owned by you" unless @item
|
||||
@item.photos << @photo unless @item.photos.include? @photo
|
||||
@photo.save! if @photo.present?
|
||||
end
|
||||
respond_with @photo
|
||||
end
|
||||
|
||||
# PUT /photos/1
|
||||
# PUT /photos/1.json
|
||||
def update
|
||||
flash[:notice] = 'Photo was successfully updated.' if @photo.update(photo_params)
|
||||
respond_with(@photo)
|
||||
@photo.update(photo_params)
|
||||
respond_with @photo
|
||||
end
|
||||
|
||||
# DELETE /photos/1
|
||||
# DELETE /photos/1.json
|
||||
def destroy
|
||||
@photo.destroy
|
||||
flash[:alert] = "Photo successfully deleted."
|
||||
respond_with(@photo)
|
||||
respond_with @photo
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def item_id?
|
||||
params.key? :id
|
||||
#
|
||||
# Params
|
||||
def item_id
|
||||
params[:id]
|
||||
end
|
||||
|
||||
def item_type
|
||||
params[:type]
|
||||
end
|
||||
|
||||
def flickr_photo_id_param
|
||||
@@ -65,26 +73,23 @@ class PhotosController < ApplicationController
|
||||
:license_url, :thumbnail_url, :fullsize_url, :link_url)
|
||||
end
|
||||
|
||||
def find_or_create_photo_from_flickr_photo
|
||||
@photo = Photo.find_by(flickr_photo_id: flickr_photo_id_param)
|
||||
@photo = Photo.new(photo_params) unless @photo
|
||||
@photo.owner_id = current_member.id
|
||||
@photo.set_flickr_metadata
|
||||
@photo
|
||||
# Item with photos attached
|
||||
def item_to_link_to
|
||||
raise "No item id provided" if item_id.nil?
|
||||
raise "No item type provided" if item_type.nil?
|
||||
item_class = item_type.capitalize
|
||||
raise "Photos not supported" unless Photo::PHOTO_CAPABLE.include? item_class
|
||||
item_class.constantize.find_by!(id: params[:id], owner_id: current_member.id)
|
||||
end
|
||||
|
||||
def add_photo_to_collection
|
||||
raise "Missing or invalid type provided" unless Growstuff::Constants::PhotoModels.types.include?(params[:type])
|
||||
raise "No item id provided" unless item_id?
|
||||
collection = Growstuff::Constants::PhotoModels.get_relation(@photo, params[:type])
|
||||
|
||||
item_class = Growstuff::Constants::PhotoModels.get_item(params[:type])
|
||||
item = item_class.find_by!(id: params[:id], owner_id: current_member.id)
|
||||
raise "Could not find this item owned by you" unless item
|
||||
|
||||
collection << item unless collection.include?(item)
|
||||
rescue => e
|
||||
flash[:alert] = e.message
|
||||
#
|
||||
# Flickr retrieval
|
||||
def find_or_create_photo_from_flickr_photo
|
||||
photo = Photo.find_by(flickr_photo_id: flickr_photo_id_param)
|
||||
photo ||= Photo.new(photo_params)
|
||||
photo.owner_id = current_member.id
|
||||
photo.set_flickr_metadata!
|
||||
photo
|
||||
end
|
||||
|
||||
def retrieve_from_flickr
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
class PlacesController < ApplicationController
|
||||
skip_authorize_resource
|
||||
respond_to :html, :json
|
||||
|
||||
def index
|
||||
respond_to do |format|
|
||||
format.html
|
||||
# json response is whatever we want to map here
|
||||
format.json do
|
||||
render json: Member.located.to_json(only: [
|
||||
:id, :login_name, :slug, :location, :latitude, :longitude
|
||||
])
|
||||
render json: Member.located.to_json(only: %i(
|
||||
id login_name slug location latitude longitude
|
||||
))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -21,26 +22,18 @@ class PlacesController < ApplicationController
|
||||
respond_to do |format|
|
||||
format.html # show.html.haml
|
||||
format.json do
|
||||
render json: @nearby_members.to_json(only: [
|
||||
:id, :login_name, :slug, :location, :latitude, :longitude
|
||||
])
|
||||
render json: @nearby_members.to_json(only: %i(
|
||||
id login_name slug location latitude longitude
|
||||
))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
if params[:new_place].empty?
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to places_path, alert: 'Please enter a valid location'
|
||||
end
|
||||
end
|
||||
redirect_to places_path, alert: 'Please enter a valid location'
|
||||
else
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to place_path(params[:new_place])
|
||||
end
|
||||
end
|
||||
redirect_to place_path(params[:new_place])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,70 +1,37 @@
|
||||
class PlantPartsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
responders :flash
|
||||
|
||||
# GET /plant_parts
|
||||
# GET /plant_parts.json
|
||||
def index
|
||||
@plant_parts = PlantPart.all
|
||||
@plant_parts = PlantPart.all.order(:name)
|
||||
respond_with(@plant_parts)
|
||||
end
|
||||
|
||||
# GET /plant_parts/1
|
||||
# GET /plant_parts/1.json
|
||||
def show
|
||||
respond_with(@plant_part)
|
||||
end
|
||||
|
||||
# GET /plant_parts/new
|
||||
# GET /plant_parts/new.json
|
||||
def new
|
||||
@plant_part = PlantPart.new
|
||||
respond_with(@plant_part)
|
||||
end
|
||||
|
||||
# GET /plant_parts/1/edit
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
# POST /plant_parts
|
||||
# POST /plant_parts.json
|
||||
def create
|
||||
@plant_part = PlantPart.new(plant_part_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @plant_part.save
|
||||
format.html { redirect_to @plant_part, notice: 'Plant part was successfully created.' }
|
||||
format.json { render json: @plant_part, status: :created, location: @plant_part }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @plant_part.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
@plant_part = PlantPart.create(plant_part_params)
|
||||
respond_with(@plant_part)
|
||||
end
|
||||
|
||||
# PUT /plant_parts/1
|
||||
# PUT /plant_parts/1.json
|
||||
def update
|
||||
respond_to do |format|
|
||||
if @plant_part.update(plant_part_params)
|
||||
format.html { redirect_to @plant_part, notice: 'Plant part was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
format.json { render json: @plant_part.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
@plant_part.update(plant_part_params)
|
||||
respond_with(@plant_part)
|
||||
end
|
||||
|
||||
# DELETE /plant_parts/1
|
||||
# DELETE /plant_parts/1.json
|
||||
def destroy
|
||||
@plant_part.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to plant_parts_url }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
respond_with(@plant_part)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,126 +1,93 @@
|
||||
class PlantingsController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
after_action :expire_homepage, only: %i(create update destroy)
|
||||
after_action :update_crop_medians, only: %i(create update destroy)
|
||||
after_action :update_planting_medians, only: :update
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /plantings
|
||||
# GET /plantings.json
|
||||
respond_to :html, :json
|
||||
respond_to :csv, :rss, only: [:index]
|
||||
responders :flash
|
||||
|
||||
def index
|
||||
@owner = Member.find_by(slug: params[:owner]) if params[:owner]
|
||||
@crop = Crop.find_by(slug: params[:crop]) if params[:crop]
|
||||
@show_all = params[:all] == '1'
|
||||
|
||||
@plantings = plantings
|
||||
|
||||
respond_to do |format|
|
||||
format.html { @plantings = @plantings.paginate(page: params[:page]) }
|
||||
format.json { render json: @plantings }
|
||||
format.rss { render layout: false } # index.rss.builder
|
||||
format.csv do
|
||||
specifics = (@owner ? "#{@owner.login_name}-" : @crop ? "#{@crop.name}-" : nil)
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
render csv: @plantings
|
||||
end
|
||||
end
|
||||
specifics = if @owner
|
||||
"#{@owner.login_name}-"
|
||||
elsif @crop
|
||||
"#{@crop.name}-"
|
||||
end
|
||||
|
||||
@filename = "Growstuff-#{specifics}Plantings-#{Time.zone.now.to_s(:number)}.csv"
|
||||
|
||||
respond_with(@plantings)
|
||||
end
|
||||
|
||||
# GET /plantings/1
|
||||
# GET /plantings/1.json
|
||||
def show
|
||||
@planting = Planting.includes(:owner, :crop, :garden, :photos).friendly.find(params[:id])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
format.json { render json: @planting }
|
||||
end
|
||||
@planting = Planting.includes(:owner, :crop, :garden, :photos)
|
||||
.friendly
|
||||
.find(params[:id])
|
||||
@photos = @planting.photos.order(date_taken: :desc).includes(:owner).paginate(page: params[:page])
|
||||
respond_with @planting
|
||||
end
|
||||
|
||||
# GET /plantings/new
|
||||
# GET /plantings/new.json
|
||||
def new
|
||||
@planting = Planting.new('planted_at' => Time.zone.today)
|
||||
@planting = Planting.new(planted_at: Time.zone.today)
|
||||
@seed = Seed.find_by(slug: params[:seed_id]) if params[:seed_id]
|
||||
|
||||
# using find_by_id here because it returns nil, unlike find
|
||||
@crop = Crop.approved.find_by(id: params[:crop_id]) || Crop.new
|
||||
@garden = Garden.find_by(owner: current_member, id: params[:garden_id]) || Garden.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
format.json { render json: @planting }
|
||||
end
|
||||
respond_with @planting
|
||||
end
|
||||
|
||||
# GET /plantings/1/edit
|
||||
def edit
|
||||
# the following are needed to display the form but aren't used
|
||||
@crop = Crop.new
|
||||
@garden = Garden.new
|
||||
end
|
||||
|
||||
# POST /plantings
|
||||
# POST /plantings.json
|
||||
def create
|
||||
params[:planted_at] = parse_date(params[:planted_at])
|
||||
@planting = Planting.new(planting_params)
|
||||
@planting.owner = current_member
|
||||
|
||||
respond_to do |format|
|
||||
if @planting.save
|
||||
@planting.update_attribute(:days_before_maturity,
|
||||
update_days_before_maturity(@planting, planting_params[:crop_id]))
|
||||
format.html { redirect_to @planting, notice: 'Planting was successfully created.' }
|
||||
format.json { render json: @planting, status: :created, location: @planting }
|
||||
expire_fragment("homepage_stats")
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
format.json { render json: @planting.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
@planting.crop = @planting.parent_seed.crop if @planting.parent_seed.present?
|
||||
@planting.save!
|
||||
respond_with @planting
|
||||
end
|
||||
|
||||
# PUT /plantings/1
|
||||
# PUT /plantings/1.json
|
||||
def update
|
||||
params[:planted_at] = parse_date(params[:planted_at])
|
||||
|
||||
respond_to do |format|
|
||||
if @planting.update(planting_params)
|
||||
@planting.update_attribute(:days_before_maturity,
|
||||
update_days_before_maturity(@planting, planting_params[:crop_id]))
|
||||
format.html { redirect_to @planting, notice: 'Planting was successfully updated.' }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
format.json { render json: @planting.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
@planting.update(planting_params)
|
||||
respond_with @planting
|
||||
end
|
||||
|
||||
# DELETE /plantings/1
|
||||
# DELETE /plantings/1.json
|
||||
def destroy
|
||||
@garden = @planting.garden
|
||||
@planting.destroy
|
||||
expire_fragment("homepage_stats")
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to @garden }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
respond_with @planting, location: @planting.garden
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def planting_params
|
||||
params.require(:planting).permit(:crop_id, :description, :garden_id, :planted_at,
|
||||
:quantity, :sunniness, :planted_from, :owner_id, :finished,
|
||||
:finished_at)
|
||||
def update_crop_medians
|
||||
@planting.crop.update_lifespan_medians
|
||||
end
|
||||
|
||||
def update_days_before_maturity(planting, crop_id)
|
||||
if planting.finished_at.nil?
|
||||
planting.calculate_days_before_maturity(planting, crop_id)
|
||||
else
|
||||
(planting.finished_at - planting.planted_at).to_i
|
||||
end
|
||||
def update_planting_medians
|
||||
@planting.update_harvest_days
|
||||
end
|
||||
|
||||
def planting_params
|
||||
params[:planted_at] = parse_date(params[:planted_at]) if params[:planted_at]
|
||||
params.require(:planting).permit(
|
||||
:crop_id, :description, :garden_id, :planted_at,
|
||||
:parent_seed_id,
|
||||
:quantity, :sunniness, :planted_from, :finished,
|
||||
:finished_at
|
||||
)
|
||||
end
|
||||
|
||||
def plantings
|
||||
@@ -132,6 +99,9 @@ class PlantingsController < ApplicationController
|
||||
Planting
|
||||
end
|
||||
p = p.current unless @show_all
|
||||
p.includes(:owner, :crop, :garden).order(:created_at).paginate(page: params[:page])
|
||||
p.joins(:owner, :crop, :garden)
|
||||
.order(created_at: :desc)
|
||||
.includes(:crop, :owner, :garden)
|
||||
.paginate(page: params[:page])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
class PostsController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
respond_to :rss, only: [:index, :show]
|
||||
respond_to :rss, only: %i(index show)
|
||||
|
||||
# GET /posts
|
||||
# GET /posts.json
|
||||
@@ -30,8 +30,7 @@ class PostsController < ApplicationController
|
||||
end
|
||||
|
||||
# GET /posts/1/edit
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
# POST /posts
|
||||
# POST /posts.json
|
||||
@@ -67,6 +66,6 @@ class PostsController < ApplicationController
|
||||
@author.posts
|
||||
else
|
||||
Post
|
||||
end.includes(:author, comments: :author).paginate(page: params[:page])
|
||||
end.order(created_at: :desc).includes(:author, comments: :author).paginate(page: params[:page])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
class ProductsController < ApplicationController
|
||||
before_action :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
|
||||
# GET /products
|
||||
def index
|
||||
@products = Product.all
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
end
|
||||
end
|
||||
|
||||
# GET /products/1
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
end
|
||||
end
|
||||
|
||||
# GET /products/new
|
||||
def new
|
||||
@product = Product.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
end
|
||||
end
|
||||
|
||||
# GET /products/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# POST /products
|
||||
def create
|
||||
@product = Product.new(product_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @product.save
|
||||
format.html { redirect_to @product, notice: 'Product was successfully created.' }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PUT /products/1
|
||||
def update
|
||||
respond_to do |format|
|
||||
if @product.update(product_params)
|
||||
format.html { redirect_to @product, notice: 'Product was successfully updated.' }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /products/1
|
||||
def destroy
|
||||
@product.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to products_url }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def product_params
|
||||
params.require(:product).permit(:description, :min_price, :recommended_price, :name,
|
||||
:account_type_id, :paid_months)
|
||||
end
|
||||
end
|
||||
@@ -35,6 +35,14 @@ class RegistrationsController < Devise::RegistrationsController
|
||||
render "edit"
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @member.destroy_with_password(params.require(:member)[:current_password])
|
||||
redirect_to root_path
|
||||
else
|
||||
render "edit"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# check if we need the current password to update fields
|
||||
|
||||
@@ -1,67 +1,40 @@
|
||||
class RolesController < ApplicationController
|
||||
before_action :authenticate_member!
|
||||
load_and_authorize_resource
|
||||
respond_to :html
|
||||
responders :flash
|
||||
|
||||
# GET /roles
|
||||
def index
|
||||
@roles = Role.all
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.erb
|
||||
end
|
||||
@roles = Role.all.order(:name)
|
||||
respond_with @roles
|
||||
end
|
||||
|
||||
# GET /roles/1
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
end
|
||||
respond_with @role
|
||||
end
|
||||
|
||||
# GET /roles/new
|
||||
def new
|
||||
@role = Role.new
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
end
|
||||
respond_with @role
|
||||
end
|
||||
|
||||
# GET /roles/1/edit
|
||||
def edit
|
||||
respond_with @role
|
||||
end
|
||||
|
||||
# POST /roles
|
||||
def create
|
||||
@role = Role.new(role_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @role.save
|
||||
format.html { redirect_to @role, notice: 'Role was successfully created.' }
|
||||
else
|
||||
format.html { render action: "new" }
|
||||
end
|
||||
end
|
||||
@role = Role.create(role_params)
|
||||
respond_with @role
|
||||
end
|
||||
|
||||
# PUT /roles/1
|
||||
def update
|
||||
respond_to do |format|
|
||||
if @role.update(role_params)
|
||||
format.html { redirect_to @role, notice: 'Role was successfully updated.' }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
end
|
||||
end
|
||||
@role.update(role_params)
|
||||
respond_with @role
|
||||
end
|
||||
|
||||
# DELETE /roles/1
|
||||
def destroy
|
||||
@role.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to roles_url }
|
||||
end
|
||||
respond_with @role
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
class ScientificNamesController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
responders :flash
|
||||
|
||||
# GET /scientific_names
|
||||
# GET /scientific_names.json
|
||||
def index
|
||||
@scientific_names = ScientificName.all
|
||||
@scientific_names = ScientificName.all.order(:name)
|
||||
respond_with(@scientific_names)
|
||||
end
|
||||
|
||||
@@ -25,8 +26,7 @@ class ScientificNamesController < ApplicationController
|
||||
end
|
||||
|
||||
# GET /scientific_names/1/edit
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
# POST /scientific_names
|
||||
# POST /scientific_names.json
|
||||
@@ -34,14 +34,14 @@ class ScientificNamesController < ApplicationController
|
||||
@scientific_name = ScientificName.new(scientific_name_params)
|
||||
@scientific_name.creator = current_member
|
||||
|
||||
flash[:notice] = 'Scientific name was successfully created.' if @scientific_name.save
|
||||
@scientific_name.save
|
||||
respond_with(@scientific_name.crop)
|
||||
end
|
||||
|
||||
# PUT /scientific_names/1
|
||||
# PUT /scientific_names/1.json
|
||||
def update
|
||||
flash[:notice] = 'Scientific name was successfully updated.' if @scientific_name.update(scientific_name_params)
|
||||
@scientific_name.update(scientific_name_params)
|
||||
respond_with(@scientific_name.crop)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class SeedsController < ApplicationController
|
||||
before_action :authenticate_member!, except: [:index, :show]
|
||||
before_action :authenticate_member!, except: %i(index show)
|
||||
load_and_authorize_resource
|
||||
respond_to :html, :json
|
||||
respond_to :csv, only: :index
|
||||
@@ -16,44 +16,37 @@ class SeedsController < ApplicationController
|
||||
respond_with(@seeds)
|
||||
end
|
||||
|
||||
# GET /seeds/1
|
||||
# GET /seeds/1.json
|
||||
def show
|
||||
@photos = @seed.photos.includes(:owner).order(created_at: :desc).paginate(page: params[:page])
|
||||
respond_with(@seed)
|
||||
end
|
||||
|
||||
# GET /seeds/new
|
||||
# GET /seeds/new.json
|
||||
def new
|
||||
@seed = Seed.new
|
||||
|
||||
# using find_by_id here because it returns nil, unlike find
|
||||
@crop = Crop.find_or_initialize_by(id: params[:crop_id])
|
||||
if params[:planting_id]
|
||||
@planting = Planting.find_by(slug: params[:planting_id])
|
||||
else
|
||||
@crop = Crop.find_or_initialize_by(id: params[:crop_id])
|
||||
end
|
||||
respond_with(@seed)
|
||||
end
|
||||
|
||||
# GET /seeds/1/edit
|
||||
def edit
|
||||
end
|
||||
def edit; end
|
||||
|
||||
# POST /seeds
|
||||
# POST /seeds.json
|
||||
def create
|
||||
@seed = Seed.new(seed_params)
|
||||
@seed.owner = current_member
|
||||
@seed.crop = @seed.parent_planting.crop if @seed.parent_planting
|
||||
flash[:notice] = "Successfully added #{@seed.crop} seed to your stash." if @seed.save
|
||||
respond_with(@seed)
|
||||
end
|
||||
|
||||
# PUT /seeds/1
|
||||
# PUT /seeds/1.json
|
||||
def update
|
||||
flash[:notice] = 'Seed was successfully updated.' if @seed.update(seed_params)
|
||||
respond_with(@seed)
|
||||
end
|
||||
|
||||
# DELETE /seeds/1
|
||||
# DELETE /seeds/1.json
|
||||
def destroy
|
||||
@seed.destroy
|
||||
respond_with(@seed)
|
||||
@@ -64,8 +57,11 @@ class SeedsController < ApplicationController
|
||||
def seed_params
|
||||
params.require(:seed).permit(
|
||||
:crop_id, :description, :quantity, :plant_before,
|
||||
:days_until_maturity_min, :days_until_maturity_max, :organic, :gmo,
|
||||
:heirloom, :tradable_to, :slug
|
||||
:parent_planting_id,
|
||||
:days_until_maturity_min, :days_until_maturity_max,
|
||||
:organic, :gmo,
|
||||
:heirloom, :tradable_to, :slug,
|
||||
:finished, :finished_at
|
||||
)
|
||||
end
|
||||
|
||||
@@ -76,7 +72,7 @@ class SeedsController < ApplicationController
|
||||
crop.seeds
|
||||
else
|
||||
Seed
|
||||
end.includes(:owner, :crop).paginate(page: params[:page])
|
||||
end.order(created_at: :desc).includes(:owner, :crop).paginate(page: params[:page])
|
||||
end
|
||||
|
||||
def csv_filename
|
||||
|
||||
@@ -2,7 +2,7 @@ class SessionsController < Devise::SessionsController
|
||||
respond_to :json
|
||||
|
||||
def create
|
||||
super do |resource|
|
||||
super do |_resource|
|
||||
if Crop.pending_approval.present? && current_member.role?(:crop_wrangler)
|
||||
flash[:alert] = "There are crops waiting to be wrangled."
|
||||
end
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
class ShopController < ApplicationController
|
||||
def index
|
||||
@products = Product.all
|
||||
@order_item = OrderItem.new
|
||||
|
||||
# this is (hopefully) part of a short-term hack to prevent people from
|
||||
# ordering multiple subscriptions, which would be very confusing to deal
|
||||
# with. We check whether they have an order already in progress, and if
|
||||
# so, point that out to them and encourage them to checkout, rather than
|
||||
# letting them add more stuff to their order.
|
||||
|
||||
@order = nil
|
||||
@most_recent_item = nil
|
||||
if current_member
|
||||
@order = current_member.current_order
|
||||
if @order
|
||||
@most_recent_item = @order.order_items.first
|
||||
end
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html # index.html.haml
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,11 +1,11 @@
|
||||
module ApplicationHelper
|
||||
def price_in_dollars(price)
|
||||
sprintf('%.2f', price / 100.0)
|
||||
format('%.2f', price / 100.0)
|
||||
end
|
||||
|
||||
# 999 cents becomes 9.99 AUD -- for products/orders/etc
|
||||
def price_with_currency(price)
|
||||
sprintf('%.2f %s', price / 100.0, Growstuff::Application.config.currency)
|
||||
format('%.2f %s', price / 100.0, Growstuff::Application.config.currency)
|
||||
end
|
||||
|
||||
def parse_date(str)
|
||||
@@ -55,6 +55,7 @@ module ApplicationHelper
|
||||
# Falls back to Gravatar
|
||||
#
|
||||
def avatar_uri(member, size = 150)
|
||||
return unless member
|
||||
if member.preferred_avatar_uri.present?
|
||||
# Some avatars support different sizes
|
||||
# http://graph.facebook.com/12345678/picture?width=150&height=150
|
||||
|
||||
@@ -11,7 +11,7 @@ module AutoSuggestHelper
|
||||
resource = resource.class.name.downcase
|
||||
source_path = Rails.application.routes.url_helpers.send("#{source}s_search_path")
|
||||
|
||||
%Q{
|
||||
%(
|
||||
<input id="#{source}" class="auto-suggest #{options[:class]}"
|
||||
type="text" value="#{default}" data-source-url="#{source_path}",
|
||||
placeholder="e.g. lettuce">
|
||||
@@ -20,6 +20,6 @@ module AutoSuggestHelper
|
||||
</noscript>
|
||||
<input id="#{resource}_#{source}_id" class="auto-suggest-id"
|
||||
type="hidden" name="#{resource}[#{source}_id]" value="#{default_id}">
|
||||
}.html_safe
|
||||
).html_safe
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,9 +8,7 @@ module CropsHelper
|
||||
total_quantity += seed.quantity if seed.quantity
|
||||
end
|
||||
|
||||
if !seeds.any?
|
||||
return "You don't have any seeds of this crop."
|
||||
end
|
||||
return "You don't have any seeds of this crop." if seeds.none?
|
||||
|
||||
if total_quantity != 0
|
||||
"You have #{total_quantity} #{Seed.model_name.human(count: total_quantity)} of this crop."
|
||||
|
||||
@@ -15,7 +15,7 @@ module HarvestsHelper
|
||||
|
||||
if harvest.unit == 'individual' # just the number
|
||||
number_to_human(harvest.quantity, strip_insignificant_zeros: true)
|
||||
elsif !harvest.unit.blank? # pluralize anything else
|
||||
elsif harvest.unit.present? # pluralize anything else
|
||||
pluralize(number_to_human(harvest.quantity, strip_insignificant_zeros: true), harvest.unit)
|
||||
else
|
||||
"#{number_to_human(harvest.quantity, strip_insignificant_zeros: true)} #{harvest.unit}"
|
||||
@@ -28,7 +28,12 @@ module HarvestsHelper
|
||||
end
|
||||
|
||||
def display_harvest_description(harvest)
|
||||
return "No description provided." if harvest.description.nil?
|
||||
harvest.description
|
||||
if harvest.description.nil?
|
||||
"no description provided."
|
||||
else
|
||||
truncate(harvest.description, length: 50, separator: ' ', omission: '... ') do
|
||||
link_to "Read more", harvest_path(harvest)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
51
app/helpers/photos_helper.rb
Normal file
51
app/helpers/photos_helper.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
module PhotosHelper
|
||||
def crop_image_path(crop)
|
||||
if crop.default_photo.present?
|
||||
crop.default_photo.thumbnail_url
|
||||
else
|
||||
placeholder_image
|
||||
end
|
||||
end
|
||||
|
||||
def garden_image_path(garden)
|
||||
if garden.default_photo.present?
|
||||
garden.default_photo.thumbnail_url
|
||||
else
|
||||
placeholder_image
|
||||
end
|
||||
end
|
||||
|
||||
def planting_image_path(planting)
|
||||
if planting.photos.present?
|
||||
planting.photos.first.thumbnail_url
|
||||
else
|
||||
placeholder_image
|
||||
end
|
||||
end
|
||||
|
||||
def harvest_image_path(harvest)
|
||||
if harvest.photos.present?
|
||||
harvest.photos.first.thumbnail_url
|
||||
elsif harvest.planting.present? && harvest.planting.photos.present?
|
||||
harvest.planting.photos.first.thumbnail_url
|
||||
else
|
||||
placeholder_image
|
||||
end
|
||||
end
|
||||
|
||||
def seed_image_path(seed)
|
||||
if seed.default_photo.present?
|
||||
seed.default_photo.thumbnail_url
|
||||
elsif seed.crop.default_photo.present?
|
||||
seed.crop.default_photo.thumbnail_url
|
||||
else
|
||||
placeholder_image
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def placeholder_image
|
||||
'placeholder_150.png'
|
||||
end
|
||||
end
|
||||
@@ -1,18 +1,6 @@
|
||||
module PlantingsHelper
|
||||
def display_days_before_maturity(planting)
|
||||
# First try to calc from finished/finished_at
|
||||
if planting.finished? || planting.finished_at.present?
|
||||
planting.days_until_finished.to_s
|
||||
# then try to calc from planted at + maturity
|
||||
elsif planting.planted_at.present? && planting.days_before_maturity.present?
|
||||
planting.days_until_mature.to_s
|
||||
else
|
||||
"unknown"
|
||||
end
|
||||
end
|
||||
|
||||
def display_finished(planting)
|
||||
if !planting.finished_at.nil?
|
||||
if planting.finished_at.present?
|
||||
planting.finished_at
|
||||
elsif planting.finished
|
||||
"Yes (no date specified)"
|
||||
@@ -22,11 +10,11 @@ module PlantingsHelper
|
||||
end
|
||||
|
||||
def display_planted_from(planting)
|
||||
!planting.planted_from.blank? ? planting.planted_from : "not specified"
|
||||
planting.planted_from.present? ? planting.planted_from : "not specified"
|
||||
end
|
||||
|
||||
def display_planting_quantity(planting)
|
||||
!planting.quantity.blank? ? planting.quantity : "not specified"
|
||||
planting.quantity.present? ? planting.quantity : "not specified"
|
||||
end
|
||||
|
||||
def display_planting(planting)
|
||||
|
||||
@@ -37,17 +37,20 @@ class Notifier < ActionMailer::Base
|
||||
end
|
||||
|
||||
def new_crop_request(member, request)
|
||||
@member, @request = member, request
|
||||
@member = member
|
||||
@request = request
|
||||
mail(to: @member.email, subject: "#{@request.requester.login_name} has requested #{@request.name} as a new crop")
|
||||
end
|
||||
|
||||
def crop_request_approved(member, crop)
|
||||
@member, @crop = member, crop
|
||||
@member = member
|
||||
@crop = crop
|
||||
mail(to: @member.email, subject: "#{crop.name.capitalize} has been approved")
|
||||
end
|
||||
|
||||
def crop_request_rejected(member, crop)
|
||||
@member, @crop = member, crop
|
||||
@member = member
|
||||
@crop = crop
|
||||
mail(to: @member.email, subject: "#{crop.name.capitalize} has been rejected")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
def initialize(member) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
||||
def initialize(member)
|
||||
anon_abilities(member)
|
||||
member_abilities(member) if member.present?
|
||||
admin_abilities(member) if member.present? && member.role?(:admin)
|
||||
end
|
||||
|
||||
def anon_abilities(_member)
|
||||
# See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities
|
||||
|
||||
# everyone can do these things, even non-logged in
|
||||
@@ -9,17 +15,18 @@ class Ability
|
||||
can :view_follows, Member
|
||||
can :view_followers, Member
|
||||
|
||||
# Everyone can see the charts
|
||||
can :timeline, Garden
|
||||
can :sunniness, Crop
|
||||
can :planted_from, Crop
|
||||
can :harvested_for, Crop
|
||||
|
||||
# except these, which don't make sense if you're not logged in
|
||||
cannot :read, Notification
|
||||
cannot :read, Authentication
|
||||
cannot :read, Order
|
||||
cannot :read, OrderItem
|
||||
|
||||
# and nobody should be able to view this except admins
|
||||
cannot :read, Role
|
||||
cannot :read, Product
|
||||
cannot :read, Account
|
||||
cannot :read, AccountType
|
||||
|
||||
# nobody should be able to view unapproved crops unless they
|
||||
# are wranglers or admins
|
||||
@@ -35,7 +42,9 @@ class Ability
|
||||
can :read, AlternateName do |an|
|
||||
an.crop.approved?
|
||||
end
|
||||
end
|
||||
|
||||
def member_abilities(member) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
||||
return unless member
|
||||
|
||||
# members can see even rejected or pending crops if they requested it
|
||||
@@ -102,23 +111,12 @@ class Ability
|
||||
can :update, Photo, owner_id: member.id
|
||||
can :destroy, Photo, owner_id: member.id
|
||||
|
||||
can :create, Seed
|
||||
can :update, Seed, owner_id: member.id
|
||||
can :create, Seed
|
||||
can :update, Seed, owner_id: member.id
|
||||
can :destroy, Seed, owner_id: member.id
|
||||
|
||||
# orders/shop/etc
|
||||
can :create, Order
|
||||
can :read, Order, member_id: member.id
|
||||
can :complete, Order, member_id: member.id, completed_at: nil
|
||||
can :checkout, Order, member_id: member.id, completed_at: nil
|
||||
can :cancel, Order, member_id: member.id, completed_at: nil
|
||||
can :destroy, Order, member_id: member.id, completed_at: nil
|
||||
|
||||
can :create, OrderItem
|
||||
# for now, let's not let people mess with individual order items
|
||||
cannot :read, OrderItem, order: { member_id: member.id }
|
||||
cannot :update, OrderItem, order: { member_id: member.id, completed_at: nil }
|
||||
cannot :destroy, OrderItem, order: { member_id: member.id, completed_at: nil }
|
||||
can :create, Seed, owner_id: member.id, parent_planting: { owner_id: member.id }
|
||||
can :update, Seed, owner_id: member.id, parent_planting: { owner_id: member.id }
|
||||
can :destroy, Seed, owner_id: member.id, parent_planting: { owner_id: member.id }
|
||||
|
||||
# following/unfollowing permissions
|
||||
can :create, Follow
|
||||
@@ -126,18 +124,14 @@ class Ability
|
||||
|
||||
can :destroy, Follow
|
||||
cannot :destroy, Follow, followed_id: member.id # can't unfollow yourself
|
||||
end
|
||||
|
||||
def admin_abilities(member)
|
||||
return unless member.role? :admin
|
||||
|
||||
can :read, :all
|
||||
can :manage, :all
|
||||
|
||||
# can't change order history, because it's *history*
|
||||
cannot :create, Order
|
||||
cannot :complete, Order
|
||||
cannot :destroy, Order
|
||||
cannot :manage, OrderItem
|
||||
|
||||
# can't delete plant parts if they have harvests associated with them
|
||||
cannot :destroy, PlantPart
|
||||
can :destroy, PlantPart do |pp|
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
class Account < ActiveRecord::Base
|
||||
belongs_to :member
|
||||
belongs_to :account_type
|
||||
|
||||
validates :member_id, uniqueness: {
|
||||
message: 'already has account details associated with it'
|
||||
}
|
||||
|
||||
before_create do |account|
|
||||
unless account.account_type
|
||||
account.account_type = AccountType.find_or_create_by(name:
|
||||
Growstuff::Application.config.default_account_type)
|
||||
end
|
||||
end
|
||||
|
||||
def paid_until_string
|
||||
if account_type.is_permanent_paid
|
||||
"forever"
|
||||
elsif account_type.is_paid
|
||||
paid_until.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,7 +0,0 @@
|
||||
class AccountType < ActiveRecord::Base
|
||||
has_many :products
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,6 @@ class Comment < ActiveRecord::Base
|
||||
belongs_to :author, class_name: 'Member'
|
||||
belongs_to :post
|
||||
|
||||
default_scope { order("created_at DESC") }
|
||||
scope :post_order, -> { reorder("created_at ASC") } # for display on post page
|
||||
|
||||
after_create do
|
||||
|
||||
12
app/models/concerns/finishable.rb
Normal file
12
app/models/concerns/finishable.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
module Finishable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :finished, -> { where(finished: true) }
|
||||
scope :current, -> { where.not(finished: true) }
|
||||
|
||||
def active?
|
||||
!finished
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,17 +1,10 @@
|
||||
require_relative '../../constants/photo_models.rb'
|
||||
module PhotoCapable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_and_belongs_to_many :photos # rubocop:disable Rails/HasAndBelongsToMany
|
||||
has_many :photos, through: :photographings, as: :photographable
|
||||
has_many :photographings, as: :photographable, dependent: :destroy
|
||||
|
||||
before_destroy :remove_from_list
|
||||
end
|
||||
|
||||
def remove_from_list
|
||||
photolist = photos.to_a # save a temp copy of the photo list
|
||||
photos.clear # clear relationship b/w object and photo
|
||||
|
||||
photolist.each(&:destroy_if_unused)
|
||||
scope :has_photos, -> { includes(:photos).where.not(photos: { id: nil }) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,47 +1,48 @@
|
||||
class Crop < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
friendly_id :name, use: %i(slugged finders)
|
||||
|
||||
has_many :scientific_names, after_add: :update_index, after_remove: :update_index
|
||||
accepts_nested_attributes_for :scientific_names,
|
||||
allow_destroy: true,
|
||||
reject_if: :all_blank
|
||||
##
|
||||
## Triggers
|
||||
before_destroy { |crop| crop.posts.clear }
|
||||
|
||||
##
|
||||
## Relationships
|
||||
has_many :scientific_names, after_add: :update_index, after_remove: :update_index, dependent: :destroy
|
||||
accepts_nested_attributes_for :scientific_names, allow_destroy: true, reject_if: :all_blank
|
||||
has_many :alternate_names, after_add: :update_index, after_remove: :update_index, dependent: :destroy
|
||||
has_many :plantings
|
||||
has_many :photos, through: :plantings
|
||||
has_many :seeds
|
||||
has_many :harvests
|
||||
has_many :plant_parts, -> { uniq }, through: :harvests
|
||||
has_many :plant_parts, -> { uniq.reorder("plant_parts.name") }, through: :harvests
|
||||
belongs_to :creator, class_name: 'Member'
|
||||
belongs_to :requester, class_name: 'Member'
|
||||
|
||||
belongs_to :parent, class_name: 'Crop'
|
||||
has_many :varieties, class_name: 'Crop', foreign_key: 'parent_id'
|
||||
has_and_belongs_to_many :posts # rubocop:disable Rails/HasAndBelongsToMany
|
||||
before_destroy { |crop| crop.posts.clear }
|
||||
|
||||
default_scope { order("lower(crops.name) asc") }
|
||||
scope :recent, lambda {
|
||||
approved.reorder("created_at desc")
|
||||
}
|
||||
scope :toplevel, lambda {
|
||||
approved.where(parent_id: nil)
|
||||
}
|
||||
scope :popular, lambda {
|
||||
approved.reorder("plantings_count desc, lower(name) asc")
|
||||
}
|
||||
scope :randomized, lambda {
|
||||
# ok on sqlite and psql, but not on mysql
|
||||
approved.reorder('random()')
|
||||
}
|
||||
##
|
||||
## Scopes
|
||||
scope :recent, -> { approved.order(created_at: :desc) }
|
||||
scope :toplevel, -> { approved.where(parent_id: nil) }
|
||||
scope :popular, -> { approved.reorder("plantings_count desc, lower(name) asc") }
|
||||
# ok on sqlite and psql, but not on mysql
|
||||
scope :randomized, -> { approved.reorder('random()') }
|
||||
scope :pending_approval, -> { where(approval_status: "pending") }
|
||||
scope :approved, -> { where(approval_status: "approved") }
|
||||
scope :rejected, -> { where(approval_status: "rejected") }
|
||||
|
||||
scope :interesting, -> { approved.has_photos }
|
||||
scope :interesting, -> { approved.has_photos.randomized }
|
||||
scope :has_photos, -> { includes(:photos).where.not(photos: { id: nil }) }
|
||||
|
||||
##
|
||||
## Validations
|
||||
# Reasons are only necessary when rejecting
|
||||
validates :reason_for_rejection, presence: true, if: :rejected?
|
||||
## This validation addresses a race condition
|
||||
validate :approval_status_cannot_be_changed_again
|
||||
validate :must_be_rejected_if_rejected_reasons_present
|
||||
validate :must_have_meaningful_reason_for_rejection
|
||||
## Wikipedia urls are only necessary when approving a crop
|
||||
validates :en_wikipedia_url,
|
||||
format: {
|
||||
@@ -50,16 +51,6 @@ class Crop < ActiveRecord::Base
|
||||
},
|
||||
if: :approved?
|
||||
|
||||
## Reasons are only necessary when rejecting
|
||||
validates :reason_for_rejection, presence: true, if: :rejected?
|
||||
|
||||
## This validation addresses a race condition
|
||||
validate :approval_status_cannot_be_changed_again
|
||||
|
||||
validate :must_be_rejected_if_rejected_reasons_present
|
||||
|
||||
validate :must_have_meaningful_reason_for_rejection
|
||||
|
||||
####################################
|
||||
# Elastic search configuration
|
||||
if ENV["GROWSTUFF_ELASTICSEARCH"] == "true"
|
||||
@@ -106,14 +97,8 @@ class Crop < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def as_indexed_json(_options = {})
|
||||
as_json(
|
||||
only: [:id, :name, :approval_status],
|
||||
include: {
|
||||
scientific_names: { only: :name },
|
||||
alternate_names: { only: :name }
|
||||
}
|
||||
)
|
||||
def harvest_photos
|
||||
Photo.joins(:harvests).where("harvests.crop_id": id)
|
||||
end
|
||||
|
||||
# update the Elasticsearch index (only if we're using it in this
|
||||
@@ -121,7 +106,6 @@ class Crop < ActiveRecord::Base
|
||||
def update_index(_name_obj)
|
||||
__elasticsearch__.index_document if ENV["GROWSTUFF_ELASTICSEARCH"] == "true"
|
||||
end
|
||||
|
||||
# End Elasticsearch section
|
||||
|
||||
def to_s
|
||||
@@ -132,19 +116,13 @@ class Crop < ActiveRecord::Base
|
||||
scientific_names.first.name unless scientific_names.empty?
|
||||
end
|
||||
|
||||
# crop.default_photo
|
||||
# currently returns the first available photo, but exists so that
|
||||
# later we can choose a default photo based on different criteria,
|
||||
# eg. popularity
|
||||
def default_photo
|
||||
return photos.first if photos.any?
|
||||
|
||||
# Crop has no photos? Look for the most recent harvest with a photo.
|
||||
harvest_with_photo = Harvest.where(crop_id: id).joins(:photos).order('harvests.id DESC').limit(1).first
|
||||
harvest_with_photo.photos.first if harvest_with_photo
|
||||
first_photo(:plantings) || first_photo(:harvests) || first_photo(:seeds)
|
||||
end
|
||||
|
||||
# crop.sunniness
|
||||
# returns hash indicating whether this crop is grown in
|
||||
# sun/semi-shade/shade
|
||||
# key: sunniness (eg. 'sun')
|
||||
@@ -153,7 +131,6 @@ class Crop < ActiveRecord::Base
|
||||
count_uses_of_property 'sunniness'
|
||||
end
|
||||
|
||||
# crop.planted_from
|
||||
# returns a hash of propagation methods (seed, seedling, etc),
|
||||
# key: propagation method (eg. 'seed')
|
||||
# value: count of how many times it's been used by plantings
|
||||
@@ -161,7 +138,6 @@ class Crop < ActiveRecord::Base
|
||||
count_uses_of_property 'planted_from'
|
||||
end
|
||||
|
||||
# crop.popular_plant_parts
|
||||
# returns a hash of most harvested plant parts (fruit, seed, etc)
|
||||
# key: plant part (eg. 'fruit')
|
||||
# value: count of how many times it's been used by harvests
|
||||
@@ -173,6 +149,10 @@ class Crop < ActiveRecord::Base
|
||||
.count("harvests.id")
|
||||
end
|
||||
|
||||
def annual?
|
||||
!perennial
|
||||
end
|
||||
|
||||
def interesting?
|
||||
min_plantings = 3 # needs this many plantings to be interesting
|
||||
min_photos = 3 # needs this many photos to be interesting
|
||||
@@ -206,45 +186,24 @@ class Crop < ActiveRecord::Base
|
||||
reason_for_rejection
|
||||
end
|
||||
|
||||
# Crop.search(string)
|
||||
def update_medians
|
||||
plantings.each(&:update_harvest_days)
|
||||
update_lifespan_medians
|
||||
update_harvest_medians
|
||||
end
|
||||
|
||||
def update_lifespan_medians
|
||||
# Median lifespan of plantings
|
||||
update(median_lifespan: Planting.where(crop: self).median(:lifespan))
|
||||
end
|
||||
|
||||
def update_harvest_medians
|
||||
update(median_days_to_first_harvest: Planting.where(crop: self).median(:days_to_first_harvest))
|
||||
update(median_days_to_last_harvest: Planting.where(crop: self).median(:days_to_last_harvest))
|
||||
end
|
||||
|
||||
def self.search(query)
|
||||
if ENV['GROWSTUFF_ELASTICSEARCH'] == "true"
|
||||
search_str = query.nil? ? "" : query.downcase
|
||||
response = __elasticsearch__.search( # Finds documents which match any field, but uses the _score from
|
||||
# the best field insead of adding up _score from each field.
|
||||
query: {
|
||||
multi_match: {
|
||||
query: search_str.to_s,
|
||||
analyzer: "standard",
|
||||
fields: ["name",
|
||||
"scientific_names.scientific_name",
|
||||
"alternate_names.name"]
|
||||
}
|
||||
},
|
||||
filter: {
|
||||
term: { approval_status: "approved" }
|
||||
},
|
||||
size: 50
|
||||
)
|
||||
response.records.to_a
|
||||
else
|
||||
# if we don't have elasticsearch, just do a basic SQL query.
|
||||
# also, make sure it's an actual array not an activerecord
|
||||
# collection, so it matches what we get from elasticsearch and we can
|
||||
# manipulate it in the same ways (eg. deleting elements without deleting
|
||||
# the whole record from the db)
|
||||
matches = Crop.approved.where("name ILIKE ?", "%#{query}%").to_a
|
||||
|
||||
# we want to make sure that exact matches come first, even if not
|
||||
# using elasticsearch (eg. in development)
|
||||
exact_match = Crop.approved.find_by(name: query)
|
||||
if exact_match
|
||||
matches.delete(exact_match)
|
||||
matches.unshift(exact_match)
|
||||
end
|
||||
|
||||
matches
|
||||
end
|
||||
CropSearchService.search(query)
|
||||
end
|
||||
|
||||
def self.case_insensitive_name(name)
|
||||
@@ -278,4 +237,8 @@ class Crop < ActiveRecord::Base
|
||||
return unless reason_for_rejection == "other" && rejection_notes.blank?
|
||||
errors.add(:rejection_notes, "must be added if the reason for rejection is \"other\"")
|
||||
end
|
||||
|
||||
def first_photo(type)
|
||||
Photo.joins(type).where("#{type}": { crop_id: id }).order("photos.created_at DESC").first
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,7 +34,7 @@ class CsvImporter
|
||||
|
||||
def add_scientific_names(scientific_names)
|
||||
names_to_add = []
|
||||
if !scientific_names.blank? # i.e. we actually passed something in, which isn't a given
|
||||
if scientific_names.present? # i.e. we actually passed something in, which isn't a given
|
||||
names_to_add = scientific_names.split(/,\s*/)
|
||||
elsif @crop.parent && !@crop.parent.scientific_names.empty? # pick up from parent
|
||||
names_to_add = @crop.parent.scientific_names.map(&:name)
|
||||
@@ -46,7 +46,7 @@ class CsvImporter
|
||||
|
||||
names_to_add.each do |name|
|
||||
sciname = ScientificName.find_by(name: name, crop: @crop)
|
||||
sciname = ScientificName.create!(name: name, crop: @crop, creator: cropbot) unless sciname
|
||||
sciname ||= ScientificName.create!(name: name, crop: @crop, creator: cropbot)
|
||||
@crop.scientific_names << sciname
|
||||
end
|
||||
end
|
||||
@@ -56,15 +56,15 @@ class CsvImporter
|
||||
return if alternate_names.blank?
|
||||
alternate_names.split(/,\s*/).each do |name|
|
||||
altname = AlternateName.find_by(name: name, crop: @crop)
|
||||
altname = AlternateName.create! name: name, crop: @crop, creator: cropbot unless altname
|
||||
altname ||= AlternateName.create! name: name, crop: @crop, creator: cropbot
|
||||
@crop.alternate_names << altname
|
||||
end
|
||||
end
|
||||
|
||||
def cropbot
|
||||
@cropbot = Member.find_by!(login_name: 'cropbot') unless @cropbot
|
||||
@cropbot ||= Member.find_by!(login_name: 'cropbot')
|
||||
@cropbot
|
||||
rescue
|
||||
rescue StandardError
|
||||
raise "cropbot account not found: run rake db:seed"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class Forum < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
validates :name, presence: true
|
||||
friendly_id :name, use: [:slugged, :finders]
|
||||
friendly_id :name, use: %i(slugged finders)
|
||||
|
||||
has_many :posts
|
||||
belongs_to :owner, class_name: "Member"
|
||||
|
||||
@@ -2,10 +2,10 @@ class Garden < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
include Geocodable
|
||||
include PhotoCapable
|
||||
friendly_id :garden_slug, use: [:slugged, :finders]
|
||||
friendly_id :garden_slug, use: %i(slugged finders)
|
||||
|
||||
belongs_to :owner, class_name: 'Member', foreign_key: 'owner_id', counter_cache: true
|
||||
has_many :plantings, -> { order(created_at: :desc) }, dependent: :destroy
|
||||
has_many :plantings, dependent: :destroy
|
||||
has_many :crops, through: :plantings
|
||||
|
||||
# set up geocoding
|
||||
@@ -14,7 +14,7 @@ class Garden < ActiveRecord::Base
|
||||
after_validation :empty_unwanted_geocodes
|
||||
after_save :mark_inactive_garden_plantings_as_finished
|
||||
|
||||
default_scope { order("lower(name) asc") }
|
||||
default_scope { joins(:owner) } # Ensures owner exists
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :inactive, -> { where(active: false) }
|
||||
|
||||
@@ -23,7 +23,7 @@ class Garden < ActiveRecord::Base
|
||||
|
||||
validates :name,
|
||||
format: {
|
||||
with: /\A\w+[\w ]+\z/
|
||||
with: /\A\w+[\w ()]+\z/
|
||||
},
|
||||
length: { maximum: 255 }
|
||||
|
||||
@@ -41,7 +41,7 @@ class Garden < ActiveRecord::Base
|
||||
"acres" => "acre"
|
||||
}.freeze
|
||||
validates :area_unit, inclusion: { in: AREA_UNITS_VALUES.values,
|
||||
message: "%{value} is not a valid area unit" },
|
||||
message: "%<value>s is not a valid area unit" },
|
||||
allow_nil: true,
|
||||
allow_blank: true
|
||||
|
||||
@@ -62,7 +62,7 @@ class Garden < ActiveRecord::Base
|
||||
unique_plantings = []
|
||||
seen_crops = []
|
||||
|
||||
plantings.each do |p|
|
||||
plantings.order(created_at: :desc).includes(:garden, :crop, :owner, :harvests).each do |p|
|
||||
unless seen_crops.include?(p.crop)
|
||||
unique_plantings.push(p)
|
||||
seen_crops.push(p.crop)
|
||||
@@ -88,6 +88,6 @@ class Garden < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def default_photo
|
||||
photos.first
|
||||
photos.order(created_at: :desc).first
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,29 +1,11 @@
|
||||
class Harvest < ActiveRecord::Base
|
||||
extend FriendlyId
|
||||
include ActionView::Helpers::NumberHelper
|
||||
extend FriendlyId
|
||||
include PhotoCapable
|
||||
friendly_id :harvest_slug, use: [:slugged, :finders]
|
||||
|
||||
belongs_to :crop
|
||||
belongs_to :owner, class_name: 'Member', counter_cache: true
|
||||
belongs_to :plant_part
|
||||
belongs_to :planting
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
validates :crop, approved: true
|
||||
|
||||
validates :crop, presence: { message: "must be present and exist in our database" }
|
||||
|
||||
validates :plant_part, presence: { message: "must be present and exist in our database" }
|
||||
|
||||
validates :quantity,
|
||||
numericality: {
|
||||
only_integer: false,
|
||||
greater_than_or_equal_to: 0
|
||||
},
|
||||
allow_nil: true
|
||||
friendly_id :harvest_slug, use: %i(slugged finders)
|
||||
|
||||
# Constants
|
||||
UNITS_VALUES = {
|
||||
"individual" => "individual",
|
||||
"bunches" => "bunch",
|
||||
@@ -36,29 +18,61 @@ class Harvest < ActiveRecord::Base
|
||||
"baskets" => "basket",
|
||||
"bushels" => "bushel"
|
||||
}.freeze
|
||||
validates :unit, inclusion: { in: UNITS_VALUES.values,
|
||||
message: "%{value} is not a valid unit" },
|
||||
allow_nil: true,
|
||||
allow_blank: true
|
||||
|
||||
validates :weight_quantity,
|
||||
numericality: { only_integer: false },
|
||||
allow_nil: true
|
||||
|
||||
WEIGHT_UNITS_VALUES = {
|
||||
"kg" => "kg",
|
||||
"lb" => "lb",
|
||||
"oz" => "oz"
|
||||
}.freeze
|
||||
validates :weight_unit, inclusion: { in: WEIGHT_UNITS_VALUES.values,
|
||||
message: "%{value} is not a valid unit" },
|
||||
allow_nil: true,
|
||||
allow_blank: true
|
||||
|
||||
##
|
||||
## Triggers
|
||||
after_validation :cleanup_quantities
|
||||
|
||||
before_save :set_si_weight
|
||||
|
||||
##
|
||||
## Relationships
|
||||
belongs_to :crop
|
||||
belongs_to :owner, class_name: 'Member', counter_cache: true
|
||||
belongs_to :plant_part
|
||||
belongs_to :planting
|
||||
|
||||
##
|
||||
## Scopes
|
||||
default_scope { joins(:owner) } # Ensures owner exists
|
||||
scope :interesting, -> { has_photos.one_per_owner }
|
||||
scope :recent, -> { order(created_at: :desc) }
|
||||
scope :one_per_owner, lambda {
|
||||
joins("JOIN members m ON (m.id=harvests.owner_id)
|
||||
LEFT OUTER JOIN harvests h2
|
||||
ON (m.id=h2.owner_id AND harvests.id < h2.id)").where("h2 IS NULL")
|
||||
}
|
||||
|
||||
##
|
||||
## Validations
|
||||
validates :crop, approved: true
|
||||
validates :crop, presence: { message: "must be present and exist in our database" }
|
||||
validates :plant_part, presence: { message: "must be present and exist in our database" }
|
||||
validates :harvested_at, presence: true
|
||||
validates :quantity, allow_nil: true, numericality: {
|
||||
only_integer: false, greater_than_or_equal_to: 0
|
||||
}
|
||||
validates :unit, allow_nil: true, allow_blank: true, inclusion: {
|
||||
in: UNITS_VALUES.values, message: "%<value>s is not a valid unit"
|
||||
}
|
||||
validates :weight_quantity, allow_nil: true, numericality: { only_integer: false }
|
||||
validates :weight_unit, allow_nil: true, allow_blank: true, inclusion: {
|
||||
in: WEIGHT_UNITS_VALUES.values, message: "%<value>s is not a valid unit"
|
||||
}
|
||||
validate :crop_must_match_planting
|
||||
validate :owner_must_match_planting
|
||||
validate :harvest_must_be_after_planting
|
||||
|
||||
def time_from_planting_to_harvest
|
||||
return if planting.blank?
|
||||
harvested_at - planting.planted_at
|
||||
end
|
||||
|
||||
# we're storing the harvest weight in kilograms in the db too
|
||||
# to make data manipulation easier
|
||||
def set_si_weight
|
||||
@@ -117,6 +131,24 @@ class Harvest < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def default_photo
|
||||
photos.first || crop.default_photo
|
||||
photos.order(created_at: :desc).first || crop.default_photo
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def crop_must_match_planting
|
||||
return if planting.blank? # only check if we are linked to a planting
|
||||
errors.add(:planting, "must be the same crop") unless crop == planting.crop
|
||||
end
|
||||
|
||||
def owner_must_match_planting
|
||||
return if planting.blank? # only check if we are linked to a planting
|
||||
errors.add(:owner, "of harvest must be the same as planting") unless owner == planting.owner
|
||||
end
|
||||
|
||||
def harvest_must_be_after_planting
|
||||
# only check if we are linked to a planting
|
||||
return unless harvested_at.present? && planting.present? && planting.planted_at.present?
|
||||
errors.add(:planting, "cannot be harvested before planting") unless harvested_at > planting.planted_at
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,56 +1,42 @@
|
||||
class Member < ActiveRecord::Base
|
||||
acts_as_paranoid # implements soft deletion
|
||||
before_destroy :newsletter_unsubscribe
|
||||
include Geocodable
|
||||
extend FriendlyId
|
||||
|
||||
friendly_id :login_name, use: [:slugged, :finders]
|
||||
friendly_id :login_name, use: %i(slugged finders)
|
||||
|
||||
#
|
||||
# Relationships
|
||||
has_many :posts, foreign_key: 'author_id'
|
||||
has_many :comments, foreign_key: 'author_id'
|
||||
has_many :forums, foreign_key: 'owner_id'
|
||||
|
||||
has_many :gardens, foreign_key: 'owner_id'
|
||||
has_many :plantings, foreign_key: 'owner_id'
|
||||
|
||||
has_many :seeds, foreign_key: 'owner_id'
|
||||
has_many :harvests, foreign_key: 'owner_id'
|
||||
|
||||
has_and_belongs_to_many :roles # rubocop:disable Rails/HasAndBelongsToMany
|
||||
|
||||
has_many :notifications, foreign_key: 'recipient_id'
|
||||
has_many :sent_notifications, foreign_key: 'sender_id'
|
||||
|
||||
has_many :authentications
|
||||
|
||||
has_many :orders
|
||||
has_one :account
|
||||
has_one :account_type, through: :account
|
||||
|
||||
has_many :photos
|
||||
|
||||
has_many :requested_crops, class_name: Crop, foreign_key: 'requester_id'
|
||||
has_many :likes, dependent: :destroy
|
||||
|
||||
default_scope { order("lower(login_name) asc") }
|
||||
scope :confirmed, -> { where('confirmed_at IS NOT NULL') }
|
||||
scope :located, -> { where("location <> '' and latitude IS NOT NULL and longitude IS NOT NULL") }
|
||||
scope :recently_signed_in, -> { reorder('updated_at DESC') }
|
||||
scope :recently_joined, -> { reorder("confirmed_at desc") }
|
||||
scope :wants_newsletter, -> { where(newsletter: true) }
|
||||
|
||||
scope :interesting, lambda {
|
||||
confirmed
|
||||
.located
|
||||
.recently_signed_in
|
||||
.has_plantings
|
||||
}
|
||||
|
||||
scope :has_plantings, -> { joins(:plantings).group("members.id") }
|
||||
|
||||
has_many :follows, class_name: "Follow", foreign_key: "follower_id"
|
||||
has_many :follows, class_name: "Follow", foreign_key: "follower_id", dependent: :destroy
|
||||
has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id", dependent: :destroy
|
||||
has_many :followed, through: :follows
|
||||
|
||||
has_many :inverse_follows, class_name: "Follow", foreign_key: "followed_id"
|
||||
has_many :followers, through: :inverse_follows, source: :follower
|
||||
|
||||
#
|
||||
# Scopes
|
||||
scope :confirmed, -> { where.not(confirmed_at: nil) }
|
||||
scope :located, -> { where.not(location: '').where.not(latitude: nil).where.not(longitude: nil) }
|
||||
scope :recently_signed_in, -> { reorder(updated_at: :desc) }
|
||||
scope :recently_joined, -> { reorder(confirmed_at: :desc) }
|
||||
scope :wants_newsletter, -> { where(newsletter: true) }
|
||||
scope :interesting, -> { confirmed.located.recently_signed_in.has_plantings }
|
||||
scope :has_plantings, -> { joins(:plantings).group("members.id") }
|
||||
|
||||
# Include default devise modules. Others available are:
|
||||
# :token_authenticatable, :confirmable,
|
||||
# :lockable, :timeoutable and :omniauthable
|
||||
@@ -60,45 +46,40 @@ class Member < ActiveRecord::Base
|
||||
|
||||
# set up geocoding
|
||||
geocoded_by :location
|
||||
after_validation :geocode
|
||||
after_validation :empty_unwanted_geocodes
|
||||
|
||||
# Virtual attribute for authenticating by either username or email
|
||||
# This is in addition to a real persisted field like 'username'
|
||||
attr_accessor :login
|
||||
|
||||
#
|
||||
# Validations
|
||||
# Requires acceptance of the Terms of Service
|
||||
validates :tos_agreement, acceptance: { allow_nil: true,
|
||||
accept: true }
|
||||
|
||||
validates :tos_agreement, acceptance: { allow_nil: true, accept: true }
|
||||
validates :login_name,
|
||||
length: {
|
||||
minimum: 2,
|
||||
maximum: 25,
|
||||
message: "should be between 2 and 25 characters long"
|
||||
minimum: 2, maximum: 25, message: "should be between 2 and 25 characters long"
|
||||
},
|
||||
exclusion: {
|
||||
in: %w(growstuff admin moderator staff nearby),
|
||||
message: "name is reserved"
|
||||
in: %w(growstuff admin moderator staff nearby), message: "name is reserved"
|
||||
},
|
||||
format: {
|
||||
with: /\A\w+\z/,
|
||||
message: "may only include letters, numbers, or underscores"
|
||||
with: /\A\w+\z/, message: "may only include letters, numbers, or underscores"
|
||||
},
|
||||
uniqueness: {
|
||||
case_sensitive: false
|
||||
}
|
||||
|
||||
# Give each new member a default garden
|
||||
after_create { |member| Garden.create(name: "Garden", owner_id: member.id) }
|
||||
|
||||
# and an account record (for paid accounts etc)
|
||||
# we use find_or_create to avoid accidentally creating a second one,
|
||||
# which can happen sometimes especially with FactoryGirl associations
|
||||
after_create { |member| Account.find_or_create_by(member_id: member.id) }
|
||||
|
||||
#
|
||||
# Triggers
|
||||
after_validation :geocode
|
||||
after_validation :empty_unwanted_geocodes
|
||||
after_save :update_newsletter_subscription
|
||||
|
||||
# Give each new member a default garden
|
||||
# we use find_or_create to avoid accidentally creating a second one,
|
||||
# which can happen sometimes especially with FactoryBot associations
|
||||
after_create { |member| Garden.create(name: "Garden", owner_id: member.id) }
|
||||
|
||||
# allow login via either login_name or email address
|
||||
def self.find_first_by_auth_conditions(warden_conditions)
|
||||
conditions = warden_conditions.dup
|
||||
@@ -115,34 +96,6 @@ class Member < ActiveRecord::Base
|
||||
roles.any? { |r| r.name.gsub(/\s+/, "_").underscore.to_sym == role_sym }
|
||||
end
|
||||
|
||||
def current_order
|
||||
orders.find_by(completed_at: nil)
|
||||
end
|
||||
|
||||
# when purchasing a product that gives you a paid account, this method
|
||||
# does all the messing around to actually make sure the account is
|
||||
# updated correctly -- account type, paid until, etc. Usually this is
|
||||
# called by order.update_account, which loops through all order items
|
||||
# and does this for each one.
|
||||
def update_account_after_purchase(product)
|
||||
account.account_type = product.account_type if product.account_type
|
||||
if product.paid_months
|
||||
start_date = account.paid_until || Time.zone.now
|
||||
account.paid_until = start_date + product.paid_months.months
|
||||
end
|
||||
account.save
|
||||
end
|
||||
|
||||
def paid?
|
||||
if account.account_type.is_permanent_paid
|
||||
true
|
||||
elsif account.account_type.is_paid && account.paid_until >= Time.zone.now
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def auth(provider)
|
||||
authentications.find_by(provider: provider)
|
||||
end
|
||||
|
||||
@@ -5,7 +5,6 @@ class Notification < ActiveRecord::Base
|
||||
|
||||
validates :subject, length: { maximum: 255 }
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
scope :unread, -> { where(read: false) }
|
||||
scope :by_recipient, ->(recipient) { where(recipient_id: recipient) }
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
class Order < ActiveRecord::Base
|
||||
belongs_to :member
|
||||
|
||||
has_many :order_items, dependent: :destroy
|
||||
|
||||
default_scope { order('created_at DESC') }
|
||||
|
||||
validates :referral_code, format: {
|
||||
with: /\A[a-zA-Z0-9 ]*\z/,
|
||||
message: "may only include letters and numbers"
|
||||
}
|
||||
|
||||
before_save :standardize_referral_code
|
||||
|
||||
scope :by_member, ->(member) { where(member: member) }
|
||||
|
||||
# total price of an order
|
||||
def total
|
||||
sum = 0
|
||||
order_items.each do |i|
|
||||
subtotal = i.price * i.quantity
|
||||
sum += subtotal
|
||||
end
|
||||
sum
|
||||
end
|
||||
|
||||
# return items in the format ActiveMerchant/PayPal want them
|
||||
def activemerchant_items
|
||||
items = []
|
||||
order_items.each do |i|
|
||||
items.push(name: i.product.name,
|
||||
quantity: i.quantity,
|
||||
amount: i.price)
|
||||
end
|
||||
items
|
||||
end
|
||||
|
||||
# record the paypal details for reference
|
||||
def record_paypal_details(token)
|
||||
self.paypal_express_token = token
|
||||
details = EXPRESS_GATEWAY.details_for(token)
|
||||
self.paypal_express_payer_id = details.payer_id
|
||||
save
|
||||
end
|
||||
|
||||
# when an order is completed, we update the member's account to mark
|
||||
# them as paid, or whatever, based on what products they ordered
|
||||
def update_account
|
||||
order_items.each do |i|
|
||||
member.update_account_after_purchase(i.product)
|
||||
end
|
||||
end
|
||||
|
||||
# removes whitespace and forces to uppercase (we're somewhat liberal
|
||||
# in what we accept, but we clean it up anyway.)
|
||||
def standardize_referral_code
|
||||
self.referral_code = referral_code.upcase.gsub(/\s/, '') if referral_code
|
||||
end
|
||||
|
||||
# search orders (used by admin/orders)
|
||||
# usage: Order.search({ :by => 'member', :for => 'Skud' })
|
||||
# can search by: member, order_id, paypal_token, paypal_payer_id,
|
||||
def self.search(args = {})
|
||||
if args[:for]
|
||||
case args[:by]
|
||||
when "member"
|
||||
member = Member.find_by(login_name: args[:for])
|
||||
return member.orders if member
|
||||
when "order_id"
|
||||
order = Order.find_by(id: args[:for])
|
||||
return [order] if order
|
||||
when "paypal_token"
|
||||
order = Order.find_by(paypal_express_token: args[:for])
|
||||
return [order] if order
|
||||
when "paypal_payer_id"
|
||||
order = Order.find_by(paypal_express_payer_id: args[:for])
|
||||
return [order] if order
|
||||
when "referral_code"
|
||||
# coerce to uppercase
|
||||
return Order.where(referral_code: args[:for].upcase)
|
||||
end
|
||||
end
|
||||
[]
|
||||
end
|
||||
end
|
||||
@@ -1,12 +0,0 @@
|
||||
class OrderItem < ActiveRecord::Base
|
||||
belongs_to :order
|
||||
belongs_to :product
|
||||
|
||||
validate :price_must_be_greater_than_minimum
|
||||
validates :order_id, uniqueness: { message: "may only have one item." }
|
||||
|
||||
def price_must_be_greater_than_minimum
|
||||
@product = Product.find(product_id)
|
||||
errors.add(:price, "must be greater than the product's minimum value") if price < @product.min_price
|
||||
end
|
||||
end
|
||||
@@ -1,26 +1,18 @@
|
||||
require_relative '../constants/photo_models.rb'
|
||||
class Photo < ActiveRecord::Base
|
||||
belongs_to :owner, class_name: 'Member'
|
||||
|
||||
Growstuff::Constants::PhotoModels.relations.each do |relation|
|
||||
has_and_belongs_to_many relation.to_sym # rubocop:disable Rails/HasAndBelongsToMany
|
||||
PHOTO_CAPABLE = %w(Garden Planting Harvest Seed).freeze
|
||||
|
||||
has_many :photographings, foreign_key: :photo_id, dependent: :destroy
|
||||
# creates a relationship for each assignee type
|
||||
PHOTO_CAPABLE.each do |type|
|
||||
has_many type.downcase.pluralize.to_s.to_sym,
|
||||
through: :photographings,
|
||||
source: :photographable,
|
||||
source_type: type
|
||||
end
|
||||
|
||||
before_destroy { all_associations.clear }
|
||||
|
||||
default_scope { order("created_at desc") }
|
||||
|
||||
def all_associations
|
||||
associations = []
|
||||
Growstuff::Constants::PhotoModels.relations.each do |association_name|
|
||||
associations << send(association_name.to_s).to_a
|
||||
end
|
||||
associations.flatten!
|
||||
end
|
||||
|
||||
def destroy_if_unused
|
||||
destroy if all_associations.empty?
|
||||
end
|
||||
default_scope { joins(:owner) } # Ensures the owner still exists
|
||||
|
||||
# This is split into a side-effect free method and a side-effecting method
|
||||
# for easier stubbing and testing.
|
||||
@@ -30,16 +22,39 @@ class Photo < ActiveRecord::Base
|
||||
licenses = flickr.photos.licenses.getInfo
|
||||
license = licenses.find { |l| l.id == info.license }
|
||||
{
|
||||
title: info.title || "Untitled",
|
||||
title: calculate_title(info),
|
||||
license_name: license.name,
|
||||
license_url: license.url,
|
||||
thumbnail_url: FlickRaw.url_q(info),
|
||||
fullsize_url: FlickRaw.url_z(info),
|
||||
link_url: FlickRaw.url_photopage(info)
|
||||
link_url: FlickRaw.url_photopage(info),
|
||||
date_taken: info.dates.taken
|
||||
}
|
||||
end
|
||||
|
||||
def set_flickr_metadata
|
||||
def associations?
|
||||
photographings.size.positive?
|
||||
end
|
||||
|
||||
def destroy_if_unused
|
||||
destroy unless associations?
|
||||
end
|
||||
|
||||
def calculate_title(info)
|
||||
if id && title # already has a title saved
|
||||
title
|
||||
elsif info.title # use title from flickr
|
||||
info.title
|
||||
else
|
||||
'untitled'
|
||||
end
|
||||
end
|
||||
|
||||
def set_flickr_metadata!
|
||||
update_attributes(flickr_metadata)
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{title} by #{owner.login_name}"
|
||||
end
|
||||
end
|
||||
|
||||
12
app/models/photographing.rb
Normal file
12
app/models/photographing.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class Photographing < ActiveRecord::Base
|
||||
belongs_to :photo
|
||||
belongs_to :photographable, polymorphic: true
|
||||
|
||||
def self.item(item_id, item_type)
|
||||
find_by!(photographable_id: item_id, photographable_type: item_type).photographable
|
||||
end
|
||||
|
||||
def item
|
||||
find_by!(photographable_id: photographable_id, photographable_type: photographable_type).photographable
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user