
👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者
📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
专栏:高并发项目
用户购买商品的流程为: 搜索商品 > 查看商品详情 > 添加到购物车 > 生成商品订单 > 支 付 ,搜索商品和查看商品详情功能已经完成,接下来我们编写购物 车服务。购物车数据属于临时数据,为了节约数据库开销,我们会 将其存放到redis中。
在通用模块编写购物车服务接口:
// 购物车服务
public interface CartService {
// 新增商品到购物车
void addCart(Long userId, CartGoods cartGoods);
// 修改购物车商品数量
void handleCart(Long userId, Long goodId, Integer num);
// 删除购物车商品
void deleteCartOption(Long userId, Long goodId);
// 获取用户购物车
List<CartGoods> findCartList(Long userId);
// 更新redis中的商品数据,在管理员更新商品后执行
void refreshCartGoods(CartGoods cartGoods);
// 删除redis中的商品数据,在管理员下架商品后执行
void deleteCartGoods(CartGoods cartGoods);
}
创建购物车服务模块
1、创建名为 shopping_cart_service 的SpringBoot工程,添加相关依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.itbaizhan</groupId>
<artifactId>shopping_common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!-- 操作zookeeper -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
2、设置该工程的父工程为 shopping 。
<parent>
<groupId>com.ittxc</groupId>
<artifactId>shopping</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
3、给 shopping 工程设置子模块
<!-- 子模块 -->
<modules>
<!-- 购物车服务 -->
<module>shopping_cart_service</module>
</modules>
4、编写配置文件 application.yml
# 端口号
server:
port: 9009
# 日志格式
logging:
pattern:
console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
spring:
# redis
redis:
host: 192.168.0.159
port: 6379
timeout: 30000
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
dubbo:
application:
name: shopping_cart_service # 项目名
registry:
address: zookeeper://192.168.0.159 #注册中心地址
port: 2181 # 注册中心的端口
timeout: 10000 # 注册到zk上超时时间,ms
protocol:
name: dubbo # dubbo使用的协议
port: -1 # dubbo自动分配端口
scan:
base-packages: com.itbaizhan.shopping_cart_service.service # 包扫描
5、启动类忽略数据源自动配置
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class ShoppingCartServiceApplication {
public static void main(String[] args)
{
SpringApplication.run(ShoppingCartServiceApplication.class, args);
}
}
创建购物车Api模块
1、创建名为 shopping_cart_customer_api 的SpringBoot工程,添加相关依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!-- 操作zookeeper -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.itbaizhan</groupId>
<artifactId>shopping_common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-startertest</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、设置该工程的父工程为 shopping 。
<parent>
<groupId>com.ittxc</groupId>
<artifactId>shopping</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
3、给 shopping 工程设置子模块
<!-- 子模块 -->
<modules>
<!-- 购物车api -->
<module>shopping_cart_customer_api</module>
</modules>
4、编写配置文件 application.yml
# 端口号
server:
port: 8005
# 日志格式
logging:
pattern:
console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
dubbo:
application:
name: shopping_cart_customer_api # 项目名
registry:
address: zookeeper://192.168.0.159 #注册中心地址
port: 2181 # 注册中心的端口
timeout: 10000 # 注册到zk上超时时间,ms
protocol:
name: dubbo # dubbo使用的协议
port: -1 # dubbo自动分配端口
5、启动类忽略数据源自动配置
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class ShoppingUserCustomerApiApplication {
public static void main(String[] args)
{
SpringApplication.run(ShoppingUserCustomerApiApplication.class, args);
}
}
编写查询用户购物车功能
我们将用户的购物车信息保存到redis当中,所有用户的购物车作为 一个hash类型的数据保存,hash的键是用户id,hash的值是购物车商品列表。

在购物车服务模块创建购物车服务接口的实现类,重写查询用户购物车功能
@DubboService
public class CartServiceImpl implements CartService {
@Autowired
private RedisTemplate redisTemplate;
@Override
public List<CartGoods> findCartList(Long userId) {
Object cartList = redisTemplate.boundHashOps("cartList").get(userId);
if (cartList == null) {
return new ArrayList<CartGoods>();
} else {
return (List<CartGoods>)cartList;
}
}
}
编写添加商品到购物车方法
@Override
public void addCart(Long userId, CartGoods cartGoods) {
// 1.根据用户id获取用户购物车列表
List<CartGoods> cartList = findCartList(userId);
// 2.查询购物车是否有该商品,如果有商品,添加商品数量
for (CartGoods cartGoods1 : cartList) {
if(cartGoods.getGoodId().equals(cartGoods1.getGoodId())){
int newNum = cartGoods1.getNum() + cartGoods.getNum();
cartGoods1.setNum(newNum);
redisTemplate.boundHashOps("cartList").put(userId,cartList);
return;
}
}
// 3.如果购物车没有该商品,将商品添加到购物车列表
cartList.add(cartGoods);
redisTemplate.boundHashOps("cartList").put(userId,cartList);
}
编写修改购物车商品数量方法
@Override
public void handleCart(Long userId, Long goodId, Integer num) {
// 获取用户购物车列表
List<CartGoods> cartList = findCartList(userId);
// 遍历列表找到对应商品
for (CartGoods cartGoods : cartList) {
if(goodId.equals(cartGoods.getGoodId())) {
// 改变商品数量
cartGoods.setNum(num);
break;
}
}
// 将新的购物车列表保存到redis中
redisTemplate.boundHashOps("cartList").put(userId, cartList);
}
编写删除购物车商品方法
@Override
public void deleteCartOption(Long userId,Long goodId) {
// 获取用户购物车列表
List<CartGoods> cartList = findCartList(userId);
// 将商品移出列表
for (CartGoods cartGoods : cartList) {
if (goodId.equals(cartGoods.getGoodId())) {
cartList.remove(cartGoods);
break;
}
}
// 将新的购物车列表保存到redis中
redisTemplate.boundHashOps("cartList").put(userId, cartList);
}
编写购物车控制器
在进行购物车操作之前,要先获取用户的Id,而用户的令牌中只保存了用户名,所以我们要修改JWT工具类,让令牌中也保存用户的 Id:
1、修改JWT工具类
public class JWTUtil {
//token过期时间,一天
private static final Long EXPIRE_DATE = 1000*60*60*24L;
// 秘钥
private static final String SECRET = "xiaotong";
// 签发者
private static final String ISSUER = "XIAOTONG";
/**
* 签名生成
* @param shoppingUser
* @return
*/
public static String sign(ShoppingUser shoppingUser){
String token = JWT.create()
.withIssuer(ISSUER) // 签发者
.withIssuedAt(new Date())// 签发时间
.withExpiresAt(new Date(new Date().getTime() + EXPIRE_DATE))// 过期时间
.withSubject(shoppingUser.getUsername())// 保存用户名
.withClaim("userId",shoppingUser.getId())// 保存用户id
.sign(Algorithm.HMAC256(SECRET)); // 秘钥
return token;
}
/**
* 签名解析
* @param token 签名字符串
* @return 解析得出的用户名
*/
public static String verify(String token){
try {
String username = JWT
.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISSUER)
.build()
.verify(token)
.getSubject();
return username;
} catch (Exception e){
throw new BusException(CodeEnum.VERIFY_TOKEN_ERROR);
}
}
/**
* 签名解析,获取用户id
* @param token 签名字符串
* @return 用户id
*/
public static Long getId(String token)
{
try {
Long userId = JWT
.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISSUER)
.build()
.verify(token)
.getClaim("userId")
.asLong();
return userId;
} catch (Exception e){
throw new BusException(CodeEnum.VERIFY_TOKEN_ERROR);
}
}
}
2、在购物车Api模块编写购物车控制器
/**
* 购物车
*/
@RestController
@RequestMapping("/user/cart")
public class CartController {
@DubboReference
private CartService cartService;
/**
* 查询用户购物车
* @param token 用户令牌
* @return 用户购物车列表
*/
@GetMapping("/findCartList")
public BaseResult<List<CartGoods>> findCartList(@RequestHeader String token){
Long userId = JWTUtil.getId(token); // 获取用户id
List<CartGoods> cartList = cartService.findCartList(userId);
return BaseResult.ok(cartList);
}
/**
* 新增商品到购物车
* @param cartGoods 购物车商品
* @param token 用户令牌
* @return 操作结果
*/
@PostMapping("/addCart")
public BaseResult addCart(@RequestBody CartGoods cartGoods,@RequestHeader String token){
Long userId = JWTUtil.getId(token); // 获取用户id
cartService.addCart(userId,cartGoods);
return BaseResult.ok();
}
/**
* 修改购物车商品数量
* @param token 用户令牌
* @param goodId 商品id
* @param num 修改后的数量
* @return 操作结果
*/
@GetMapping("/handleCart")
public BaseResult addCart(@RequestHeader String token,Long goodId,Integer num){
Long userId = JWTUtil.getId(token); // 获取用户id
cartService.handleCart(userId,goodId,num);
return BaseResult.ok();
}
/**
* 删除购物车商品
* @param token 用户令牌
* @param goodId 商品id
* @return 操作结果
*/
@DeleteMapping("/deleteCart")
public BaseResult addCart(@RequestHeader String token,Long goodId){
Long userId = JWTUtil.getId(token); // 获取用户id
cartService.deleteCartOption(userId,goodId);
return BaseResult.ok();
}
}
编写修改所有用户购物车商品方法
当管理员修改了商品的价格等数据后,需要将数据同步到用户购物车中,所以我们要编写给所有用户的购物车中修改某件商品的方法。
@Override
public void refreshCartGoods(CartGoods
cartGoods) {
// 获取所有用户购物车商品
BoundHashOperations cartList = redisTemplate.boundHashOps("cartList");
Map<Long,List<CartGoods>> allCartGoods = cartList.entries();
Set<Map.Entry<Long, List<CartGoods>>> entries = allCartGoods.entrySet();
// 遍历所有用户的购物车
for (Map.Entry<Long, List<CartGoods>> entry : entries) {
List<CartGoods> goodsList = entry.getValue();
// 遍历一个用户购物车的所有商品
for (CartGoods goods : goodsList) {
// 如果该商品是被更新的商品,修改商品数据
if(cartGoods.getGoodId().equals(goods.getGoodId())){
goods.setGoodsName(cartGoods.getGoodsName());
goods.setHeaderPic(cartGoods.getHeaderPic());
goods.setPrice(cartGoods.getPrice());
}
}
}
// 将改变后所有用户购物车重新放入redis
redisTemplate.delete("cartList");
redisTemplate.boundHashOps("cartList").putAll(allCartGoods);
}
编写删除所有用户购物车商品方法
当管理员下架某件商品后,所有用户的购物车中该商品也应该被删除,所以我们要编写给所有用户的购物车中删除某件商品的方法。
@Override
public void deleteCartGoods(CartGoods cartGoods) {
BoundHashOperations cartList = redisTemplate.boundHashOps("cartList");
// 所有用户的购物车
Map<String,List<CartGoods>> allCartGoods = cartList.entries();
Set<Map.Entry<String, List<CartGoods>>> entries = allCartGoods.entrySet();
// 遍历所有用户的购物车
for (Map.Entry<String, List<CartGoods>> entry : entries) {
List<CartGoods> goodsList = entry.getValue();
// 遍历一个用户购物车的所有商品
for (CartGoods goods : goodsList) {
// 如果该商品是被删除的商品
if (cartGoods.getGoodId().equals(goods.getGoodId())){
goodsList.remove(goods);
break;
}
}
}
// 将改变后的map重新放入redis
redisTemplate.delete("cartList");
redisTemplate.boundHashOps("cartList").putAll(allCartGoods);
}
接下来我们修改商品服务模块代码,在修改商品后发送消息同步修 改购物车数据;在下架商品后发送消息同步删除购物车数据:
1、在MQ配置文件创建队列,绑定队列到交换机:
@Configuration
public class RabbitConfig {
// 交换机
private final String GOODS_EXCHANGE = "goods_exchange";
// 同步商品数据队列
private final String SYNC_GOODS_QUEUE = "sync_goods_queue";
// 删除商品数据队列
private final String DEL_GOODS_QUEUE = "del_goods_queue";
// 向购物车同步商品队列
private final String SYNC_CART_QUEUE = "sync_cart_queue";
// 向购物车删除商品队列
private final String DEL_CART_QUEUE = "del_cart_queue";
// 创建交换机
@Bean(GOODS_EXCHANGE)
public Exchange getExchange() {
return ExchangeBuilder
.topicExchange(GOODS_EXCHANGE) // 交换机类型
.durable(true) // 是否持久化
.build();
}
// 创建队列
@Bean(SYNC_GOODS_QUEUE)
public Queue getQueue1() {
return new Queue(SYNC_GOODS_QUEUE); // 队列名
}
@Bean(DEL_GOODS_QUEUE)
public Queue getQueue2() {
return new Queue(DEL_GOODS_QUEUE);
// 队列名
}
@Bean(SYNC_CART_QUEUE)
public Queue getQueue3() {
return new Queue(SYNC_CART_QUEUE);
// 队列名
}
@Bean(DEL_CART_QUEUE)
public Queue getQueue4() {
return new Queue(DEL_CART_QUEUE);
// 队列名
}
// 交换机绑定队列
@Bean
public Binding bindQueue1(@Qualifier(GOODS_EXCHANGE) Exchange exchange,
@Qualifier(SYNC_GOODS_QUEUE) Queue queue)
{
return BindingBuilder
.bind(queue)
.to(exchange)
.with("#.sync_goods.#")
.noargs();
}
@Bean
public Binding bindQueue2(@Qualifier(GOODS_EXCHANGE) Exchange exchange,
@Qualifier(DEL_GOODS_QUEUE) Queue queue) {
return BindingBuilder
.bind(queue)
.to(exchange)
.with("#.del_goods.#")
.noargs();
}
@Bean
public Binding
bindQueue3(@Qualifier(GOODS_EXCHANGE)
Exchange exchange,
@Qualifier(SYNC_CART_QUEUE) Queue queue)
{
return BindingBuilder
.bind(queue)
.to(exchange)
.with("#.sync_cart.#")
.noargs();
}
@Bean
public Binding bindQueue4(@Qualifier(GOODS_EXCHANGE) Exchange exchange,
@Qualifier(DEL_CART_QUEUE) Queue queue) {
return BindingBuilder
.bind(queue)
.to(exchange)
.with("#.del_cart.#")
.noargs();
}
}
@DubboService
public class GoodsServiceImpl implements
GoodsService {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private GoodsImageMapper goodsImageMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public void update(Goods goods) {
// 删除旧图片数据
Long goodsId = goods.getId(); //商品id
QueryWrapper<GoodsImage> queryWrapper = new QueryWrapper();
queryWrapper.eq("goodsId",goodsId);
goodsImageMapper.delete(queryWrapper);
// 删除旧规格项数据
goodsMapper.deleteGoodsSpecificationOption(goodsId);
// 插入商品数据
goodsMapper.updateById(goods);
// 插入图片数据
List<GoodsImage> images = goods.getImages(); // 商品图片
for (GoodsImage image : images) {
image.setGoodsId(goodsId); // 给图片设置商品id
goodsImageMapper.insert(image); // 插入图片
}
// 插入商品_规格项数据
List<Specification> specifications = goods.getSpecifications(); // 获取规格
List<SpecificationOption> options = new ArrayList(); // 规格项集合
// 遍历规格,获取规格中的所有规格项
for (Specification specification : specifications) {
options.addAll(specification.getSpecificationOptions());
}
// 遍历规格项,插入商品_规格项数据
for (SpecificationOption option : options) {
goodsMapper.addGoodsSpecificationOption(goodsId,option.getId());
}
rabbitTemplate.convertAndSend("goods_exchange","sync_goods",findById(goodsId));
// 将商品修改数据同步到用户购物车
CartGoods cartGoods = new CartGoods();
cartGoods.setGoodId(goods.getId());
cartGoods.setGoodsName(goods.getGoodsName());
cartGoods.setHeaderPic(goods.getHeaderPic());
cartGoods.setPrice(goods.getPrice());
rabbitTemplate.convertAndSend("goods_exchange","sync_cart",cartGoods);
}
@Override
public void putAway(Long id, Boolean isMarketable) {
goodsMapper.putAway(id,isMarketable);
if (isMarketable){
// 上架时同步到ES
rabbitTemplate.convertAndSend("goods_exchange","sync_goods",findById(id));
}else{
// 下架删除ES数据
rabbitTemplate.convertAndSend("goods_exchange","del_goods",id);
// 商品下架删除用户购物车
CartGoods cartGoods = new CartGoods();
cartGoods.setGoodId(id);
rabbitTemplate.convertAndSend("goods_exchange","del_cart",cartGoods);
}
}
}
1、在购物车服务模块添加RabbitMQ依赖
<!-- RabbitMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、添加RabbitMQ配置
spring:
# redis
redis:
host: 192.168.0.159
port: 6379
timeout: 30000
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
# rabbitmq
rabbitmq:
host: 192.168.0.159
port: 5672
username: guest
password: guest
virtual-host: /
3、修改购物车服务接口实现类,监听队列
@DubboService
@Service
public class CartServiceImpl implements CartService {
@Autowired
private RedisTemplate redisTemplate;
// 监听修改购物车商品队列
@RabbitListener(queues = "sync_cart_queue")
public void listenSyncQueue(CartGoods cartGoods){
refreshCartGoods(cartGoods);
}
// 监听删除购物车商品队列
@RabbitListener(queues = "del_cart_queue")
public void listenDelQueue(CartGoods cartGoods){
deleteCartGoods(cartGoods);
}
}
4、测试管理员修改商品、下架商品,看用户购物车是否发生改变。
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah